From 6d69a2b8f40779bbee672ff2fd9ad4247754e040 Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Wed, 4 Feb 2026 14:48:06 -0600 Subject: [PATCH 1/3] [ruby/prism] Rename Java package to org.ruby_lang.prism This moves all of the non-JRuby Java code from the org.prism package to the org.ruby_lang.prism package (corresponding to the ruby-lang.org domain and prism project). https://github.com/ruby/prism/commit/80b7402f8b --- prism/templates/template.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prism/templates/template.rb b/prism/templates/template.rb index 65e6ed038168d9..18da0647a007bd 100755 --- a/prism/templates/template.rb +++ b/prism/templates/template.rb @@ -724,9 +724,9 @@ def locals "javascript/src/deserialize.js", "javascript/src/nodes.js", "javascript/src/visitor.js", - "java/org/prism/Loader.java", - "java/org/prism/Nodes.java", - "java/org/prism/AbstractNodeVisitor.java", + "java/org/ruby_lang/prism/Loader.java", + "java/org/ruby_lang/prism/Nodes.java", + "java/org/ruby_lang/prism/AbstractNodeVisitor.java", "lib/prism/compiler.rb", "lib/prism/dispatcher.rb", "lib/prism/dot_visitor.rb", From 731b6092e9268af960bf0f060e37504f4deb7380 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Thu, 12 Feb 2026 13:10:40 +0100 Subject: [PATCH 2/3] [ruby/prism] [Feature #19107] Allow trailing comma in method signature https://github.com/ruby/prism/commit/b7e247ce6a --- prism/prism.c | 56 ++++++++++++++----- ...w_trailing_commas_in_method_parameters.txt | 0 ...ing_commas_after_terminating_arguments.txt | 6 ++ .../trailing_comma_after_method_arguments.txt | 15 +++++ test/prism/fixtures_test.rb | 2 + test/prism/locals_test.rb | 3 + test/prism/ruby/ripper_test.rb | 2 + 7 files changed, 69 insertions(+), 15 deletions(-) rename test/prism/errors/{ => 3.3-4.0}/do_not_allow_trailing_commas_in_method_parameters.txt (100%) create mode 100644 test/prism/errors/4.1/do_not_allow_trailing_commas_after_terminating_arguments.txt create mode 100644 test/prism/fixtures/4.1/trailing_comma_after_method_arguments.txt diff --git a/prism/prism.c b/prism/prism.c index 4465b4982e13b5..7c6e3cfae4a30e 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -13907,6 +13907,43 @@ update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_ord return true; } +static inline void +parse_parameters_handle_trailing_comma( + pm_parser_t *parser, + pm_parameters_node_t *params, + pm_parameters_order_t order, + bool in_block, + bool allows_trailing_comma +) { + if (!allows_trailing_comma) { + pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA); + return; + } + + if (in_block) { + if (order >= PM_PARAMETERS_ORDER_NAMED) { + // foo do |bar,|; end + pm_node_t *param = UP(pm_implicit_rest_node_create(parser, &parser->previous)); + + if (params->rest == NULL) { + pm_parameters_node_rest_set(params, param); + } else { + pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_SPLAT_MULTI); + pm_parameters_node_posts_append(params, UP(param)); + } + } else { + // foo do |*bar,|; end + pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA); + } + } else { + // https://bugs.ruby-lang.org/issues/19107 + // Allow `def foo(bar,); end`, `def foo(*bar,); end`, etc. but not `def foo(...,); end` + if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1 || order == PM_PARAMETERS_ORDER_NOTHING_AFTER) { + pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA); + } + } +} + /** * Parse a list of parameters on a method definition. */ @@ -14255,20 +14292,7 @@ parse_parameters( } default: if (parser->previous.type == PM_TOKEN_COMMA) { - if (allows_trailing_comma && order >= PM_PARAMETERS_ORDER_NAMED) { - // If we get here, then we have a trailing comma in a - // block parameter list. - pm_node_t *param = UP(pm_implicit_rest_node_create(parser, &parser->previous)); - - if (params->rest == NULL) { - pm_parameters_node_rest_set(params, param); - } else { - pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_SPLAT_MULTI); - pm_parameters_node_posts_append(params, UP(param)); - } - } else { - pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA); - } + parse_parameters_handle_trailing_comma(parser, params, order, in_block, allows_trailing_comma); } parsing = false; @@ -18865,7 +18889,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) { params = NULL; } else { - params = parse_parameters(parser, PM_BINDING_POWER_DEFINED, true, false, true, true, false, (uint16_t) (depth + 1)); + // https://bugs.ruby-lang.org/issues/19107 + bool allow_trailing_comma = parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1; + params = parse_parameters(parser, PM_BINDING_POWER_DEFINED, true, allow_trailing_comma, true, true, false, (uint16_t) (depth + 1)); } lex_state_set(parser, PM_LEX_STATE_BEG); diff --git a/test/prism/errors/do_not_allow_trailing_commas_in_method_parameters.txt b/test/prism/errors/3.3-4.0/do_not_allow_trailing_commas_in_method_parameters.txt similarity index 100% rename from test/prism/errors/do_not_allow_trailing_commas_in_method_parameters.txt rename to test/prism/errors/3.3-4.0/do_not_allow_trailing_commas_in_method_parameters.txt diff --git a/test/prism/errors/4.1/do_not_allow_trailing_commas_after_terminating_arguments.txt b/test/prism/errors/4.1/do_not_allow_trailing_commas_after_terminating_arguments.txt new file mode 100644 index 00000000000000..b3e06f4154ee58 --- /dev/null +++ b/test/prism/errors/4.1/do_not_allow_trailing_commas_after_terminating_arguments.txt @@ -0,0 +1,6 @@ +def foo(a,b,...,);end + ^ unexpected `,` in parameters + +def foo(a,b,&block,);end + ^ unexpected `,` in parameters + diff --git a/test/prism/fixtures/4.1/trailing_comma_after_method_arguments.txt b/test/prism/fixtures/4.1/trailing_comma_after_method_arguments.txt new file mode 100644 index 00000000000000..ef1385d973f2ef --- /dev/null +++ b/test/prism/fixtures/4.1/trailing_comma_after_method_arguments.txt @@ -0,0 +1,15 @@ +def foo(a,b,c,);end + +def foo(a,b,*c,);end + +def foo(a,b,*,);end + +def foo(a,b,**c,);end + +def foo(a,b,**,);end + +def foo( + a, + b, + c, +);end diff --git a/test/prism/fixtures_test.rb b/test/prism/fixtures_test.rb index 3a704b83895054..dcbcb7c117c725 100644 --- a/test/prism/fixtures_test.rb +++ b/test/prism/fixtures_test.rb @@ -28,6 +28,8 @@ class FixturesTest < TestCase except << "command_method_call_2.txt" # https://bugs.ruby-lang.org/issues/21669 except << "4.1/void_value.txt" + # https://bugs.ruby-lang.org/issues/19107 + except << "4.1/trailing_comma_after_method_arguments.txt" Fixture.each_for_current_ruby(except: except) do |fixture| define_method(fixture.test_name) { assert_valid_syntax(fixture.read) } diff --git a/test/prism/locals_test.rb b/test/prism/locals_test.rb index bcb964aff67568..417730a8a73ba6 100644 --- a/test/prism/locals_test.rb +++ b/test/prism/locals_test.rb @@ -31,6 +31,9 @@ class LocalsTest < TestCase # https://bugs.ruby-lang.org/issues/21669 "4.1/void_value.txt", + + # https://bugs.ruby-lang.org/issues/19107 + "4.1/trailing_comma_after_method_arguments.txt", ] Fixture.each_for_current_ruby(except: except) do |fixture| diff --git a/test/prism/ruby/ripper_test.rb b/test/prism/ruby/ripper_test.rb index 39cb9395ab680e..ba01732bcb2823 100644 --- a/test/prism/ruby/ripper_test.rb +++ b/test/prism/ruby/ripper_test.rb @@ -39,6 +39,8 @@ class RipperTest < TestCase # https://bugs.ruby-lang.org/issues/21669 incorrect << "4.1/void_value.txt" + # https://bugs.ruby-lang.org/issues/19107 + incorrect << "4.1/trailing_comma_after_method_arguments.txt" # Skip these tests that we haven't implemented yet. omitted_sexp_raw = [ From b94162ab8a8659013a3b7e0d6881d8a8495f609c Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:54:40 +0100 Subject: [PATCH 3/3] [ruby/prism] Preserve line-continuation only in dedent heredocs Closes https://github.com/ruby/prism/issues/3837 While these lines are whitespace only from a runtime perspective, the line continuation is significant for AST consumers. Sort of a followup to https://github.com/ruby/prism/commit/faab217d9382186133862ce4d5ba9a9e090a9550 https://github.com/ruby/prism/commit/a8a7c6b77d --- prism/prism.c | 18 +++++++++++++++--- .../heredoc_dedent_line_continuation.txt | 5 +++++ test/prism/ruby/ruby_parser_test.rb | 1 + 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 test/prism/fixtures/heredoc_dedent_line_continuation.txt diff --git a/prism/prism.c b/prism/prism.c index 7c6e3cfae4a30e..4ab1ca4594db98 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -15957,6 +15957,19 @@ parse_heredoc_dedent_string(pm_string_t *string, size_t common_whitespace) { string->length = dest_length; } +/** + * If we end up trimming all of the whitespace from a node and it isn't + * part of a line continuation, then we'll drop it from the list entirely. + */ +static inline bool +heredoc_dedent_discard_string_node(pm_parser_t *parser, pm_string_node_t *string_node) { + if (string_node->unescaped.length == 0) { + const uint8_t *cursor = parser->start + PM_LOCATION_START(&string_node->content_loc); + return pm_memchr(cursor, '\\', string_node->content_loc.length, parser->encoding_changed, parser->encoding) == NULL; + } + return false; +} + /** * Take a heredoc node that is indented by a ~ and trim the leading whitespace. */ @@ -15967,8 +15980,7 @@ parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_w bool dedent_next = true; // Iterate over all nodes, and trim whitespace accordingly. We're going to - // keep around two indices: a read and a write. If we end up trimming all of - // the whitespace from a node, then we'll drop it from the list entirely. + // keep around two indices: a read and a write. size_t write_index = 0; pm_node_t *node; @@ -15987,7 +15999,7 @@ parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_w parse_heredoc_dedent_string(&string_node->unescaped, common_whitespace); } - if (string_node->unescaped.length == 0) { + if (heredoc_dedent_discard_string_node(parser, string_node)) { pm_node_destroy(parser, node); } else { nodes->nodes[write_index++] = node; diff --git a/test/prism/fixtures/heredoc_dedent_line_continuation.txt b/test/prism/fixtures/heredoc_dedent_line_continuation.txt new file mode 100644 index 00000000000000..661db490c766e1 --- /dev/null +++ b/test/prism/fixtures/heredoc_dedent_line_continuation.txt @@ -0,0 +1,5 @@ +<<~FOO + foo\ + \ + bar +FOO diff --git a/test/prism/ruby/ruby_parser_test.rb b/test/prism/ruby/ruby_parser_test.rb index 25107978f3828c..bc89bdae72b9bc 100644 --- a/test/prism/ruby/ruby_parser_test.rb +++ b/test/prism/ruby/ruby_parser_test.rb @@ -37,6 +37,7 @@ class RubyParserTest < TestCase "alias.txt", "dsym_str.txt", "dos_endings.txt", + "heredoc_dedent_line_continuation.txt", "heredoc_percent_q_newline_delimiter.txt", "heredocs_with_fake_newlines.txt", "heredocs_with_ignored_newlines.txt",