From 86406f63aadfaaaffcd0a00d336a2a94639d3c3e Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Tue, 2 Dec 2025 10:54:10 +0100 Subject: [PATCH 1/2] Fix the ripper translator to parse as the current ruby Otherwise, it uses the latest prism version --- lib/prism/translation/ripper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/prism/translation/ripper.rb b/lib/prism/translation/ripper.rb index 6ea98fc1ea..e488b7c5cf 100644 --- a/lib/prism/translation/ripper.rb +++ b/lib/prism/translation/ripper.rb @@ -71,7 +71,7 @@ def self.parse(src, filename = "(ripper)", lineno = 1) # [[1, 13], :on_kw, "end", END ]] # def self.lex(src, filename = "-", lineno = 1, raise_errors: false) - result = Prism.lex_compat(src, filepath: filename, line: lineno) + result = Prism.lex_compat(src, filepath: filename, line: lineno, version: "current") if result.failure? && raise_errors raise SyntaxError, result.errors.first.message @@ -3295,7 +3295,7 @@ def visit_yield_node(node) # Lazily initialize the parse result. def result - @result ||= Prism.parse(source, partial_script: true) + @result ||= Prism.parse(source, partial_script: true, version: "current") end ########################################################################## From e5a0221c37929f8c1972a1d93dca920119d7cadc Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Tue, 2 Dec 2025 14:41:44 +0100 Subject: [PATCH 2/2] Clean up test excludes Mostly not having to list version-specific excludes when testing against ripper/parse.y Also don't test new syntax additions against the parser gems. The version support for them may (or may not) be expanded but we shouldn't bother while the ruby version hasn't even released yet. (ruby_parser translation is not versioned, so let as is for now) I also removed excludes that have since been implemented by parse.y --- test/prism/errors_test.rb | 2 +- test/prism/fixtures_test.rb | 16 +--------------- test/prism/lex_test.rb | 32 +++++++++----------------------- test/prism/locals_test.rb | 19 +++---------------- test/prism/ruby/parser_test.rb | 13 ++----------- test/prism/ruby/ripper_test.rb | 32 +++++++++++--------------------- test/prism/snapshots_test.rb | 2 +- test/prism/snippets_test.rb | 2 +- test/prism/test_helper.rb | 22 ++++++++++++++++------ 9 files changed, 45 insertions(+), 95 deletions(-) diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index bd7a8a6381..706b739557 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -88,7 +88,7 @@ def assert_errors(filepath, version) expected = File.read(filepath, binmode: true, external_encoding: Encoding::UTF_8) source = expected.lines.grep_v(/^\s*\^/).join.gsub(/\n*\z/, "") - refute_valid_syntax(source) if current_major_minor == version + refute_valid_syntax(source) if CURRENT_MAJOR_MINOR == version result = Prism.parse(source, version: version) errors = result.errors diff --git a/test/prism/fixtures_test.rb b/test/prism/fixtures_test.rb index 2aebb18477..7df97029d3 100644 --- a/test/prism/fixtures_test.rb +++ b/test/prism/fixtures_test.rb @@ -24,23 +24,9 @@ class FixturesTest < TestCase except << "whitequark/ruby_bug_19281.txt" end - if RUBY_VERSION < "3.4.0" - except << "3.4/circular_parameters.txt" - end - - # Valid only on Ruby 3.3 - except << "3.3-3.3/block_args_in_array_assignment.txt" - except << "3.3-3.3/it_with_ordinary_parameter.txt" - except << "3.3-3.3/keyword_args_in_array_assignment.txt" - except << "3.3-3.3/return_in_sclass.txt" - - # Leaving these out until they are supported by parse.y. - except << "4.0/leading_logical.txt" - except << "4.0/endless_methods_command_call.txt" - # https://bugs.ruby-lang.org/issues/21168#note-5 except << "command_method_call_2.txt" - Fixture.each(except: except) do |fixture| + Fixture.each_for_current_ruby(except: except) do |fixture| define_method(fixture.test_name) { assert_valid_syntax(fixture.read) } end end diff --git a/test/prism/lex_test.rb b/test/prism/lex_test.rb index 19dd845d75..68e47a0964 100644 --- a/test/prism/lex_test.rb +++ b/test/prism/lex_test.rb @@ -7,19 +7,13 @@ module Prism class LexTest < TestCase except = [ - # It seems like there are some oddities with nested heredocs and ripper. - # Waiting for feedback on https://bugs.ruby-lang.org/issues/19838. - "seattlerb/heredoc_nested.txt", - "whitequark/dedenting_heredoc.txt", - # Ripper seems to have a bug that the regex portions before and after - # the heredoc are combined into a single token. See - # https://bugs.ruby-lang.org/issues/19838. + # https://bugs.ruby-lang.org/issues/21756 "spanning_heredoc.txt", - "spanning_heredoc_newlines.txt", - # Prism emits a single :on_tstring_content in <<- style heredocs when there - # is a line continuation preceded by escaped backslashes. It should emit two, same - # as if the backslashes are not present. + # Prism emits a single string in some cases when ripper splits them up + "whitequark/dedenting_heredoc.txt", "heredocs_with_fake_newlines.txt", + # Prism emits BEG for `on_regexp_end` + "spanning_heredoc_newlines.txt", ] if RUBY_VERSION < "3.3.0" @@ -42,17 +36,11 @@ class LexTest < TestCase except << "whitequark/ruby_bug_19281.txt" end - # https://bugs.ruby-lang.org/issues/20925 - except << "4.0/leading_logical.txt" - - # https://bugs.ruby-lang.org/issues/17398#note-12 - except << "4.0/endless_methods_command_call.txt" - # https://bugs.ruby-lang.org/issues/21168#note-5 except << "command_method_call_2.txt" - Fixture.each_with_version(except: except) do |fixture, version| - define_method(fixture.test_name(version)) { assert_lex(fixture, version) } + Fixture.each_for_current_ruby(except: except) do |fixture| + define_method(fixture.test_name) { assert_lex(fixture) } end def test_lex_file @@ -97,12 +85,10 @@ def test_parse_lex_file private - def assert_lex(fixture, version) - return unless current_major_minor == version - + def assert_lex(fixture) source = fixture.read - result = Prism.lex_compat(source, version: version) + result = Prism.lex_compat(source, version: "current") assert_equal [], result.errors Prism.lex_ripper(source).zip(result.value).each do |(ripper, prism)| diff --git a/test/prism/locals_test.rb b/test/prism/locals_test.rb index 814c9a9978..4f8d6080e8 100644 --- a/test/prism/locals_test.rb +++ b/test/prism/locals_test.rb @@ -13,11 +13,6 @@ # in comparing the locals because they will be the same. return if RubyVM::InstructionSequence.compile("").to_a[4][:parser] == :prism -# In Ruby 3.4.0, the local table for method forwarding changed. But 3.4.0 can -# refer to the dev version, so while 3.4.0 still isn't released, we need to -# check if we have a high enough revision. -return if RubyVM::InstructionSequence.compile("def foo(...); end").to_a[13][2][2][10].length != 1 - # Omit tests if running on a 32-bit machine because there is a bug with how # Ruby is handling large ISeqs on 32-bit machines return if RUBY_PLATFORM =~ /i686/ @@ -31,19 +26,11 @@ class LocalsTest < TestCase # CRuby is eliminating dead code. "whitequark/ruby_bug_10653.txt", - # Valid only on Ruby 3.3 - "3.3-3.3/block_args_in_array_assignment.txt", - "3.3-3.3/it_with_ordinary_parameter.txt", - "3.3-3.3/keyword_args_in_array_assignment.txt", - "3.3-3.3/return_in_sclass.txt", - - # Leaving these out until they are supported by parse.y. - "4.0/leading_logical.txt", - "4.0/endless_methods_command_call.txt", - "command_method_call_2.txt" + # https://bugs.ruby-lang.org/issues/21168#note-5 + "command_method_call_2.txt", ] - Fixture.each(except: except) do |fixture| + Fixture.each_for_current_ruby(except: except) do |fixture| define_method(fixture.test_name) { assert_locals(fixture) } end diff --git a/test/prism/ruby/parser_test.rb b/test/prism/ruby/parser_test.rb index 648c44e77a..c972f0962b 100644 --- a/test/prism/ruby/parser_test.rb +++ b/test/prism/ruby/parser_test.rb @@ -65,15 +65,6 @@ class ParserTest < TestCase # 1.. && 2 "ranges.txt", - # https://bugs.ruby-lang.org/issues/20478 - "3.4/circular_parameters.txt", - - # Cannot yet handling leading logical operators. - "4.0/leading_logical.txt", - - # Ruby >= 4.0 specific syntax - "4.0/endless_methods_command_call.txt", - # https://bugs.ruby-lang.org/issues/21168#note-5 "command_method_call_2.txt", ] @@ -148,7 +139,7 @@ class ParserTest < TestCase "whitequark/space_args_block.txt" ] - Fixture.each(except: skip_syntax_error) do |fixture| + Fixture.each_for_version(except: skip_syntax_error, version: "3.3") do |fixture| define_method(fixture.test_name) do assert_equal_parses( fixture, @@ -171,7 +162,7 @@ def test_non_prism_builder_class_deprecated if RUBY_VERSION >= "3.3" def test_current_parser_for_current_ruby - major, minor = current_major_minor.split(".") + major, minor = CURRENT_MAJOR_MINOR.split(".") # Let's just hope there never is a Ruby 3.10 or similar expected = major.to_i * 10 + minor.to_i assert_equal(expected, Translation::ParserCurrent.new.version) diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index 400139acc0..bd63302efc 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -8,44 +8,34 @@ module Prism class RipperTest < TestCase # Skip these tests that Ripper is reporting the wrong results for. incorrect = [ - # Not yet supported. - "4.0/leading_logical.txt", - # Ripper incorrectly attributes the block to the keyword. - "seattlerb/block_break.txt", - "seattlerb/block_next.txt", "seattlerb/block_return.txt", - "whitequark/break_block.txt", - "whitequark/next_block.txt", "whitequark/return_block.txt", - # Ripper is not accounting for locals created by patterns using the ** - # operator within an `in` clause. - "seattlerb/parse_pattern_058.txt", - # Ripper cannot handle named capture groups in regular expressions. "regex.txt", - "regex_char_width.txt", - "whitequark/lvar_injecting_match.txt", # Ripper fails to understand some structures that span across heredocs. "spanning_heredoc.txt", - "3.3-3.3/block_args_in_array_assignment.txt", - "3.3-3.3/it_with_ordinary_parameter.txt", - "3.3-3.3/keyword_args_in_array_assignment.txt", - "3.3-3.3/return_in_sclass.txt", - - # https://bugs.ruby-lang.org/issues/20478 + # Ripper interprets circular keyword arguments as method calls. "3.4/circular_parameters.txt", - # https://bugs.ruby-lang.org/issues/17398#note-12 + # Ripper doesn't emit `args_add_block` when endless method is prefixed by modifier. "4.0/endless_methods_command_call.txt", # https://bugs.ruby-lang.org/issues/21168#note-5 "command_method_call_2.txt", ] + if RUBY_VERSION.start_with?("3.3.") + incorrect += [ + "whitequark/lvar_injecting_match.txt", + "seattlerb/parse_pattern_058.txt", + "regex_char_width.txt", + ] + end + # Skip these tests that we haven't implemented yet. omitted = [ "dos_endings.txt", @@ -68,7 +58,7 @@ class RipperTest < TestCase "whitequark/slash_newline_in_heredocs.txt" ] - Fixture.each(except: incorrect | omitted) do |fixture| + Fixture.each_for_current_ruby(except: incorrect | omitted) do |fixture| define_method(fixture.test_name) { assert_ripper(fixture.read) } end diff --git a/test/prism/snapshots_test.rb b/test/prism/snapshots_test.rb index 58627e3668..23e5c9c087 100644 --- a/test/prism/snapshots_test.rb +++ b/test/prism/snapshots_test.rb @@ -36,7 +36,7 @@ def teardown ) end - Fixture.each_with_version(except: except) do |fixture, version| + Fixture.each_with_all_versions(except: except) do |fixture, version| define_method(fixture.test_name(version)) { assert_snapshot(fixture, version) } end diff --git a/test/prism/snippets_test.rb b/test/prism/snippets_test.rb index 3160442cc0..3c28d27a25 100644 --- a/test/prism/snippets_test.rb +++ b/test/prism/snippets_test.rb @@ -18,7 +18,7 @@ class SnippetsTest < TestCase "whitequark/multiple_pattern_matches.txt" ] - Fixture.each_with_version(except: except) do |fixture, version| + Fixture.each_with_all_versions(except: except) do |fixture, version| define_method(fixture.test_name(version)) { assert_snippets(fixture, version) } end diff --git a/test/prism/test_helper.rb b/test/prism/test_helper.rb index 42555738cf..f78e68e87c 100644 --- a/test/prism/test_helper.rb +++ b/test/prism/test_helper.rb @@ -72,7 +72,18 @@ def self.each(except: [], &block) paths.each { |path| yield Fixture.new(path) } end - def self.each_with_version(except: [], &block) + def self.each_for_version(except: [], version:, &block) + each(except: except) do |fixture| + next unless TestCase.ruby_versions_for(fixture.path).include?(version) + yield fixture + end + end + + def self.each_for_current_ruby(except: [], &block) + each_for_version(except: except, version: CURRENT_MAJOR_MINOR, &block) + end + + def self.each_with_all_versions(except: [], &block) each(except: except) do |fixture| TestCase.ruby_versions_for(fixture.path).each do |version| yield fixture, version @@ -232,6 +243,9 @@ def self.windows? # All versions that prism can parse SYNTAX_VERSIONS = %w[3.3 3.4 4.0] + # `RUBY_VERSION` with the patch version excluded + CURRENT_MAJOR_MINOR = RUBY_VERSION.split(".")[0, 2].join(".") + # Returns an array of ruby versions that a given filepath should test against: # test.txt # => all available versions # 3.4/test.txt # => versions since 3.4 (inclusive) @@ -250,13 +264,9 @@ def self.ruby_versions_for(filepath) end end - def current_major_minor - RUBY_VERSION.split(".")[0, 2].join(".") - end - if RUBY_VERSION >= "3.3.0" def test_all_syntax_versions_present - assert_include(SYNTAX_VERSIONS, current_major_minor) + assert_include(SYNTAX_VERSIONS, CURRENT_MAJOR_MINOR) end end