From caa9ae780430a67c1ed3cb5f15b0e023452d76e4 Mon Sep 17 00:00:00 2001 From: Hiroya Fujinami Date: Tue, 21 Nov 2023 00:52:40 +0900 Subject: [PATCH] [ruby/prism] Fix parsing `...` in arguments (https://github.com/ruby/prism/pull/1882) * Fix parsing `...` in arguments Fix https://github.com/ruby/prism/pull/1830 Fix https://github.com/ruby/prism/pull/1831 * Rename the constant name to PM_ERR_ARGUMENT_FORWARDING_UNBOUND https://github.com/ruby/prism/pull/1882#discussion_r1398461156 https://github.com/ruby/prism/commit/519653aec2 --- prism/diagnostic.c | 2 ++ prism/diagnostic.h | 2 ++ prism/prism.c | 11 +++++++++++ test/prism/errors_test.rb | 14 ++++++++++++++ 4 files changed, 29 insertions(+) diff --git a/prism/diagnostic.c b/prism/diagnostic.c index 2c59e4effc..c1d930a430 100644 --- a/prism/diagnostic.c +++ b/prism/diagnostic.c @@ -54,12 +54,14 @@ static const char* const diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = { [PM_ERR_ALIAS_ARGUMENT] = "Invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable", [PM_ERR_AMPAMPEQ_MULTI_ASSIGN] = "Unexpected `&&=` in a multiple assignment", [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_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", [PM_ERR_ARGUMENT_FORMAL_GLOBAL] = "Invalid formal argument; formal argument cannot be a global variable", [PM_ERR_ARGUMENT_FORMAL_IVAR] = "Invalid formal argument; formal argument cannot be an instance variable", + [PM_ERR_ARGUMENT_FORWARDING_UNBOUND] = "Unexpected `...` in an non-parenthesized call", [PM_ERR_ARGUMENT_NO_FORWARDING_AMP] = "Unexpected `&` when the parent method is not forwarding", [PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES] = "Unexpected `...` when the parent method is not forwarding", [PM_ERR_ARGUMENT_NO_FORWARDING_STAR] = "Unexpected `*` when the parent method is not forwarding", diff --git a/prism/diagnostic.h b/prism/diagnostic.h index 10b547ae20..c963289cbf 100644 --- a/prism/diagnostic.h +++ b/prism/diagnostic.h @@ -40,12 +40,14 @@ typedef enum { PM_ERR_ALIAS_ARGUMENT, PM_ERR_AMPAMPEQ_MULTI_ASSIGN, PM_ERR_ARGUMENT_AFTER_BLOCK, + PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES, PM_ERR_ARGUMENT_BARE_HASH, PM_ERR_ARGUMENT_BLOCK_MULTI, PM_ERR_ARGUMENT_FORMAL_CLASS, PM_ERR_ARGUMENT_FORMAL_CONSTANT, PM_ERR_ARGUMENT_FORMAL_GLOBAL, PM_ERR_ARGUMENT_FORMAL_IVAR, + PM_ERR_ARGUMENT_FORWARDING_UNBOUND, PM_ERR_ARGUMENT_NO_FORWARDING_AMP, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES, PM_ERR_ARGUMENT_NO_FORWARDING_STAR, diff --git a/prism/prism.c b/prism/prism.c index 86d9fce00f..83f5a33bea 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -11039,13 +11039,18 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for return; } + bool parsed_first_argument = false; bool parsed_bare_hash = false; bool parsed_block_argument = false; + bool parsed_forwarding_arguments = false; while (!match1(parser, PM_TOKEN_EOF)) { if (parsed_block_argument) { pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_BLOCK); } + if (parsed_forwarding_arguments) { + pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES); + } pm_node_t *argument = NULL; @@ -11129,9 +11134,13 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for if (pm_parser_local_depth(parser, &parser->previous) == -1) { pm_parser_err_previous(parser, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES); } + if (parsed_first_argument && terminator == PM_TOKEN_EOF) { + pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORWARDING_UNBOUND); + } argument = (pm_node_t *) pm_forwarding_arguments_node_create(parser, &parser->previous); parse_arguments_append(parser, arguments, argument); + parsed_forwarding_arguments = true; break; } } @@ -11183,6 +11192,8 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for } } + parsed_first_argument = true; + // If parsing the argument failed, we need to stop parsing arguments. if (PM_NODE_TYPE_P(argument, PM_MISSING_NODE) || parser->recovering) break; diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index f13327c198..11036b1f81 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -1682,6 +1682,20 @@ module Prism ] end + def test_argument_after_ellipsis + source = 'def foo(...); foo(..., 1); end' + assert_errors expression(source), source, [ + ['Unexpected argument after `...`', 23..24] + ] + end + + def test_ellipsis_in_no_paren_call + source = 'def foo(...); foo 1, ...; end' + assert_errors expression(source), source, [ + ['Unexpected `...` in an non-parenthesized call', 21..24] + ] + end + private def assert_errors(expected, source, errors, compare_ripper: RUBY_ENGINE == "ruby")