diff --git a/ext/prism/extension.c b/ext/prism/extension.c index 363144970c..fe3cd6a8f3 100644 --- a/ext/prism/extension.c +++ b/ext/prism/extension.c @@ -205,18 +205,14 @@ build_options_i(VALUE key, VALUE value, VALUE argument) { rb_exc_raise(rb_exc_new_cstr(rb_cPrismCurrentVersionError, ruby_version)); } } else if (RSTRING_LEN(value) == 7 && strncmp(version, "nearest", 7) == 0) { - const char *nearest_version; - - if (ruby_version[0] < '3' || (ruby_version[0] == '3' && ruby_version[2] < '3')) { - nearest_version = "3.3"; - } else if (ruby_version[0] > '4' || (ruby_version[0] == '4' && ruby_version[2] > '1')) { - nearest_version = "4.1"; - } else { - nearest_version = ruby_version; - } - - if (!pm_options_version_set(options, nearest_version, 3)) { - rb_raise(rb_eArgError, "invalid nearest version: %s", nearest_version); + if (!pm_options_version_set(options, ruby_version, 3)) { + // Prism doesn't know this specific version. Is it lower? + if (ruby_version[0] < '3' || (ruby_version[0] == '3' && ruby_version[2] < '3')) { + options->version == PM_OPTIONS_VERSION_CRUBY_3_3; + } else { + // Must be higher. + options->version == PM_OPTIONS_VERSION_LATEST; + } } } else if (!pm_options_version_set(options, version, RSTRING_LEN(value))) { rb_raise(rb_eArgError, "invalid version: %" PRIsVALUE, value); diff --git a/lib/prism/ffi.rb b/lib/prism/ffi.rb index 57d878a33f..618324ded6 100644 --- a/lib/prism/ffi.rb +++ b/lib/prism/ffi.rb @@ -423,27 +423,28 @@ def dump_options_command_line(options) # Return the value that should be dumped for the version option. def dump_options_version(version) - checking = - case version - when "current" - RUBY_VERSION - when "latest" - nil - when "nearest" - if RUBY_VERSION <= "3.3" - "3.3" - elsif RUBY_VERSION >= "4.1" - "4.1" - else - RUBY_VERSION - end + case version + when "current" + version_string_to_number(RUBY_VERSION) || raise(CurrentVersionError, RUBY_VERSION) + when "latest", nil + 0 # Handled in pm_parser_init + when "nearest" + dump = version_string_to_number(RUBY_VERSION) + return dump if dump + if RUBY_VERSION < "3.3" + version_string_to_number("3.3") else - version + 0 # Handled in pm_parser_init end + else + version_string_to_number(version) || raise(ArgumentError, "invalid version: #{version}") + end + end - case checking - when nil - 0 # Handled in pm_parser_init + # Converts a version string like "4.0.0" or "4.0" into a number. + # Returns nil if the version is unknown. + def version_string_to_number(version) + case version when /\A3\.3(\.\d+)?\z/ 1 when /\A3\.4(\.\d+)?\z/ @@ -452,12 +453,6 @@ def dump_options_version(version) 3 when /\A4\.1(\.\d+)?\z/ 4 - else - if version == "current" - raise CurrentVersionError, RUBY_VERSION - else - raise ArgumentError, "invalid version: #{version}" - end end end diff --git a/test/prism/api/parse_test.rb b/test/prism/api/parse_test.rb index bbf28201ff..c9a47c1a61 100644 --- a/test/prism/api/parse_test.rb +++ b/test/prism/api/parse_test.rb @@ -154,6 +154,10 @@ def test_version_current end end + def test_nearest + assert Prism.parse_success?("1 + 1", version: "nearest") + end + def test_scopes assert_kind_of Prism::CallNode, Prism.parse_statement("foo") assert_kind_of Prism::LocalVariableReadNode, Prism.parse_statement("foo", scopes: [[:foo]])