[ruby/prism] Fix to check multiple block arguments for forwarding arg

Fix https://github.com/ruby/prism/pull/2111

21ca243d0a
This commit is contained in:
TSUYUSATO Kitsune 2023-12-27 12:05:04 +09:00 committed by git
parent 165deec5fe
commit 0ee625ceae
4 changed files with 31 additions and 5 deletions

View file

@ -56,6 +56,7 @@ static const char* const diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
[PM_ERR_ARGUMENT_AFTER_BLOCK] = "unexpected argument after a block argument", [PM_ERR_ARGUMENT_AFTER_BLOCK] = "unexpected argument after a block argument",
[PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES] = "unexpected argument after `...`", [PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES] = "unexpected argument after `...`",
[PM_ERR_ARGUMENT_BARE_HASH] = "unexpected bare hash argument", [PM_ERR_ARGUMENT_BARE_HASH] = "unexpected bare hash argument",
[PM_ERR_ARGUMENT_BLOCK_FORWARDING] = "both a block argument and a forwarding argument; only one block is allowed",
[PM_ERR_ARGUMENT_BLOCK_MULTI] = "multiple block arguments; only one block is allowed", [PM_ERR_ARGUMENT_BLOCK_MULTI] = "multiple block arguments; only one block is allowed",
[PM_ERR_ARGUMENT_FORMAL_CLASS] = "invalid formal argument; formal argument cannot be a class variable", [PM_ERR_ARGUMENT_FORMAL_CLASS] = "invalid formal argument; formal argument cannot be a class variable",
[PM_ERR_ARGUMENT_FORMAL_CONSTANT] = "invalid formal argument; formal argument cannot be a constant", [PM_ERR_ARGUMENT_FORMAL_CONSTANT] = "invalid formal argument; formal argument cannot be a constant",

View file

@ -47,6 +47,7 @@ typedef enum {
PM_ERR_ARGUMENT_AFTER_BLOCK, PM_ERR_ARGUMENT_AFTER_BLOCK,
PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES, PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES,
PM_ERR_ARGUMENT_BARE_HASH, PM_ERR_ARGUMENT_BARE_HASH,
PM_ERR_ARGUMENT_BLOCK_FORWARDING,
PM_ERR_ARGUMENT_BLOCK_MULTI, PM_ERR_ARGUMENT_BLOCK_MULTI,
PM_ERR_ARGUMENT_FORMAL_CLASS, PM_ERR_ARGUMENT_FORMAL_CLASS,
PM_ERR_ARGUMENT_FORMAL_CONSTANT, PM_ERR_ARGUMENT_FORMAL_CONSTANT,

View file

@ -813,6 +813,9 @@ typedef struct {
/** The optional block attached to the call. */ /** The optional block attached to the call. */
pm_node_t *block; pm_node_t *block;
/** The flag indicating whether this arguments list has forwarding argument. */
bool has_forwarding;
} pm_arguments_t; } pm_arguments_t;
/** /**
@ -11306,6 +11309,7 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
argument = (pm_node_t *) pm_forwarding_arguments_node_create(parser, &parser->previous); argument = (pm_node_t *) pm_forwarding_arguments_node_create(parser, &parser->previous);
parse_arguments_append(parser, arguments, argument); parse_arguments_append(parser, arguments, argument);
arguments->has_forwarding = true;
parsed_forwarding_arguments = true; parsed_forwarding_arguments = true;
break; break;
} }
@ -12170,14 +12174,20 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept
} }
if (block != NULL) { if (block != NULL) {
if (arguments->block == NULL) { if (arguments->block == NULL && !arguments->has_forwarding) {
arguments->block = (pm_node_t *) block; arguments->block = (pm_node_t *) block;
} else {
if (arguments->has_forwarding) {
pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_BLOCK_FORWARDING);
} else { } else {
pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_BLOCK_MULTI); pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_BLOCK_MULTI);
}
if (arguments->block != NULL) {
if (arguments->arguments == NULL) { if (arguments->arguments == NULL) {
arguments->arguments = pm_arguments_node_create(parser); arguments->arguments = pm_arguments_node_create(parser);
} }
pm_arguments_node_arguments_append(arguments->arguments, arguments->block); pm_arguments_node_arguments_append(arguments->arguments, arguments->block);
}
arguments->block = (pm_node_t *) block; arguments->block = (pm_node_t *) block;
} }
} }

View file

@ -2046,6 +2046,20 @@ module Prism
] ]
end end
def test_block_arg_and_block
source = 'foo(&1) { }'
assert_errors expression(source), source, [
['multiple block arguments; only one block is allowed', 8..11]
], compare_ripper: false # Ripper does not check 'both block arg and actual block given'.
end
def test_forwarding_arg_and_block
source = 'def foo(...) = foo(...) { }'
assert_errors expression(source), source, [
['both a block argument and a forwarding argument; only one block is allowed', 24..27]
], compare_ripper: false # Ripper does not check 'both block arg and actual block given'.
end
private private
def assert_errors(expected, source, errors, compare_ripper: RUBY_ENGINE == "ruby") def assert_errors(expected, source, errors, compare_ripper: RUBY_ENGINE == "ruby")