From 5a184b8085b2059b3c0d055808b3934450dea9aa Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Tue, 24 Mar 2026 13:10:31 +0100 Subject: [PATCH 1/4] Add a test for ripper events Currently there is just one very basic one. --- test/prism/ruby/ripper_test.rb | 63 ++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index 92aa1ad0b3..e5858cae07 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -82,6 +82,41 @@ class RipperTest < TestCase "whitequark/procarg0.txt", ] + omitted_scan = [ + "dos_endings.txt", + "heredocs_with_fake_newlines.txt", + "rescue_modifier.txt", + "seattlerb/block_call_dot_op2_brace_block.txt", + "seattlerb/block_command_operation_colon.txt", + "seattlerb/block_command_operation_dot.txt", + "seattlerb/case_in.txt", + "seattlerb/heredoc__backslash_dos_format.txt", + "seattlerb/heredoc_backslash_nl.txt", + "seattlerb/heredoc_nested.txt", + "seattlerb/heredoc_squiggly_blank_line_plus_interpolation.txt", + "seattlerb/heredoc_squiggly_empty.txt", + "seattlerb/masgn_command_call.txt", + "seattlerb/messy_op_asgn_lineno.txt", + "seattlerb/op_asgn_primary_colon_const_command_call.txt", + "seattlerb/parse_pattern_076.txt", + "tilde_heredocs.txt", + "unparser/corpus/literal/assignment.txt", + "unparser/corpus/literal/pattern.txt", + "unparser/corpus/semantic/dstr.txt", + "variables.txt", + "whitequark/dedenting_heredoc.txt", + "whitequark/masgn_nested.txt", + "whitequark/numparam_ruby_bug_19025.txt", + "whitequark/op_asgn_cmd.txt", + "whitequark/parser_drops_truncated_parts_of_squiggly_heredoc.txt", + "whitequark/parser_slash_slash_n_escaping_in_literals.txt", + "whitequark/pattern_matching_nil_pattern.txt", + "whitequark/ruby_bug_12402.txt", + "whitequark/ruby_bug_18878.txt", + "whitequark/send_block_chain_cmd.txt", + "whitequark/slash_newline_in_heredocs.txt", + ] + Fixture.each_for_current_ruby(except: incorrect | omitted_sexp_raw) do |fixture| define_method("#{fixture.test_name}_sexp_raw") { assert_ripper_sexp_raw(fixture.read) } end @@ -100,6 +135,9 @@ def test_lex_ignored_missing_heredoc_end end end + UNSUPPORTED_EVENTS = %i[backtick comma comment heredoc_beg heredoc_end ident ignored_nl int kw label_end lbrace lbracket lparen nl op rbrace rbracket rparen semicolon sp symbeg tstring_beg tstring_end words_sep ignored_sp] + SUPPORTED_EVENTS = Translation::Ripper::EVENTS - UNSUPPORTED_EVENTS + module Events attr_reader :events @@ -108,9 +146,9 @@ def initialize(...) @events = [] end - Prism::Translation::Ripper::PARSER_EVENTS.each do |event| + SUPPORTED_EVENTS.each do |event| define_method(:"on_#{event}") do |*args| - @events << [event, *args] + @events << [event, *args.map(&:to_s)] super(*args) end end @@ -126,28 +164,25 @@ class PrismEvents < Translation::Ripper class ObjectEvents < Translation::Ripper OBJECT = BasicObject.new - Prism::Translation::Ripper::PARSER_EVENTS.each do |event| + SUPPORTED_EVENTS.each do |event| define_method(:"on_#{event}") { |*args| OBJECT } end end - Fixture.each_for_current_ruby(except: incorrect) do |fixture| + Fixture.each_for_current_ruby(except: incorrect | omitted_scan) do |fixture| define_method("#{fixture.test_name}_events") do source = fixture.read # Similar to test/ripper/assert_parse_files.rb in CRuby object_events = ObjectEvents.new(source) assert_nothing_raised { object_events.parse } - end - end - def test_events - source = "1 rescue 2" - ripper = RipperEvents.new(source) - prism = PrismEvents.new(source) - ripper.parse - prism.parse - # This makes sure that the content is the same. Ordering is not correct for now. - assert_equal(ripper.events.sort, prism.events.sort) + ripper = RipperEvents.new(source) + prism = PrismEvents.new(source) + ripper.parse + prism.parse + # This makes sure that the content is the same. Ordering is not correct for now. + assert_equal(ripper.events.sort, prism.events.sort) + end end def test_lexer From 894f395449c41afb65b9259c8ba12645012c6197 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Tue, 24 Mar 2026 13:18:17 +0100 Subject: [PATCH 2/4] Emit `on_comment` with a newline when available --- lib/prism/translation/ripper.rb | 7 ++++++- test/prism/ruby/ripper_test.rb | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/prism/translation/ripper.rb b/lib/prism/translation/ripper.rb index d1c28a2401..9900a74c47 100644 --- a/lib/prism/translation/ripper.rb +++ b/lib/prism/translation/ripper.rb @@ -513,7 +513,12 @@ def parse bounds(location) if comment.is_a?(InlineComment) - on_comment(comment.slice) + # Inline comments always contain a newline if the line itself contains it + if result.source.source.bytesize > comment.location.end_offset + on_comment("#{comment.slice}\n") + else + on_comment(comment.slice) + end else offset = location.start_offset lines = comment.slice.lines diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index e5858cae07..6ca527edfe 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -135,7 +135,7 @@ def test_lex_ignored_missing_heredoc_end end end - UNSUPPORTED_EVENTS = %i[backtick comma comment heredoc_beg heredoc_end ident ignored_nl int kw label_end lbrace lbracket lparen nl op rbrace rbracket rparen semicolon sp symbeg tstring_beg tstring_end words_sep ignored_sp] + UNSUPPORTED_EVENTS = %i[backtick comma heredoc_beg heredoc_end ident ignored_nl int kw label_end lbrace lbracket lparen nl op rbrace rbracket rparen semicolon sp symbeg tstring_beg tstring_end words_sep ignored_sp] SUPPORTED_EVENTS = Translation::Ripper::EVENTS - UNSUPPORTED_EVENTS module Events From 44d064c2bc284141d34d2390cec00b7c71074502 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Tue, 24 Mar 2026 13:30:25 +0100 Subject: [PATCH 3/4] Fix double-visiting on some constant path node types `::X &&= 1` for example emitted `on_int` twice --- lib/prism/translation/ripper.rb | 3 --- test/prism/ruby/ripper_test.rb | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/prism/translation/ripper.rb b/lib/prism/translation/ripper.rb index 9900a74c47..70ce1354be 100644 --- a/lib/prism/translation/ripper.rb +++ b/lib/prism/translation/ripper.rb @@ -1582,7 +1582,6 @@ def visit_constant_path_write_node(node) # ^^^^^^^^^^^^^^^ def visit_constant_path_operator_write_node(node) target = visit_constant_path_write_node_target(node.target) - value = visit(node.value) bounds(node.binary_operator_loc) operator = on_op("#{node.binary_operator}=") @@ -1596,7 +1595,6 @@ def visit_constant_path_operator_write_node(node) # ^^^^^^^^^^^^^^^^ def visit_constant_path_and_write_node(node) target = visit_constant_path_write_node_target(node.target) - value = visit(node.value) bounds(node.operator_loc) operator = on_op("&&=") @@ -1610,7 +1608,6 @@ def visit_constant_path_and_write_node(node) # ^^^^^^^^^^^^^^^^ def visit_constant_path_or_write_node(node) target = visit_constant_path_write_node_target(node.target) - value = visit(node.value) bounds(node.operator_loc) operator = on_op("||=") diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index 6ca527edfe..1525030ac5 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -135,7 +135,7 @@ def test_lex_ignored_missing_heredoc_end end end - UNSUPPORTED_EVENTS = %i[backtick comma heredoc_beg heredoc_end ident ignored_nl int kw label_end lbrace lbracket lparen nl op rbrace rbracket rparen semicolon sp symbeg tstring_beg tstring_end words_sep ignored_sp] + UNSUPPORTED_EVENTS = %i[backtick comma heredoc_beg heredoc_end ident ignored_nl kw label_end lbrace lbracket lparen nl op rbrace rbracket rparen semicolon sp symbeg tstring_beg tstring_end words_sep ignored_sp] SUPPORTED_EVENTS = Translation::Ripper::EVENTS - UNSUPPORTED_EVENTS module Events From fb5303f2b8c1873d878d6b70d6f1b5fdbf324f6b Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Tue, 24 Mar 2026 13:44:09 +0100 Subject: [PATCH 4/4] Visit block locals in lambda definitions They may not be part of `on_param` but ripper still calls `on_ident` for them --- lib/prism/translation/ripper.rb | 2 ++ test/prism/ruby/ripper_test.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/prism/translation/ripper.rb b/lib/prism/translation/ripper.rb index 70ce1354be..17272eebdd 100644 --- a/lib/prism/translation/ripper.rb +++ b/lib/prism/translation/ripper.rb @@ -2359,6 +2359,8 @@ def visit_lambda_node(node) visit(node.parameters.parameters) end + visit_all(node.parameters.locals) + if node.parameters.opening_loc.nil? params else diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index 1525030ac5..8c80b9f886 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -135,7 +135,7 @@ def test_lex_ignored_missing_heredoc_end end end - UNSUPPORTED_EVENTS = %i[backtick comma heredoc_beg heredoc_end ident ignored_nl kw label_end lbrace lbracket lparen nl op rbrace rbracket rparen semicolon sp symbeg tstring_beg tstring_end words_sep ignored_sp] + UNSUPPORTED_EVENTS = %i[backtick comma heredoc_beg heredoc_end ignored_nl kw label_end lbrace lbracket lparen nl op rbrace rbracket rparen semicolon sp symbeg tstring_beg tstring_end words_sep ignored_sp] SUPPORTED_EVENTS = Translation::Ripper::EVENTS - UNSUPPORTED_EVENTS module Events