Skip to content

Commit 731b609

Browse files
Earlopainmatzbot
authored andcommitted
[ruby/prism] [Feature #19107] Allow trailing comma in method signature
ruby/prism@b7e247ce6a
1 parent 6d69a2b commit 731b609

7 files changed

Lines changed: 69 additions & 15 deletions

prism/prism.c

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13907,6 +13907,43 @@ update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_ord
1390713907
return true;
1390813908
}
1390913909

13910+
static inline void
13911+
parse_parameters_handle_trailing_comma(
13912+
pm_parser_t *parser,
13913+
pm_parameters_node_t *params,
13914+
pm_parameters_order_t order,
13915+
bool in_block,
13916+
bool allows_trailing_comma
13917+
) {
13918+
if (!allows_trailing_comma) {
13919+
pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
13920+
return;
13921+
}
13922+
13923+
if (in_block) {
13924+
if (order >= PM_PARAMETERS_ORDER_NAMED) {
13925+
// foo do |bar,|; end
13926+
pm_node_t *param = UP(pm_implicit_rest_node_create(parser, &parser->previous));
13927+
13928+
if (params->rest == NULL) {
13929+
pm_parameters_node_rest_set(params, param);
13930+
} else {
13931+
pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_SPLAT_MULTI);
13932+
pm_parameters_node_posts_append(params, UP(param));
13933+
}
13934+
} else {
13935+
// foo do |*bar,|; end
13936+
pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
13937+
}
13938+
} else {
13939+
// https://bugs.ruby-lang.org/issues/19107
13940+
// Allow `def foo(bar,); end`, `def foo(*bar,); end`, etc. but not `def foo(...,); end`
13941+
if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1 || order == PM_PARAMETERS_ORDER_NOTHING_AFTER) {
13942+
pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
13943+
}
13944+
}
13945+
}
13946+
1391013947
/**
1391113948
* Parse a list of parameters on a method definition.
1391213949
*/
@@ -14255,20 +14292,7 @@ parse_parameters(
1425514292
}
1425614293
default:
1425714294
if (parser->previous.type == PM_TOKEN_COMMA) {
14258-
if (allows_trailing_comma && order >= PM_PARAMETERS_ORDER_NAMED) {
14259-
// If we get here, then we have a trailing comma in a
14260-
// block parameter list.
14261-
pm_node_t *param = UP(pm_implicit_rest_node_create(parser, &parser->previous));
14262-
14263-
if (params->rest == NULL) {
14264-
pm_parameters_node_rest_set(params, param);
14265-
} else {
14266-
pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_SPLAT_MULTI);
14267-
pm_parameters_node_posts_append(params, UP(param));
14268-
}
14269-
} else {
14270-
pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14271-
}
14295+
parse_parameters_handle_trailing_comma(parser, params, order, in_block, allows_trailing_comma);
1427214296
}
1427314297

1427414298
parsing = false;
@@ -18865,7 +18889,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1886518889
if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
1886618890
params = NULL;
1886718891
} else {
18868-
params = parse_parameters(parser, PM_BINDING_POWER_DEFINED, true, false, true, true, false, (uint16_t) (depth + 1));
18892+
// https://bugs.ruby-lang.org/issues/19107
18893+
bool allow_trailing_comma = parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1;
18894+
params = parse_parameters(parser, PM_BINDING_POWER_DEFINED, true, allow_trailing_comma, true, true, false, (uint16_t) (depth + 1));
1886918895
}
1887018896

1887118897
lex_state_set(parser, PM_LEX_STATE_BEG);

test/prism/errors/do_not_allow_trailing_commas_in_method_parameters.txt renamed to test/prism/errors/3.3-4.0/do_not_allow_trailing_commas_in_method_parameters.txt

File renamed without changes.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
def foo(a,b,...,);end
2+
^ unexpected `,` in parameters
3+
4+
def foo(a,b,&block,);end
5+
^ unexpected `,` in parameters
6+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
def foo(a,b,c,);end
2+
3+
def foo(a,b,*c,);end
4+
5+
def foo(a,b,*,);end
6+
7+
def foo(a,b,**c,);end
8+
9+
def foo(a,b,**,);end
10+
11+
def foo(
12+
a,
13+
b,
14+
c,
15+
);end

test/prism/fixtures_test.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class FixturesTest < TestCase
2828
except << "command_method_call_2.txt"
2929
# https://bugs.ruby-lang.org/issues/21669
3030
except << "4.1/void_value.txt"
31+
# https://bugs.ruby-lang.org/issues/19107
32+
except << "4.1/trailing_comma_after_method_arguments.txt"
3133

3234
Fixture.each_for_current_ruby(except: except) do |fixture|
3335
define_method(fixture.test_name) { assert_valid_syntax(fixture.read) }

test/prism/locals_test.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ class LocalsTest < TestCase
3131

3232
# https://bugs.ruby-lang.org/issues/21669
3333
"4.1/void_value.txt",
34+
35+
# https://bugs.ruby-lang.org/issues/19107
36+
"4.1/trailing_comma_after_method_arguments.txt",
3437
]
3538

3639
Fixture.each_for_current_ruby(except: except) do |fixture|

test/prism/ruby/ripper_test.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ class RipperTest < TestCase
3939

4040
# https://bugs.ruby-lang.org/issues/21669
4141
incorrect << "4.1/void_value.txt"
42+
# https://bugs.ruby-lang.org/issues/19107
43+
incorrect << "4.1/trailing_comma_after_method_arguments.txt"
4244

4345
# Skip these tests that we haven't implemented yet.
4446
omitted_sexp_raw = [

0 commit comments

Comments
 (0)