From ed4a55fc4d7dc5f9f95983f4713cbd0c733601a3 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 17 Sep 2024 22:44:07 +0200 Subject: [PATCH] [ruby/prism] Accept all 3.3.x and 3.4.x Ruby versions for Prism.parse https://github.com/ruby/prism/commit/a4fcd5339a --- lib/prism/ffi.rb | 16 ++++++++++- prism/extension.c | 6 ++-- prism/options.c | 56 ++++++++++++++++++-------------------- test/prism/version_test.rb | 40 ++++++++++++++++++++++++++- 4 files changed, 84 insertions(+), 34 deletions(-) diff --git a/lib/prism/ffi.rb b/lib/prism/ffi.rb index 95206680f9b205..572807e8057f3d 100644 --- a/lib/prism/ffi.rb +++ b/lib/prism/ffi.rb @@ -411,6 +411,20 @@ def dump_options_command_line(options) end end + # Return the value that should be dumped for the version option. + def dump_options_version(version) + case version + when nil, "latest" + 0 + when /\A3\.3\.\d+\z/ + 1 + when /\A3\.4\.\d+\z/ + 0 + else + raise ArgumentError, "invalid version: #{version}" + end + end + # Convert the given options into a serialized options string. def dump_options(options) template = +"" @@ -443,7 +457,7 @@ def dump_options(options) values << dump_options_command_line(options) template << "C" - values << { nil => 0, "3.3.0" => 1, "3.3.1" => 1, "3.4.0" => 0, "latest" => 0 }.fetch(options[:version]) + values << dump_options_version(options[:version]) template << "C" values << (options[:encoding] == false ? 1 : 0) diff --git a/prism/extension.c b/prism/extension.c index d8b35b72136c4b..93fa7b09897747 100644 --- a/prism/extension.c +++ b/prism/extension.c @@ -770,8 +770,10 @@ parse_input(pm_string_t *input, const pm_options_t *options) { * * `version` - the version of Ruby syntax that prism should used to parse Ruby * code. By default prism assumes you want to parse with the latest version * of Ruby syntax (which you can trigger with `nil` or `"latest"`). You - * may also restrict the syntax to a specific version of Ruby. The - * supported values are `"3.3.0"` and `"3.4.0"`. + * may also restrict the syntax to a specific version of Ruby, e.g., with `"3.3.0"`. + * To parse with the same syntax version that the current Ruby is running + * use `version: RUBY_VERSION`. Raises ArgumentError if the version is not + * currently supported by Prism. */ static VALUE parse(int argc, VALUE *argv, VALUE self) { diff --git a/prism/options.c b/prism/options.c index 3004fcef2c452d..7c775a4edd63c8 100644 --- a/prism/options.c +++ b/prism/options.c @@ -1,4 +1,5 @@ #include "prism/options.h" +#include "prism/util/pm_char.h" /** * Set the shebang callback option on the given options struct. @@ -57,6 +58,10 @@ pm_options_command_line_set(pm_options_t *options, uint8_t command_line) { options->command_line = command_line; } +static bool is_number(const char *string, size_t length) { + return pm_strspn_decimal_digit((const uint8_t *) string, (ptrdiff_t) length) == length; +} + /** * Set the version option on the given options struct by parsing the given * string. If the string contains an invalid option, this returns false. @@ -64,40 +69,31 @@ pm_options_command_line_set(pm_options_t *options, uint8_t command_line) { */ PRISM_EXPORTED_FUNCTION bool pm_options_version_set(pm_options_t *options, const char *version, size_t length) { - switch (length) { - case 0: - if (version == NULL) { - options->version = PM_OPTIONS_VERSION_LATEST; - return true; - } - - return false; - case 5: - assert(version != NULL); - - if ((strncmp(version, "3.3.0", length) == 0) || (strncmp(version, "3.3.1", length) == 0)) { - options->version = PM_OPTIONS_VERSION_CRUBY_3_3; - return true; - } - - if (strncmp(version, "3.4.0", length) == 0) { - options->version = PM_OPTIONS_VERSION_LATEST; - return true; - } + if (version == NULL) { + options->version = PM_OPTIONS_VERSION_LATEST; + return true; + } - return false; - case 6: - assert(version != NULL); + if (length >= 4) { + if (strncmp(version, "3.3.", 4) == 0 && is_number(version + 4, length - 4)) { + options->version = PM_OPTIONS_VERSION_CRUBY_3_3; + return true; + } - if (strncmp(version, "latest", length) == 0) { - options->version = PM_OPTIONS_VERSION_LATEST; - return true; - } + if (strncmp(version, "3.4.", 4) == 0 && is_number(version + 4, length - 4)) { + options->version = PM_OPTIONS_VERSION_LATEST; + return true; + } + } - return false; - default: - return false; + if (length >= 6) { + if (strncmp(version, "latest", 7) == 0) { // 7 to compare the \0 as well + options->version = PM_OPTIONS_VERSION_LATEST; + return true; + } } + + return false; } /** diff --git a/test/prism/version_test.rb b/test/prism/version_test.rb index 29ee6b224cc735..843e3b70365d5e 100644 --- a/test/prism/version_test.rb +++ b/test/prism/version_test.rb @@ -4,8 +4,46 @@ module Prism class VersionTest < TestCase - def test_version_is_set + def test_prism_version_is_set refute_nil VERSION end + + def test_syntax_versions + assert Prism.parse("1 + 1", version: "3.3.0").success? + assert Prism.parse("1 + 1", version: "3.3.1").success? + assert Prism.parse("1 + 1", version: "3.3.9").success? + assert Prism.parse("1 + 1", version: "3.3.10").success? + + assert Prism.parse("1 + 1", version: "3.4.0").success? + assert Prism.parse("1 + 1", version: "3.4.9").success? + assert Prism.parse("1 + 1", version: "3.4.10").success? + + assert Prism.parse("1 + 1", version: "latest").success? + + # Test edge case + error = assert_raise ArgumentError do + Prism.parse("1 + 1", version: "latest2") + end + assert_equal "invalid version: latest2", error.message + + assert_raise ArgumentError do + Prism.parse("1 + 1", version: "3.3.a") + end + + # Not supported version syntax + assert_raise ArgumentError do + Prism.parse("1 + 1", version: "3.3") + end + + # Not supported version (too old) + assert_raise ArgumentError do + Prism.parse("1 + 1", version: "3.2.0") + end + + # Not supported version (too new) + assert_raise ArgumentError do + Prism.parse("1 + 1", version: "3.5.0") + end + end end end