From d282e76fb6c9f56ac44b3abfc8a3fc9f10edd6f0 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Mon, 8 Dec 2025 14:56:06 -0800 Subject: [PATCH] Bump Prism to v1.5.2 [Backport #21187] --- lib/prism/polyfill/warn.rb | 38 ++++---- lib/prism/prism.gemspec | 2 +- lib/prism/translation/parser.rb | 9 +- lib/prism/translation/ruby_parser.rb | 2 +- prism/config.yml | 3 +- prism/extension.h | 2 +- prism/prism.c | 93 +++++++++++++++---- prism/templates/include/prism/ast.h.erb | 2 + prism/templates/lib/prism/serialize.rb.erb | 2 +- prism/templates/src/diagnostic.c.erb | 1 + prism/util/pm_string.c | 14 ++- prism/version.h | 4 +- test/prism/errors/command_calls.txt | 7 ++ test/prism/errors/command_calls_33.txt | 6 ++ test/prism/errors/command_calls_34.txt | 24 +++++ test/prism/errors/def_with_optional_splat.txt | 6 ++ ...endless_method_command_call_parameters.txt | 24 +++++ test/prism/fixtures/character_literal.txt | 2 + test/prism/fixtures/command_method_call_2.txt | 3 + .../string_concatination_frozen_false.txt | 5 + .../string_concatination_frozen_true.txt | 5 + test/prism/fixtures_test.rb | 2 + test/prism/lex_test.rb | 3 + test/prism/locals_test.rb | 3 +- test/prism/ruby/parser_test.rb | 3 + test/prism/ruby/ripper_test.rb | 3 + test/prism/ruby/ruby_parser_test.rb | 4 + 27 files changed, 216 insertions(+), 56 deletions(-) create mode 100644 test/prism/errors/command_calls_33.txt create mode 100644 test/prism/errors/command_calls_34.txt create mode 100644 test/prism/errors/def_with_optional_splat.txt create mode 100644 test/prism/errors/endless_method_command_call_parameters.txt create mode 100644 test/prism/fixtures/character_literal.txt create mode 100644 test/prism/fixtures/command_method_call_2.txt create mode 100644 test/prism/fixtures/string_concatination_frozen_false.txt create mode 100644 test/prism/fixtures/string_concatination_frozen_true.txt diff --git a/lib/prism/polyfill/warn.rb b/lib/prism/polyfill/warn.rb index 560380d308..76a4264623 100644 --- a/lib/prism/polyfill/warn.rb +++ b/lib/prism/polyfill/warn.rb @@ -7,17 +7,14 @@ if (method = Kernel.instance_method(:warn)).respond_to?(:parameters) ? method.pa Kernel.prepend( Module.new { def warn(*msgs, uplevel: nil, category: nil) # :nodoc: - uplevel = - case uplevel - when nil - 1 - when Integer - uplevel + 1 - else - uplevel.to_int + 1 - end - - super(*msgs, uplevel: uplevel) + case uplevel + when nil + super(*msgs) + when Integer + super(*msgs, uplevel: uplevel + 1) + else + super(*msgs, uplevel: uplevel.to_int + 1) + end end } ) @@ -25,17 +22,14 @@ if (method = Kernel.instance_method(:warn)).respond_to?(:parameters) ? method.pa Object.prepend( Module.new { def warn(*msgs, uplevel: nil, category: nil) # :nodoc: - uplevel = - case uplevel - when nil - 1 - when Integer - uplevel + 1 - else - uplevel.to_int + 1 - end - - super(*msgs, uplevel: uplevel) + case uplevel + when nil + super(*msgs) + when Integer + super(*msgs, uplevel: uplevel + 1) + else + super(*msgs, uplevel: uplevel.to_int + 1) + end end } ) diff --git a/lib/prism/prism.gemspec b/lib/prism/prism.gemspec index 65305d0ec1..8f6075ad96 100644 --- a/lib/prism/prism.gemspec +++ b/lib/prism/prism.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |spec| spec.name = "prism" - spec.version = "1.5.1" + spec.version = "1.5.2" spec.authors = ["Shopify"] spec.email = ["ruby@shopify.com"] diff --git a/lib/prism/translation/parser.rb b/lib/prism/translation/parser.rb index a7888f77ec..1ad7a193c4 100644 --- a/lib/prism/translation/parser.rb +++ b/lib/prism/translation/parser.rb @@ -19,6 +19,13 @@ module Prism # whitequark/parser gem's syntax tree. It inherits from the base parser for # the parser gem, and overrides the parse* methods to parse with prism and # then translate. + # + # Note that this version of the parser always parses using the latest + # version of Ruby syntax supported by Prism. If you want specific version + # support, use one of the version-specific subclasses, such as + # `Prism::Translation::Parser34`. If you want to parse using the same + # version of Ruby syntax as the currently running version of Ruby, use + # `Prism::Translation::ParserCurrent`. class Parser < ::Parser::Base Diagnostic = ::Parser::Diagnostic # :nodoc: private_constant :Diagnostic @@ -77,7 +84,7 @@ module Prism end def version # :nodoc: - 34 + 35 end # The default encoding for Ruby files is UTF-8. diff --git a/lib/prism/translation/ruby_parser.rb b/lib/prism/translation/ruby_parser.rb index ac538a2e97..2ca7da0bf2 100644 --- a/lib/prism/translation/ruby_parser.rb +++ b/lib/prism/translation/ruby_parser.rb @@ -152,7 +152,7 @@ module Prism # ^^ # ``` def visit_back_reference_read_node(node) - s(node, :back_ref, node.name.name.delete_prefix("$").to_sym) + s(node, :back_ref, node.name.to_s.delete_prefix("$").to_sym) end # ``` diff --git a/prism/config.yml b/prism/config.yml index 3366b6235d..ed5c9d8b9c 100644 --- a/prism/config.yml +++ b/prism/config.yml @@ -60,6 +60,7 @@ errors: - CONDITIONAL_WHILE_PREDICATE - CONSTANT_PATH_COLON_COLON_CONSTANT - DEF_ENDLESS + - DEF_ENDLESS_PARAMETERS - DEF_ENDLESS_SETTER - DEF_NAME - DEF_PARAMS_TERM @@ -1800,7 +1801,7 @@ nodes: Represents the predicate of the case statement. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). case true; when false; end - ^^^^ + ^^^^ - name: conditions type: node[] kind: WhenNode diff --git a/prism/extension.h b/prism/extension.h index b18e770d92..1f15b775ff 100644 --- a/prism/extension.h +++ b/prism/extension.h @@ -1,7 +1,7 @@ #ifndef PRISM_EXT_NODE_H #define PRISM_EXT_NODE_H -#define EXPECTED_PRISM_VERSION "1.5.1" +#define EXPECTED_PRISM_VERSION "1.5.2" #include #include diff --git a/prism/prism.c b/prism/prism.c index 06419d1378..cc1896ee34 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -2622,10 +2622,11 @@ pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_argument // There are certain flags that we want to use internally but don't want to // expose because they are not relevant beyond parsing. Therefore we'll define // them here and not define them in config.yml/a header file. -static const pm_node_flags_t PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = 0x4; -static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = 0x40; -static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = 0x80; -static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = 0x100; +static const pm_node_flags_t PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = (1 << 2); + +static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = ((PM_CALL_NODE_FLAGS_LAST - 1) << 1); +static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = ((PM_CALL_NODE_FLAGS_LAST - 1) << 2); +static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = ((PM_CALL_NODE_FLAGS_LAST - 1) << 3); /** * Allocate and initialize a new CallNode node. This sets everything to NULL or @@ -5279,6 +5280,12 @@ pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_ switch (PM_NODE_TYPE(part)) { case PM_STRING_NODE: + // If inner string is not frozen, it stops being a static literal. We should *not* clear other flags, + // because concatenating two frozen strings (`'foo' 'bar'`) is still frozen. This holds true for + // as long as this interpolation only consists of other string literals. + if (!PM_NODE_FLAG_P(part, PM_STRING_FLAGS_FROZEN)) { + pm_node_flag_unset((pm_node_t *) node, PM_NODE_FLAG_STATIC_LITERAL); + } part->flags = (pm_node_flags_t) ((part->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE); break; case PM_INTERPOLATED_STRING_NODE: @@ -14443,6 +14450,17 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for if (accepted_newline) { pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA); } + + // If this is a command call and an argument takes a block, + // there can be no further arguments. For example, + // `foo(bar 1 do end, 2)` should be rejected. + if (PM_NODE_TYPE_P(argument, PM_CALL_NODE)) { + pm_call_node_t *call = (pm_call_node_t *) argument; + if (call->opening_loc.start == NULL && call->arguments != NULL && call->block != NULL) { + pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA); + break; + } + } } else { // If there is no comma at the end of the argument list then we're // done parsing arguments and can break out of this loop. @@ -14594,6 +14612,18 @@ update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_ord return true; } +/** + * Ensures that after parsing a parameter, the next token is not `=`. + * Some parameters like `def(* = 1)` cannot become optional. When no parens + * are present like in `def * = 1`, this creates ambiguity with endless method definitions. + */ +static inline void +refute_optional_parameter(pm_parser_t *parser) { + if (match1(parser, PM_TOKEN_EQUAL)) { + pm_parser_err_previous(parser, PM_ERR_DEF_ENDLESS_PARAMETERS); + } +} + /** * Parse a list of parameters on a method definition. */ @@ -14646,6 +14676,10 @@ parse_parameters( parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK; } + if (!uses_parentheses) { + refute_optional_parameter(parser); + } + pm_block_parameter_node_t *param = pm_block_parameter_node_create(parser, &name, &operator); if (repeated) { pm_node_flag_set_repeated_parameter((pm_node_t *)param); @@ -14667,6 +14701,10 @@ parse_parameters( bool succeeded = update_parameter_state(parser, &parser->current, &order); parser_lex(parser); + if (!uses_parentheses) { + refute_optional_parameter(parser); + } + parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL; pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous); @@ -14848,6 +14886,10 @@ parse_parameters( context_pop(parser); pm_parameters_node_keywords_append(params, param); + if (!uses_parentheses) { + refute_optional_parameter(parser); + } + // If parsing the value of the parameter resulted in error recovery, // then we can put a missing node in its place and stop parsing the // parameters entirely now. @@ -14879,6 +14921,10 @@ parse_parameters( parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS; } + if (!uses_parentheses) { + refute_optional_parameter(parser); + } + pm_node_t *param = (pm_node_t *) pm_rest_parameter_node_create(parser, &operator, &name); if (repeated) { pm_node_flag_set_repeated_parameter(param); @@ -14927,6 +14973,10 @@ parse_parameters( } } + if (!uses_parentheses) { + refute_optional_parameter(parser); + } + if (params->keyword_rest == NULL) { pm_parameters_node_keyword_rest_set(params, param); } else { @@ -18491,20 +18541,28 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b return (pm_node_t *) node; } case PM_TOKEN_CHARACTER_LITERAL: { - parser_lex(parser); - - pm_token_t opening = parser->previous; - opening.type = PM_TOKEN_STRING_BEGIN; - opening.end = opening.start + 1; - - pm_token_t content = parser->previous; - content.type = PM_TOKEN_STRING_CONTENT; - content.start = content.start + 1; - pm_token_t closing = not_provided(parser); - pm_node_t *node = (pm_node_t *) pm_string_node_create_current_string(parser, &opening, &content, &closing); + pm_node_t *node = (pm_node_t *) pm_string_node_create_current_string( + parser, + &(pm_token_t) { + .type = PM_TOKEN_STRING_BEGIN, + .start = parser->current.start, + .end = parser->current.start + 1 + }, + &(pm_token_t) { + .type = PM_TOKEN_STRING_CONTENT, + .start = parser->current.start + 1, + .end = parser->current.end + }, + &closing + ); + pm_node_flag_set(node, parse_unescaped_encoding(parser)); + // Skip past the character literal here, since now we have handled + // parser->explicit_encoding correctly. + parser_lex(parser); + // Characters can be followed by strings in which case they are // automatically concatenated. if (match1(parser, PM_TOKEN_STRING_BEGIN)) { @@ -20901,7 +20959,7 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding bool permitted = true; if (previous_binding_power != PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_USTAR)) permitted = false; - pm_node_t *value = parse_starred_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MATCH, diag_id, (uint16_t) (depth + 1)); + pm_node_t *value = parse_starred_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MODIFIER, diag_id, (uint16_t) (depth + 1)); if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE); parse_assignment_value_local(parser, value); @@ -22498,9 +22556,10 @@ parse_program(pm_parser_t *parser) { statements = wrap_statements(parser, statements); } else { flush_block_exits(parser, previous_block_exits); - pm_node_list_free(¤t_block_exits); } + pm_node_list_free(¤t_block_exits); + // If this is an empty file, then we're still going to parse all of the // statements in order to gather up all of the comments and such. Here we'll // correct the location information. diff --git a/prism/templates/include/prism/ast.h.erb b/prism/templates/include/prism/ast.h.erb index 087eb81890..e82cb78fe3 100644 --- a/prism/templates/include/prism/ast.h.erb +++ b/prism/templates/include/prism/ast.h.erb @@ -212,6 +212,8 @@ typedef enum pm_<%= flag.human %> { /** <%= value.comment %> */ PM_<%= flag.human.upcase %>_<%= value.name %> = <%= 1 << (index + Prism::Template::COMMON_FLAGS_COUNT) %>, <%- end -%> + + PM_<%= flag.human.upcase %>_LAST, } pm_<%= flag.human %>_t; <%- end -%> diff --git a/prism/templates/lib/prism/serialize.rb.erb b/prism/templates/lib/prism/serialize.rb.erb index 366878f709..271631b5ac 100644 --- a/prism/templates/lib/prism/serialize.rb.erb +++ b/prism/templates/lib/prism/serialize.rb.erb @@ -14,7 +14,7 @@ module Prism # The patch version of prism that we are expecting to find in the serialized # strings. - PATCH_VERSION = 1 + PATCH_VERSION = 2 # Deserialize the dumped output from a request to parse or parse_file. # diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb index 9a30a57e3b..2373253085 100644 --- a/prism/templates/src/diagnostic.c.erb +++ b/prism/templates/src/diagnostic.c.erb @@ -144,6 +144,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_CONDITIONAL_WHILE_PREDICATE] = { "expected a predicate expression for the `while` statement", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT] = { "expected a constant after the `::` operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_ENDLESS] = { "could not parse the endless method body", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_DEF_ENDLESS_PARAMETERS] = { "could not parse the endless method parameters", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_ENDLESS_SETTER] = { "invalid method name; a setter method cannot be defined in an endless method definition", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_NAME] = { "unexpected %s; expected a method name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_DEF_PARAMS_TERM] = { "expected a delimiter to close the parameters", PM_ERROR_LEVEL_SYNTAX }, diff --git a/prism/util/pm_string.c b/prism/util/pm_string.c index 75422fbdf2..a7493c468b 100644 --- a/prism/util/pm_string.c +++ b/prism/util/pm_string.c @@ -1,5 +1,7 @@ #include "prism/util/pm_string.h" +static const uint8_t empty_source[] = ""; + /** * Returns the size of the pm_string_t struct. This is necessary to allocate the * correct amount of memory in the FFI backend. @@ -133,8 +135,7 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) { // the source to a constant empty string and return. if (file_size == 0) { pm_string_file_handle_close(&handle); - const uint8_t source[] = ""; - *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 }; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; return PM_STRING_INIT_SUCCESS; } @@ -182,8 +183,7 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) { if (size == 0) { close(fd); - const uint8_t source[] = ""; - *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 }; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; return PM_STRING_INIT_SUCCESS; } @@ -225,8 +225,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) { // the source to a constant empty string and return. if (file_size == 0) { pm_string_file_handle_close(&handle); - const uint8_t source[] = ""; - *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 }; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; return PM_STRING_INIT_SUCCESS; } @@ -278,8 +277,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) { size_t size = (size_t) sb.st_size; if (size == 0) { close(fd); - const uint8_t source[] = ""; - *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 }; + *string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = empty_source, .length = 0 }; return PM_STRING_INIT_SUCCESS; } diff --git a/prism/version.h b/prism/version.h index 697ba0647d..c0d82dc8e2 100644 --- a/prism/version.h +++ b/prism/version.h @@ -19,11 +19,11 @@ /** * The patch version of the Prism library as an int. */ -#define PRISM_VERSION_PATCH 1 +#define PRISM_VERSION_PATCH 2 /** * The version of the Prism library as a constant string. */ -#define PRISM_VERSION "1.5.1" +#define PRISM_VERSION "1.5.2" #endif diff --git a/test/prism/errors/command_calls.txt b/test/prism/errors/command_calls.txt index 19812a1d0a..6601e5fbbc 100644 --- a/test/prism/errors/command_calls.txt +++ b/test/prism/errors/command_calls.txt @@ -1,3 +1,10 @@ [a b] ^ unexpected local variable or method; expected a `,` separator for the array elements + +[ + a b do + ^ unexpected local variable or method; expected a `,` separator for the array elements + end, +] + diff --git a/test/prism/errors/command_calls_33.txt b/test/prism/errors/command_calls_33.txt new file mode 100644 index 0000000000..13e3b35c9e --- /dev/null +++ b/test/prism/errors/command_calls_33.txt @@ -0,0 +1,6 @@ +1 if foo = bar baz + ^~~ unexpected local variable or method, expecting end-of-input + +1 and foo = bar baz + ^~~ unexpected local variable or method, expecting end-of-input + diff --git a/test/prism/errors/command_calls_34.txt b/test/prism/errors/command_calls_34.txt new file mode 100644 index 0000000000..ce62bc1492 --- /dev/null +++ b/test/prism/errors/command_calls_34.txt @@ -0,0 +1,24 @@ +foo(bar 1 do end, 2) + ^ invalid comma + ^ unexpected integer; expected a `)` to close the arguments + ^ unexpected integer, expecting end-of-input + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + +foo(bar 1 do end,) + ^ invalid comma + +foo(1, bar 2 do end) + ^ unexpected integer; expected a `)` to close the arguments + ^ unexpected integer, expecting end-of-input + ^~ unexpected 'do', expecting end-of-input + ^~ unexpected 'do', ignoring it + ^~~ unexpected 'end', ignoring it + ^ unexpected ')', ignoring it + +foo(1, bar 2) + ^ unexpected integer; expected a `)` to close the arguments + ^ unexpected integer, expecting end-of-input + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + diff --git a/test/prism/errors/def_with_optional_splat.txt b/test/prism/errors/def_with_optional_splat.txt new file mode 100644 index 0000000000..74a833ceec --- /dev/null +++ b/test/prism/errors/def_with_optional_splat.txt @@ -0,0 +1,6 @@ +def foo(*bar = nil); end + ^ unexpected '='; expected a `)` to close the parameters + ^ unexpected ')', expecting end-of-input + ^ unexpected ')', ignoring it + ^~~ unexpected 'end', ignoring it + diff --git a/test/prism/errors/endless_method_command_call_parameters.txt b/test/prism/errors/endless_method_command_call_parameters.txt new file mode 100644 index 0000000000..94c4f88fc8 --- /dev/null +++ b/test/prism/errors/endless_method_command_call_parameters.txt @@ -0,0 +1,24 @@ +def f x: = 1 + ^~ could not parse the endless method parameters + +def f ... = 1 + ^~~ could not parse the endless method parameters + +def f * = 1 + ^ could not parse the endless method parameters + +def f ** = 1 + ^~ could not parse the endless method parameters + +def f & = 1 + ^ could not parse the endless method parameters + +def f *a = 1 + ^ could not parse the endless method parameters + +def f **a = 1 + ^ could not parse the endless method parameters + +def f &a = 1 + ^ could not parse the endless method parameters + diff --git a/test/prism/fixtures/character_literal.txt b/test/prism/fixtures/character_literal.txt new file mode 100644 index 0000000000..920332123f --- /dev/null +++ b/test/prism/fixtures/character_literal.txt @@ -0,0 +1,2 @@ +# encoding: Windows-31J +p ?\u3042"" diff --git a/test/prism/fixtures/command_method_call_2.txt b/test/prism/fixtures/command_method_call_2.txt new file mode 100644 index 0000000000..165c45987a --- /dev/null +++ b/test/prism/fixtures/command_method_call_2.txt @@ -0,0 +1,3 @@ +foo(bar baz do end) + +foo(bar baz, bat) diff --git a/test/prism/fixtures/string_concatination_frozen_false.txt b/test/prism/fixtures/string_concatination_frozen_false.txt new file mode 100644 index 0000000000..abe9301408 --- /dev/null +++ b/test/prism/fixtures/string_concatination_frozen_false.txt @@ -0,0 +1,5 @@ +# frozen_string_literal: false + +'foo' 'bar' + +'foo' 'bar' "baz#{bat}" diff --git a/test/prism/fixtures/string_concatination_frozen_true.txt b/test/prism/fixtures/string_concatination_frozen_true.txt new file mode 100644 index 0000000000..829777f0a7 --- /dev/null +++ b/test/prism/fixtures/string_concatination_frozen_true.txt @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +'foo' 'bar' + +'foo' 'bar' "baz#{bat}" diff --git a/test/prism/fixtures_test.rb b/test/prism/fixtures_test.rb index b4b656fcf4..ddb6ffb40c 100644 --- a/test/prism/fixtures_test.rb +++ b/test/prism/fixtures_test.rb @@ -27,6 +27,8 @@ module Prism # Leaving these out until they are supported by parse.y. except << "leading_logical.txt" except << "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| define_method(fixture.test_name) { assert_valid_syntax(fixture.read) } diff --git a/test/prism/lex_test.rb b/test/prism/lex_test.rb index 4eacbab3e1..3a0da1a2d8 100644 --- a/test/prism/lex_test.rb +++ b/test/prism/lex_test.rb @@ -48,6 +48,9 @@ module Prism # https://bugs.ruby-lang.org/issues/17398#note-12 except << "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| define_method(fixture.test_name) { assert_lex(fixture) } end diff --git a/test/prism/locals_test.rb b/test/prism/locals_test.rb index 950e7118af..9a3224e8ef 100644 --- a/test/prism/locals_test.rb +++ b/test/prism/locals_test.rb @@ -33,7 +33,8 @@ module Prism # Leaving these out until they are supported by parse.y. "leading_logical.txt", - "endless_methods_command_call.txt" + "endless_methods_command_call.txt", + "command_method_call_2.txt" ] Fixture.each(except: except) do |fixture| diff --git a/test/prism/ruby/parser_test.rb b/test/prism/ruby/parser_test.rb index 98740f0973..10b5fca5ea 100644 --- a/test/prism/ruby/parser_test.rb +++ b/test/prism/ruby/parser_test.rb @@ -70,6 +70,9 @@ module Prism # Ruby >= 3.5 specific syntax "endless_methods_command_call.txt", + + # https://bugs.ruby-lang.org/issues/21168#note-5 + "command_method_call_2.txt", ] # These files contain code that is being parsed incorrectly by the parser diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index 39325137ba..4916ec8d9d 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -33,6 +33,9 @@ module Prism # https://bugs.ruby-lang.org/issues/17398#note-12 "endless_methods_command_call.txt", + + # https://bugs.ruby-lang.org/issues/21168#note-5 + "command_method_call_2.txt", ] # Skip these tests that we haven't implemented yet. diff --git a/test/prism/ruby/ruby_parser_test.rb b/test/prism/ruby/ruby_parser_test.rb index bcaed79791..ec55e41967 100644 --- a/test/prism/ruby/ruby_parser_test.rb +++ b/test/prism/ruby/ruby_parser_test.rb @@ -16,6 +16,7 @@ end module Prism class RubyParserTest < TestCase todos = [ + "character_literal.txt", "encoding_euc_jp.txt", "regex_char_width.txt", "seattlerb/masgn_colon3.txt", @@ -78,6 +79,9 @@ module Prism # Ruby >= 3.5 specific syntax "endless_methods_command_call.txt", + + # https://bugs.ruby-lang.org/issues/21168#note-5 + "command_method_call_2.txt", ] Fixture.each(except: failures) do |fixture|