lib/rbi/parser.rb in rbi-0.0.13 vs lib/rbi/parser.rb in rbi-0.0.14
- old
+ new
@@ -15,10 +15,43 @@
super(message)
@location = location
end
end
+ class UnexpectedParserError < StandardError
+ extend T::Sig
+
+ sig { returns(Loc) }
+ attr_reader :last_location
+
+ sig { params(parent_exception: Exception, last_location: Loc).void }
+ def initialize(parent_exception, last_location)
+ super(parent_exception)
+ set_backtrace(parent_exception.backtrace)
+ @last_location = last_location
+ end
+
+ sig { params(io: T.any(IO, StringIO)).void }
+ def print_debug(io: $stderr)
+ io.puts ""
+ io.puts "##################################"
+ io.puts "### RBI::Parser internal error ###"
+ io.puts "##################################"
+ io.puts ""
+ io.puts "There was an internal parser error while processing this source."
+ io.puts ""
+ io.puts "Error: #{message} while parsing #{last_location}:"
+ io.puts ""
+ io.puts last_location.source || "<no source>"
+ io.puts ""
+ io.puts "Please open an issue at https://github.com/Shopify/rbi/issues/new."
+ io.puts ""
+ io.puts "##################################"
+ io.puts ""
+ end
+ end
+
class Parser
extend T::Sig
# opt-in to most recent AST format
::Parser::Builders::Default.emit_lambda = true
@@ -75,10 +108,23 @@
builder.visit(node)
builder.post_process
builder.tree
rescue ::Parser::SyntaxError => e
raise ParseError.new(e.message, Loc.from_ast_loc(file, e.diagnostic.location))
+ rescue ParseError => e
+ raise e
+ rescue => e
+ last_node = builder&.last_node
+ last_location = if last_node
+ Loc.from_ast_loc(file, last_node.location)
+ else
+ Loc.new(file: file)
+ end
+
+ exception = UnexpectedParserError.new(e, last_location)
+ exception.print_debug
+ raise exception
end
end
class ASTVisitor
extend T::Helpers
@@ -111,10 +157,13 @@
extend T::Sig
sig { returns(Tree) }
attr_reader :tree
+ sig { returns(T.nilable(::AST::Node)) }
+ attr_reader :last_node
+
sig do
params(
file: String,
comments: T::Array[::Parser::Source::Comment],
nodes_comments_assoc: T::Hash[::Parser::Source::Map, T::Array[::Parser::Source::Comment]]
@@ -125,10 +174,11 @@
@file = file
@comments = comments
@nodes_comments_assoc = nodes_comments_assoc
@tree = T.let(Tree.new, Tree)
@scopes_stack = T.let([@tree], T::Array[Tree])
+ @last_node = T.let(nil, T.nilable(::AST::Node))
@last_sigs = T.let([], T::Array[RBI::Sig])
separate_header_comments
end
@@ -139,10 +189,12 @@
end
sig { override.params(node: T.nilable(Object)).void }
def visit(node)
return unless node.is_a?(AST::Node)
+ @last_node = node
+
case node.type
when :module, :class, :sclass
scope = parse_scope(node)
current_scope << scope
@scopes_stack << scope
@@ -163,10 +215,12 @@
current_scope << node
end
else
visit_all(node.children)
end
+
+ @last_node = nil
end
private
sig { params(node: AST::Node).returns(Scope) }
@@ -610,22 +664,28 @@
when :override
@current.is_override = true
when :overridable
@current.is_overridable = true
when :checked
- @current.checked = node.children[2].children[0]
+ if node.children.length >= 3
+ @current.checked = node.children[2].children[0]
+ end
when :type_parameters
node.children[2..-1].each do |child|
@current.type_params << child.children[0].to_s
end
when :params
- node.children[2].children.each do |child|
- name = child.children[0].children[0].to_s
- type = parse_expr(child.children[1])
- @current << SigParam.new(name, type)
+ if node.children.length >= 3
+ node.children[2].children.each do |child|
+ name = child.children[0].children[0].to_s
+ type = parse_expr(child.children[1])
+ @current << SigParam.new(name, type)
+ end
end
when :returns
- @current.return_type = parse_expr(node.children[2])
+ if node.children.length >= 3
+ @current.return_type = parse_expr(node.children[2])
+ end
when :void
@current.return_type = nil
else
raise "#{node.location.line}: Unhandled #{name}"
end