From 8318a113ca1d42d86f16f3854ba3bd3120a87d5b Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:16:40 +0200 Subject: [PATCH] Do not use `0` to indicate the latest ruby version to parse This makes it hard to do version checks against this value. The current version checks work because there are so few possible values at the moment. As an example, PR 3337 introduces new syntax for ruby 3.5 and uses `PM_OPTIONS_VERSION_LATEST` as its version guard. Because what is considered the latest changes every year, it must later be changed to `parser->version == parser->version == PM_OPTIONS_VERSION_CRUBY_3_5 || parser->version == PM_OPTIONS_VERSION_LATEST`, with one extra version each year. With this change, the PR can instead write `parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5` which is self-explanatory and works for future versions. --- include/prism/options.h | 12 +++++++++--- java/org/prism/ParsingOptions.java | 5 +++-- javascript/src/parsePrism.js | 6 ++++-- lib/prism/ffi.rb | 4 ++-- src/options.c | 4 ++-- src/prism.c | 26 ++++++++++++++++---------- 6 files changed, 36 insertions(+), 21 deletions(-) diff --git a/include/prism/options.h b/include/prism/options.h index 2f64701b0c..092fda4f07 100644 --- a/include/prism/options.h +++ b/include/prism/options.h @@ -82,14 +82,20 @@ typedef void (*pm_options_shebang_callback_t)(struct pm_options *options, const * parse in the same way as a specific version of CRuby would have. */ typedef enum { - /** The current version of prism. */ - PM_OPTIONS_VERSION_LATEST = 0, + /** If an explicit version is not provided, the current version of prism will be used. */ + PM_OPTIONS_VERSION_UNSET = 0, /** The vendored version of prism in CRuby 3.3.x. */ PM_OPTIONS_VERSION_CRUBY_3_3 = 1, /** The vendored version of prism in CRuby 3.4.x. */ - PM_OPTIONS_VERSION_CRUBY_3_4 = 2 + PM_OPTIONS_VERSION_CRUBY_3_4 = 2, + + /** The vendored version of prism in CRuby 3.5.x. */ + PM_OPTIONS_VERSION_CRUBY_3_5 = 3, + + /** The current version of prism. */ + PM_OPTIONS_VERSION_LATEST = PM_OPTIONS_VERSION_CRUBY_3_5 } pm_options_version_t; /** diff --git a/java/org/prism/ParsingOptions.java b/java/org/prism/ParsingOptions.java index d4652f069a..4aff7dbe35 100644 --- a/java/org/prism/ParsingOptions.java +++ b/java/org/prism/ParsingOptions.java @@ -12,9 +12,10 @@ public abstract class ParsingOptions { * See pm_options_version_t in include/prism/options.h. */ public enum SyntaxVersion { - LATEST(0), + LATEST(0), // Handled in pm_parser_init V3_3(1), - V3_4(2); + V3_4(2), + V3_5(3); private final int value; diff --git a/javascript/src/parsePrism.js b/javascript/src/parsePrism.js index 535e6e9a0f..d021c63a9b 100644 --- a/javascript/src/parsePrism.js +++ b/javascript/src/parsePrism.js @@ -140,12 +140,14 @@ function dumpOptions(options) { values.push(dumpCommandLineOptions(options)); template.push("C"); - if (!options.version || options.version === "latest" || options.version.match(/^3\.5(\.\d+)?$/)) { - values.push(0); + if (!options.version || options.version === "latest") { + values.push(0); // Handled in pm_parser_init } else if (options.version.match(/^3\.3(\.\d+)?$/)) { values.push(1); } else if (options.version.match(/^3\.4(\.\d+)?$/)) { values.push(2); + } else if (options.version.match(/^3\.5(\.\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 5a4ba09a4f..1e1bf8b1c8 100644 --- a/lib/prism/ffi.rb +++ b/lib/prism/ffi.rb @@ -422,13 +422,13 @@ def dump_options_command_line(options) def dump_options_version(version) case version when nil, "latest" - 0 + 0 # Handled in pm_parser_init when /\A3\.3(\.\d+)?\z/ 1 when /\A3\.4(\.\d+)?\z/ 2 when /\A3\.5(\.\d+)?\z/ - 0 + 3 else raise ArgumentError, "invalid version: #{version}" end diff --git a/src/options.c b/src/options.c index a457178ce8..1b5c022cf5 100644 --- a/src/options.c +++ b/src/options.c @@ -89,7 +89,7 @@ pm_options_version_set(pm_options_t *options, const char *version, size_t length } if (strncmp(version, "3.5", 3) == 0) { - options->version = PM_OPTIONS_VERSION_LATEST; + options->version = PM_OPTIONS_VERSION_CRUBY_3_5; return true; } @@ -108,7 +108,7 @@ pm_options_version_set(pm_options_t *options, const char *version, size_t length } if (strncmp(version, "3.5.", 4) == 0 && is_number(version + 4, length - 4)) { - options->version = PM_OPTIONS_VERSION_LATEST; + options->version = PM_OPTIONS_VERSION_CRUBY_3_5; return true; } } diff --git a/src/prism.c b/src/prism.c index 8707330c95..e071199590 100644 --- a/src/prism.c +++ b/src/prism.c @@ -1409,7 +1409,7 @@ pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) { static inline void pm_conditional_predicate_warn_write_literal(pm_parser_t *parser, const pm_node_t *node) { if (pm_conditional_predicate_warn_write_literal_p(node)) { - pm_parser_warn_node(parser, node, parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_WARN_EQUAL_IN_CONDITIONAL_3_3 : PM_WARN_EQUAL_IN_CONDITIONAL); + pm_parser_warn_node(parser, node, parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_WARN_EQUAL_IN_CONDITIONAL_3_3 : PM_WARN_EQUAL_IN_CONDITIONAL); } } @@ -2976,7 +2976,7 @@ pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const */ static void pm_index_arguments_check(pm_parser_t *parser, const pm_arguments_node_t *arguments, const pm_node_t *block) { - if (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) { + if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) { if (arguments != NULL && PM_NODE_FLAG_P(arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS)) { pm_node_t *node; PM_NODE_LIST_FOREACH(&arguments->arguments, index, node) { @@ -9094,7 +9094,7 @@ lex_global_variable(pm_parser_t *parser) { } while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0); // $0 isn't allowed to be followed by anything. - pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; + pm_diagnostic_id_t diag_id = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, diag_id); } @@ -9131,7 +9131,7 @@ lex_global_variable(pm_parser_t *parser) { } else { // If we get here, then we have a $ followed by something that // isn't recognized as a global variable. - pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; + pm_diagnostic_id_t diag_id = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; const uint8_t *end = parser->current.end + parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); PM_PARSER_ERR_FORMAT(parser, parser->current.start, end, diag_id, (int) (end - parser->current.start), (const char *) parser->current.start); } @@ -10158,7 +10158,7 @@ lex_at_variable(pm_parser_t *parser) { } } else if (parser->current.end < end && pm_char_is_decimal_digit(*parser->current.end)) { pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE; - if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3) { + if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) { diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3; } @@ -14648,7 +14648,7 @@ parse_parameters( parser_lex(parser); pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name); - uint32_t reads = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; + uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true); pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT, (uint16_t) (depth + 1)); @@ -14664,7 +14664,7 @@ parse_parameters( // If the value of the parameter increased the number of // reads of that parameter, then we need to warn that we // have a circular definition. - if ((parser->version == PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) { + if ((parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) { PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, name, PM_ERR_PARAMETER_CIRCULAR); } @@ -14749,13 +14749,13 @@ parse_parameters( if (token_begins_expression_p(parser->current.type)) { pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local); - uint32_t reads = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; + uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true); pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT_KW, (uint16_t) (depth + 1)); if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser); - if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) { + if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) { PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_PARAMETER_CIRCULAR); } @@ -16459,7 +16459,7 @@ parse_variable(pm_parser_t *parser) { pm_node_list_append(¤t_scope->implicit_parameters, node); return node; - } else if ((parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) && pm_token_is_it(parser->previous.start, parser->previous.end)) { + } else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) { pm_node_t *node = (pm_node_t *) pm_it_local_variable_read_node_create(parser, &parser->previous); pm_node_list_append(¤t_scope->implicit_parameters, node); @@ -22628,6 +22628,12 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm } } + // Now that we have established the user-provided options, check if + // a version was given and parse as the latest version otherwise. + if (parser->version == PM_OPTIONS_VERSION_UNSET) { + parser->version = PM_OPTIONS_VERSION_LATEST; + } + pm_accepts_block_stack_push(parser, true); // Skip past the UTF-8 BOM if it exists.