From 54606b8f72af116fb10c834b5ade620173c9f1b5 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Fri, 7 Nov 2025 12:55:05 +0100 Subject: [PATCH 1/2] Reverse-sync https://github.com/ruby/ruby/commit/f4e01783d3412b10f9978b5297142979cb067ce8 --- include/prism/options.h | 5 ++++- lib/prism/ffi.rb | 2 ++ src/options.c | 10 ++++++++++ test/prism/test_helper.rb | 2 +- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/include/prism/options.h b/include/prism/options.h index 1a92c470f1..44cd745e15 100644 --- a/include/prism/options.h +++ b/include/prism/options.h @@ -94,8 +94,11 @@ typedef enum { /** The vendored version of prism in CRuby 3.5.x. */ PM_OPTIONS_VERSION_CRUBY_3_5 = 3, + /** The vendored version of prism in CRuby 4.0.x. */ + PM_OPTIONS_VERSION_CRUBY_4_0 = 4, + /** The current version of prism. */ - PM_OPTIONS_VERSION_LATEST = PM_OPTIONS_VERSION_CRUBY_3_5 + PM_OPTIONS_VERSION_LATEST = PM_OPTIONS_VERSION_CRUBY_4_0 } pm_options_version_t; /** diff --git a/lib/prism/ffi.rb b/lib/prism/ffi.rb index a3bf5786bd..f6ad6f98b1 100644 --- a/lib/prism/ffi.rb +++ b/lib/prism/ffi.rb @@ -434,6 +434,8 @@ def dump_options_version(version) 2 when /\A3\.5(\.\d+)?\z/ 3 + when /\A4\.0(\.\d+)?\z/ + 4 else if current raise CurrentVersionError, RUBY_VERSION diff --git a/src/options.c b/src/options.c index 1b5c022cf5..373d76a21f 100644 --- a/src/options.c +++ b/src/options.c @@ -93,6 +93,11 @@ pm_options_version_set(pm_options_t *options, const char *version, size_t length return true; } + if (strncmp(version, "4.0", 3) == 0) { + options->version = PM_OPTIONS_VERSION_CRUBY_4_0; + return true; + } + return false; } @@ -111,6 +116,11 @@ pm_options_version_set(pm_options_t *options, const char *version, size_t length options->version = PM_OPTIONS_VERSION_CRUBY_3_5; return true; } + + if (strncmp(version, "4.0.", 4) == 0 && is_number(version + 4, length - 4)) { + options->version = PM_OPTIONS_VERSION_CRUBY_4_0; + return true; + } } if (length >= 6) { diff --git a/test/prism/test_helper.rb b/test/prism/test_helper.rb index 84871722c9..faf6117668 100644 --- a/test/prism/test_helper.rb +++ b/test/prism/test_helper.rb @@ -230,7 +230,7 @@ def self.windows? end # All versions that prism can parse - SYNTAX_VERSIONS = %w[3.3 3.4 3.5] + SYNTAX_VERSIONS = %w[3.3 3.4 3.5 4.0] # Returns an array of ruby versions that a given filepath should test against: # test.txt # => all available versions From d0a823f0455b7e0ef18f0beea9bfece91732db8b Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:14:07 +0100 Subject: [PATCH 2/2] Rename Ruby 3.5 to Ruby 4.0 See https://github.com/ruby/ruby/commit/6d81969b475262aba251e99b518181bdf7c5a523 It leaves the old variant around. RuboCop for examples accesses `Prism::Translation::Parser35` to test against ruby-head. For now I left these simply as an alias --- include/prism/options.h | 4 ++-- java/org/prism/ParsingOptions.java | 3 ++- javascript/src/parsePrism.js | 2 +- lib/prism/ffi.rb | 4 +--- lib/prism/translation.rb | 1 + lib/prism/translation/parser.rb | 6 +++--- lib/prism/translation/parser35.rb | 7 +------ lib/prism/translation/parser40.rb | 13 ++++++++++++ lib/prism/translation/parser_current.rb | 4 ++-- prism.gemspec | 2 ++ rbi/prism/translation/parser35.rbi | 2 -- rbi/prism/translation/parser40.rbi | 6 ++++++ .../endless_methods_command_call.txt | 0 snapshots/{3.5 => 4.0}/leading_logical.txt | 0 src/options.c | 20 +++++-------------- src/prism.c | 8 ++++---- test/prism/api/parse_test.rb | 3 +++ .../endless_methods_command_call.txt | 0 .../fixtures/{3.5 => 4.0}/leading_logical.txt | 0 test/prism/fixtures_test.rb | 4 ++-- test/prism/lex_test.rb | 4 ++-- test/prism/locals_test.rb | 4 ++-- test/prism/ractor_test.rb | 2 +- test/prism/ruby/parser_test.rb | 7 ++++--- test/prism/ruby/ripper_test.rb | 4 ++-- test/prism/ruby/ruby_parser_test.rb | 4 ++-- test/prism/test_helper.rb | 3 ++- 27 files changed, 63 insertions(+), 54 deletions(-) create mode 100644 lib/prism/translation/parser40.rb create mode 100644 rbi/prism/translation/parser40.rbi rename snapshots/{3.5 => 4.0}/endless_methods_command_call.txt (100%) rename snapshots/{3.5 => 4.0}/leading_logical.txt (100%) rename test/prism/fixtures/{3.5 => 4.0}/endless_methods_command_call.txt (100%) rename test/prism/fixtures/{3.5 => 4.0}/leading_logical.txt (100%) diff --git a/include/prism/options.h b/include/prism/options.h index 44cd745e15..a663c9767e 100644 --- a/include/prism/options.h +++ b/include/prism/options.h @@ -91,11 +91,11 @@ typedef enum { /** The vendored version of prism in CRuby 3.4.x. */ PM_OPTIONS_VERSION_CRUBY_3_4 = 2, - /** The vendored version of prism in CRuby 3.5.x. */ + /** The vendored version of prism in CRuby 4.0.x. */ PM_OPTIONS_VERSION_CRUBY_3_5 = 3, /** The vendored version of prism in CRuby 4.0.x. */ - PM_OPTIONS_VERSION_CRUBY_4_0 = 4, + PM_OPTIONS_VERSION_CRUBY_4_0 = 3, /** The current version of prism. */ PM_OPTIONS_VERSION_LATEST = PM_OPTIONS_VERSION_CRUBY_4_0 diff --git a/java/org/prism/ParsingOptions.java b/java/org/prism/ParsingOptions.java index 4aff7dbe35..6f7a342818 100644 --- a/java/org/prism/ParsingOptions.java +++ b/java/org/prism/ParsingOptions.java @@ -15,7 +15,8 @@ public enum SyntaxVersion { LATEST(0), // Handled in pm_parser_init V3_3(1), V3_4(2), - V3_5(3); + V3_5(3), + V4_0(3); private final int value; diff --git a/javascript/src/parsePrism.js b/javascript/src/parsePrism.js index d021c63a9b..a2f0370993 100644 --- a/javascript/src/parsePrism.js +++ b/javascript/src/parsePrism.js @@ -146,7 +146,7 @@ function dumpOptions(options) { values.push(1); } else if (options.version.match(/^3\.4(\.\d+)?$/)) { values.push(2); - } else if (options.version.match(/^3\.5(\.\d+)?$/)) { + } else if (options.version.match(/^3\.5(\.\d+)?$/) || options.version.match(/^4\.0(\.\d+)?$/)) { values.push(3); } else { throw new Error(`Unsupported version '${options.version}' in compiler options`); diff --git a/lib/prism/ffi.rb b/lib/prism/ffi.rb index f6ad6f98b1..7e6103fde7 100644 --- a/lib/prism/ffi.rb +++ b/lib/prism/ffi.rb @@ -432,10 +432,8 @@ def dump_options_version(version) 1 when /\A3\.4(\.\d+)?\z/ 2 - when /\A3\.5(\.\d+)?\z/ + when /\A3\.5(\.\d+)?\z/, /\A4\.0(\.\d+)?\z/ 3 - when /\A4\.0(\.\d+)?\z/ - 4 else if current raise CurrentVersionError, RUBY_VERSION diff --git a/lib/prism/translation.rb b/lib/prism/translation.rb index d127f2006c..7933b4a722 100644 --- a/lib/prism/translation.rb +++ b/lib/prism/translation.rb @@ -10,6 +10,7 @@ module Translation # steep:ignore autoload :Parser33, "prism/translation/parser33" autoload :Parser34, "prism/translation/parser34" autoload :Parser35, "prism/translation/parser35" + autoload :Parser40, "prism/translation/parser40" autoload :Ripper, "prism/translation/ripper" autoload :RubyParser, "prism/translation/ruby_parser" end diff --git a/lib/prism/translation/parser.rb b/lib/prism/translation/parser.rb index 1ad7a193c4..23245dc383 100644 --- a/lib/prism/translation/parser.rb +++ b/lib/prism/translation/parser.rb @@ -84,7 +84,7 @@ def initialize(builder = Prism::Translation::Parser::Builder.new, parser: Prism) end def version # :nodoc: - 35 + 40 end # The default encoding for Ruby files is UTF-8. @@ -356,8 +356,8 @@ def convert_for_prism(version) "3.3.1" when 34 "3.4.0" - when 35 - "3.5.0" + when 35, 40 + "4.0.0" else "latest" end diff --git a/lib/prism/translation/parser35.rb b/lib/prism/translation/parser35.rb index 79cd59cbd9..52eeeb6c8c 100644 --- a/lib/prism/translation/parser35.rb +++ b/lib/prism/translation/parser35.rb @@ -3,11 +3,6 @@ module Prism module Translation - # This class is the entry-point for Ruby 3.5 of `Prism::Translation::Parser`. - class Parser35 < Parser - def version # :nodoc: - 35 - end - end + Parser35 = Parser40 # :nodoc: end end diff --git a/lib/prism/translation/parser40.rb b/lib/prism/translation/parser40.rb new file mode 100644 index 0000000000..2ec7445882 --- /dev/null +++ b/lib/prism/translation/parser40.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true +# :markup: markdown + +module Prism + module Translation + # This class is the entry-point for Ruby 4.0 of `Prism::Translation::Parser`. + class Parser40 < Parser + def version # :nodoc: + 40 + end + end + end +end diff --git a/lib/prism/translation/parser_current.rb b/lib/prism/translation/parser_current.rb index 1b1794abbe..76d71e9409 100644 --- a/lib/prism/translation/parser_current.rb +++ b/lib/prism/translation/parser_current.rb @@ -10,8 +10,8 @@ module Translation ParserCurrent = Parser33 when /^3\.4\./ ParserCurrent = Parser34 - when /^3\.5\./ - ParserCurrent = Parser35 + when /^3\.5\./, /^4\.0\./ + ParserCurrent = Parser40 else # Keep this in sync with released Ruby. parser = Parser34 diff --git a/prism.gemspec b/prism.gemspec index 168f8211ff..10c2eaad20 100644 --- a/prism.gemspec +++ b/prism.gemspec @@ -101,6 +101,7 @@ Gem::Specification.new do |spec| "lib/prism/translation/parser33.rb", "lib/prism/translation/parser34.rb", "lib/prism/translation/parser35.rb", + "lib/prism/translation/parser40.rb", "lib/prism/translation/parser/builder.rb", "lib/prism/translation/parser/compiler.rb", "lib/prism/translation/parser/lexer.rb", @@ -123,6 +124,7 @@ Gem::Specification.new do |spec| "rbi/prism/translation/parser33.rbi", "rbi/prism/translation/parser34.rbi", "rbi/prism/translation/parser35.rbi", + "rbi/prism/translation/parser40.rbi", "rbi/prism/translation/ripper.rbi", "rbi/prism/visitor.rbi", "sig/prism.rbs", diff --git a/rbi/prism/translation/parser35.rbi b/rbi/prism/translation/parser35.rbi index 0239fc82ad..68d44e70c6 100644 --- a/rbi/prism/translation/parser35.rbi +++ b/rbi/prism/translation/parser35.rbi @@ -1,6 +1,4 @@ # typed: strict class Prism::Translation::Parser35 < Prism::Translation::Parser - sig { override.returns(Integer) } - def version; end end diff --git a/rbi/prism/translation/parser40.rbi b/rbi/prism/translation/parser40.rbi new file mode 100644 index 0000000000..218682365b --- /dev/null +++ b/rbi/prism/translation/parser40.rbi @@ -0,0 +1,6 @@ +# typed: strict + +class Prism::Translation::Parser40 < Prism::Translation::Parser + sig { override.returns(Integer) } + def version; end +end diff --git a/snapshots/3.5/endless_methods_command_call.txt b/snapshots/4.0/endless_methods_command_call.txt similarity index 100% rename from snapshots/3.5/endless_methods_command_call.txt rename to snapshots/4.0/endless_methods_command_call.txt diff --git a/snapshots/3.5/leading_logical.txt b/snapshots/4.0/leading_logical.txt similarity index 100% rename from snapshots/3.5/leading_logical.txt rename to snapshots/4.0/leading_logical.txt diff --git a/src/options.c b/src/options.c index 373d76a21f..4a8953da7d 100644 --- a/src/options.c +++ b/src/options.c @@ -88,12 +88,7 @@ pm_options_version_set(pm_options_t *options, const char *version, size_t length return true; } - if (strncmp(version, "3.5", 3) == 0) { - options->version = PM_OPTIONS_VERSION_CRUBY_3_5; - return true; - } - - if (strncmp(version, "4.0", 3) == 0) { + if (strncmp(version, "3.5", 3) == 0 || strncmp(version, "4.0", 3) == 0) { options->version = PM_OPTIONS_VERSION_CRUBY_4_0; return true; } @@ -101,23 +96,18 @@ pm_options_version_set(pm_options_t *options, const char *version, size_t length return false; } - if (length >= 4) { - if (strncmp(version, "3.3.", 4) == 0 && is_number(version + 4, length - 4)) { + if (length >= 4 && is_number(version + 4, length - 4)) { + if (strncmp(version, "3.3.", 4) == 0) { options->version = PM_OPTIONS_VERSION_CRUBY_3_3; return true; } - if (strncmp(version, "3.4.", 4) == 0 && is_number(version + 4, length - 4)) { + if (strncmp(version, "3.4.", 4) == 0) { options->version = PM_OPTIONS_VERSION_CRUBY_3_4; return true; } - if (strncmp(version, "3.5.", 4) == 0 && is_number(version + 4, length - 4)) { - options->version = PM_OPTIONS_VERSION_CRUBY_3_5; - return true; - } - - if (strncmp(version, "4.0.", 4) == 0 && is_number(version + 4, length - 4)) { + if (strncmp(version, "3.5.", 4) == 0 || strncmp(version, "4.0.", 4) == 0) { options->version = PM_OPTIONS_VERSION_CRUBY_4_0; return true; } diff --git a/src/prism.c b/src/prism.c index 03b12e9db8..02dd2f1175 100644 --- a/src/prism.c +++ b/src/prism.c @@ -10864,11 +10864,11 @@ parser_lex(pm_parser_t *parser) { } - // If we are parsing as CRuby 3.5 or later and we + // If we are parsing as CRuby 4.0 or later and we // hit a '&&' or a '||' then we will lex the ignored // newline. if ( - (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5) && + (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) && following && ( (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '&') || (peek_at(parser, following) == '|' && peek_at(parser, following + 1) == '|') || @@ -10915,7 +10915,7 @@ parser_lex(pm_parser_t *parser) { LEX(PM_TOKEN_AMPERSAND_DOT); } - if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5) { + if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) { // If we hit an && then we are in a logical chain // and we need to return the logical operator. if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '&') { @@ -19625,7 +19625,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b statements = (pm_node_t *) pm_statements_node_create(parser); bool allow_command_call; - if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5) { + if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) { allow_command_call = accepts_command_call; } else { // Allow `def foo = puts "Hello"` but not `private def foo = puts "Hello"` diff --git a/test/prism/api/parse_test.rb b/test/prism/api/parse_test.rb index 1f885fa493..bb1761109f 100644 --- a/test/prism/api/parse_test.rb +++ b/test/prism/api/parse_test.rb @@ -119,6 +119,9 @@ def test_version assert Prism.parse_success?("1 + 1", version: "3.5") assert Prism.parse_success?("1 + 1", version: "3.5.0") + assert Prism.parse_success?("1 + 1", version: "4.0") + assert Prism.parse_success?("1 + 1", version: "4.0.0") + assert Prism.parse_success?("1 + 1", version: "latest") # Test edge case diff --git a/test/prism/fixtures/3.5/endless_methods_command_call.txt b/test/prism/fixtures/4.0/endless_methods_command_call.txt similarity index 100% rename from test/prism/fixtures/3.5/endless_methods_command_call.txt rename to test/prism/fixtures/4.0/endless_methods_command_call.txt diff --git a/test/prism/fixtures/3.5/leading_logical.txt b/test/prism/fixtures/4.0/leading_logical.txt similarity index 100% rename from test/prism/fixtures/3.5/leading_logical.txt rename to test/prism/fixtures/4.0/leading_logical.txt diff --git a/test/prism/fixtures_test.rb b/test/prism/fixtures_test.rb index 0f0577c10d..2aebb18477 100644 --- a/test/prism/fixtures_test.rb +++ b/test/prism/fixtures_test.rb @@ -35,8 +35,8 @@ class FixturesTest < TestCase except << "3.3-3.3/return_in_sclass.txt" # Leaving these out until they are supported by parse.y. - except << "3.5/leading_logical.txt" - except << "3.5/endless_methods_command_call.txt" + 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" diff --git a/test/prism/lex_test.rb b/test/prism/lex_test.rb index 9682bf8a32..19dd845d75 100644 --- a/test/prism/lex_test.rb +++ b/test/prism/lex_test.rb @@ -43,10 +43,10 @@ class LexTest < TestCase end # https://bugs.ruby-lang.org/issues/20925 - except << "3.5/leading_logical.txt" + except << "4.0/leading_logical.txt" # https://bugs.ruby-lang.org/issues/17398#note-12 - except << "3.5/endless_methods_command_call.txt" + except << "4.0/endless_methods_command_call.txt" # https://bugs.ruby-lang.org/issues/21168#note-5 except << "command_method_call_2.txt" diff --git a/test/prism/locals_test.rb b/test/prism/locals_test.rb index 439625b750..814c9a9978 100644 --- a/test/prism/locals_test.rb +++ b/test/prism/locals_test.rb @@ -38,8 +38,8 @@ class LocalsTest < TestCase "3.3-3.3/return_in_sclass.txt", # Leaving these out until they are supported by parse.y. - "3.5/leading_logical.txt", - "3.5/endless_methods_command_call.txt", + "4.0/leading_logical.txt", + "4.0/endless_methods_command_call.txt", "command_method_call_2.txt" ] diff --git a/test/prism/ractor_test.rb b/test/prism/ractor_test.rb index 6169940beb..0e008ffb08 100644 --- a/test/prism/ractor_test.rb +++ b/test/prism/ractor_test.rb @@ -64,7 +64,7 @@ def with_ractor(*arguments, &block) else ractor = ignore_warnings { Ractor.new(*arguments, &block) } - # Somewhere in the Ruby 3.5.* series, Ractor#take was removed and + # Somewhere in the Ruby 4.0.* series, Ractor#take was removed and # Ractor#value was added. puts(ractor.respond_to?(:value) ? ractor.value : ractor.take) end diff --git a/test/prism/ruby/parser_test.rb b/test/prism/ruby/parser_test.rb index 3104369d3e..1629c36b38 100644 --- a/test/prism/ruby/parser_test.rb +++ b/test/prism/ruby/parser_test.rb @@ -69,10 +69,10 @@ class ParserTest < TestCase "3.4/circular_parameters.txt", # Cannot yet handling leading logical operators. - "3.5/leading_logical.txt", + "4.0/leading_logical.txt", - # Ruby >= 3.5 specific syntax - "3.5/endless_methods_command_call.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", @@ -172,6 +172,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(".") + return if major == "3" && minor == "5" # TODO: Remove once ruby-dev becomes 4.0 # 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 12c854aea6..400139acc0 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -9,7 +9,7 @@ class RipperTest < TestCase # Skip these tests that Ripper is reporting the wrong results for. incorrect = [ # Not yet supported. - "3.5/leading_logical.txt", + "4.0/leading_logical.txt", # Ripper incorrectly attributes the block to the keyword. "seattlerb/block_break.txt", @@ -40,7 +40,7 @@ class RipperTest < TestCase "3.4/circular_parameters.txt", # https://bugs.ruby-lang.org/issues/17398#note-12 - "3.5/endless_methods_command_call.txt", + "4.0/endless_methods_command_call.txt", # https://bugs.ruby-lang.org/issues/21168#note-5 "command_method_call_2.txt", diff --git a/test/prism/ruby/ruby_parser_test.rb b/test/prism/ruby/ruby_parser_test.rb index 42a888be82..fae5077e20 100644 --- a/test/prism/ruby/ruby_parser_test.rb +++ b/test/prism/ruby/ruby_parser_test.rb @@ -84,8 +84,8 @@ class RubyParserTest < TestCase "3.4/circular_parameters.txt", - "3.5/endless_methods_command_call.txt", - "3.5/leading_logical.txt", + "4.0/endless_methods_command_call.txt", + "4.0/leading_logical.txt", # https://bugs.ruby-lang.org/issues/21168#note-5 "command_method_call_2.txt", diff --git a/test/prism/test_helper.rb b/test/prism/test_helper.rb index faf6117668..c03f70b2cd 100644 --- a/test/prism/test_helper.rb +++ b/test/prism/test_helper.rb @@ -230,7 +230,7 @@ def self.windows? end # All versions that prism can parse - SYNTAX_VERSIONS = %w[3.3 3.4 3.5 4.0] + SYNTAX_VERSIONS = %w[3.3 3.4 4.0] # Returns an array of ruby versions that a given filepath should test against: # test.txt # => all available versions @@ -256,6 +256,7 @@ def current_major_minor if RUBY_VERSION >= "3.3.0" def test_all_syntax_versions_present + return if RUBY_VERSION.start_with?("3.5") # TODO: Remove once ruby-dev becomes 4.0 assert_include(SYNTAX_VERSIONS, current_major_minor) end end