diff --git a/prism/diagnostic.c b/prism/diagnostic.c index fd5dce5a04..c779955eb3 100644 --- a/prism/diagnostic.c +++ b/prism/diagnostic.c @@ -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_FORWARDING_ELLIPSES] = "unexpected argument after `...`", [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_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", diff --git a/prism/diagnostic.h b/prism/diagnostic.h index 08a3284abf..da430b5438 100644 --- a/prism/diagnostic.h +++ b/prism/diagnostic.h @@ -47,6 +47,7 @@ typedef enum { PM_ERR_ARGUMENT_AFTER_BLOCK, PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES, PM_ERR_ARGUMENT_BARE_HASH, + PM_ERR_ARGUMENT_BLOCK_FORWARDING, PM_ERR_ARGUMENT_BLOCK_MULTI, PM_ERR_ARGUMENT_FORMAL_CLASS, PM_ERR_ARGUMENT_FORMAL_CONSTANT, diff --git a/prism/prism.c b/prism/prism.c index 03df698449..29505a9646 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -813,6 +813,9 @@ typedef struct { /** The optional block attached to the call. */ pm_node_t *block; + + /** The flag indicating whether this arguments list has forwarding argument. */ + bool has_forwarding; } 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); parse_arguments_append(parser, arguments, argument); + arguments->has_forwarding = true; parsed_forwarding_arguments = true; break; } @@ -12170,14 +12174,20 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accept } if (block != NULL) { - if (arguments->block == NULL) { + if (arguments->block == NULL && !arguments->has_forwarding) { arguments->block = (pm_node_t *) block; } else { - pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_BLOCK_MULTI); - if (arguments->arguments == NULL) { - arguments->arguments = pm_arguments_node_create(parser); + if (arguments->has_forwarding) { + pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_BLOCK_FORWARDING); + } else { + pm_parser_err_node(parser, (pm_node_t *) block, PM_ERR_ARGUMENT_BLOCK_MULTI); + } + if (arguments->block != NULL) { + if (arguments->arguments == NULL) { + 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; } } diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index 57c0719aa7..7648875691 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -2046,6 +2046,20 @@ module Prism ] 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 def assert_errors(expected, source, errors, compare_ripper: RUBY_ENGINE == "ruby")