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