From e050097bebe64e86e97e915fa74a3fc010c588e8 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 29 Jan 2024 11:02:11 -0500 Subject: [PATCH] [ruby/prism] Raise diagnostics for parser https://github.com/ruby/prism/commit/102b4a16f5 --- lib/prism/translation/parser.rb | 57 +++++++++++++++++------- lib/prism/translation/parser/compiler.rb | 9 +++- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/lib/prism/translation/parser.rb b/lib/prism/translation/parser.rb index 7cc18ac5de..589b33b6fe 100644 --- a/lib/prism/translation/parser.rb +++ b/lib/prism/translation/parser.rb @@ -9,6 +9,20 @@ module Prism # the parser gem, and overrides the parse* methods to parse with prism and # then translate. class Parser < ::Parser::Base + # The parser gem has a list of diagnostics with a hard-coded set of error + # messages. We create our own diagnostic class in order to set our own + # error messages. + class Diagnostic < ::Parser::Diagnostic + # The message generated by prism. + attr_reader :message + + # Initialize a new diagnostic with the given message and location. + def initialize(message, location) + @message = message + super(:error, :prism_error, {}, location, []) + end + end + Racc_debug_parser = false # :nodoc: def version # :nodoc: @@ -28,10 +42,9 @@ module Prism @source_buffer = source_buffer source = source_buffer.source - build_ast( - Prism.parse(source, filepath: source_buffer.name).value, - build_offset_cache(source) - ) + result = unwrap(Prism.parse(source, filepath: source_buffer.name)) + + build_ast(result.value, build_offset_cache(source)) ensure @source_buffer = nil end @@ -42,7 +55,7 @@ module Prism source = source_buffer.source offset_cache = build_offset_cache(source) - result = Prism.parse(source, filepath: source_buffer.name) + result = unwrap(Prism.parse(source, filepath: source_buffer.name)) [ build_ast(result.value, offset_cache), @@ -59,7 +72,8 @@ module Prism source = source_buffer.source offset_cache = build_offset_cache(source) - result = Prism.parse_lex(source, filepath: source_buffer.name) + result = unwrap(Prism.parse_lex(source, filepath: source_buffer.name)) + program, tokens = result.value [ @@ -79,6 +93,18 @@ module Prism private + # If there was a error generated during the parse, then raise an + # appropriate syntax error. Otherwise return the result. + def unwrap(result) + return result if result.success? + + error = result.errors.first + offset_cache = build_offset_cache(source_buffer.source) + + diagnostic = Diagnostic.new(error.message, build_range(error.location, offset_cache)) + raise ::Parser::SyntaxError, diagnostic + end + # Prism deals with offsets in bytes, while the parser gem deals with # offsets in characters. We need to handle this conversion in order to # build the parser gem AST. @@ -109,15 +135,7 @@ module Prism # Build the parser gem comments from the prism comments. def build_comments(comments, offset_cache) comments.map do |comment| - location = comment.location - - ::Parser::Source::Comment.new( - ::Parser::Source::Range.new( - source_buffer, - offset_cache[location.start_offset], - offset_cache[location.end_offset] - ) - ) + ::Parser::Source::Comment.new(build_range(comment.location, offset_cache)) end end @@ -126,6 +144,15 @@ module Prism Lexer.new(source_buffer, tokens.map(&:first), offset_cache).to_a end + # Build a range from a prism location. + def build_range(location, offset_cache) + ::Parser::Source::Range.new( + source_buffer, + offset_cache[location.start_offset], + offset_cache[location.end_offset] + ) + end + require_relative "parser/compiler" require_relative "parser/lexer" diff --git a/lib/prism/translation/parser/compiler.rb b/lib/prism/translation/parser/compiler.rb index d03de9efc5..ccd02c2181 100644 --- a/lib/prism/translation/parser/compiler.rb +++ b/lib/prism/translation/parser/compiler.rb @@ -83,11 +83,16 @@ module Prism elements = [*node.requireds] elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode) elements.concat(node.posts) + visited = visit_all(elements) + + if node.rest.is_a?(ImplicitRestNode) + visited[-1] = builder.match_with_trailing_comma(visited[-1], token(node.rest.location)) + end if node.constant - builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.array_pattern(nil, visit_all(elements), nil), token(node.closing_loc)) + builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.array_pattern(nil, visited, nil), token(node.closing_loc)) else - builder.array_pattern(token(node.opening_loc), visit_all(elements), token(node.closing_loc)) + builder.array_pattern(token(node.opening_loc), visited, token(node.closing_loc)) end end