[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
This commit is contained in:
Hiroya Fujinami 2023-11-21 00:52:40 +09:00 committed by git
parent 83da4a7e62
commit caa9ae7804
4 changed files with 29 additions and 0 deletions

View File

@ -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",

View File

@ -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,

View File

@ -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;

View File

@ -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")