From fdde2cef5f74b28dbe307db4fe485beb6d3bddfe Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 30 Sep 2024 13:16:18 -0400 Subject: [PATCH] Check command calls in parse_expression --- config.yml | 1 - src/prism.c | 26 +++++++++++++++++++++++--- templates/src/diagnostic.c.erb | 1 - test/prism/errors/command_call_in.txt | 6 ++---- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/config.yml b/config.yml index b5de4d2b4af..972f1e8e69a 100644 --- a/config.yml +++ b/config.yml @@ -15,7 +15,6 @@ errors: - ARGUMENT_FORMAL_GLOBAL - ARGUMENT_FORMAL_IVAR - ARGUMENT_FORWARDING_UNBOUND - - ARGUMENT_IN - ARGUMENT_NO_FORWARDING_AMPERSAND - ARGUMENT_NO_FORWARDING_ELLIPSES - ARGUMENT_NO_FORWARDING_STAR diff --git a/src/prism.c b/src/prism.c index 3c6863ab1bf..043fa13bf1f 100644 --- a/src/prism.c +++ b/src/prism.c @@ -14271,9 +14271,6 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for pm_static_literals_free(&hash_keys); parsed_bare_hash = true; - } else if (accept1(parser, PM_TOKEN_KEYWORD_IN)) { - // TODO: Could we solve this with binding powers instead? - pm_parser_err_current(parser, PM_ERR_ARGUMENT_IN); } parse_arguments_append(parser, arguments, argument); @@ -21573,6 +21570,19 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t #undef PM_PARSE_PATTERN_TOP #undef PM_PARSE_PATTERN_MULTI +/** + * Determine if a given call node looks like a "command", which means it has + * arguments but does not have parentheses. + */ +static inline bool +pm_call_node_command_p(const pm_call_node_t *node) { + return ( + (node->opening_loc.start == NULL) && + (node->block == NULL || PM_NODE_TYPE_P(node->block, PM_BLOCK_ARGUMENT_NODE)) && + (node->arguments != NULL || node->block != NULL) + ); +} + /** * Parse an expression at the given point of the parser using the given binding * power to parse subsequent chains. If this function finds a syntax error, it @@ -21607,6 +21617,16 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc return node; } break; + case PM_CALL_NODE: + // If we have a call node, then we need to check if it looks like a + // method call without parentheses that contains arguments. If it + // does, then it has different rules for parsing infix operators, + // namely that it only accepts composition (and/or) and modifiers + // (if/unless/etc.). + if ((pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_COMPOSITION) && pm_call_node_command_p((pm_call_node_t *) node)) { + return node; + } + break; default: break; } diff --git a/templates/src/diagnostic.c.erb b/templates/src/diagnostic.c.erb index b2381db57fa..d2b7b4f691e 100644 --- a/templates/src/diagnostic.c.erb +++ b/templates/src/diagnostic.c.erb @@ -100,7 +100,6 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_ARGUMENT_FORMAL_GLOBAL] = { "invalid formal argument; formal argument cannot be a global variable", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_FORMAL_IVAR] = { "invalid formal argument; formal argument cannot be an instance variable", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_FORWARDING_UNBOUND] = { "unexpected `...` in an non-parenthesized call", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_ARGUMENT_IN] = { "unexpected `in` keyword in arguments", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_NO_FORWARDING_AMPERSAND] = { "unexpected `&`; no anonymous block parameter", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES] = { "unexpected ... when the parent method is not forwarding", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_ARGUMENT_NO_FORWARDING_STAR] = { "unexpected `*`; no anonymous rest parameter", PM_ERROR_LEVEL_SYNTAX }, diff --git a/test/prism/errors/command_call_in.txt b/test/prism/errors/command_call_in.txt index a4357028c67..2fdcf097389 100644 --- a/test/prism/errors/command_call_in.txt +++ b/test/prism/errors/command_call_in.txt @@ -1,7 +1,5 @@ foo 1 in a - ^ unexpected `in` keyword in arguments - ^ unexpected local variable or method, expecting end-of-input + ^~ unexpected 'in', expecting end-of-input + ^~ unexpected 'in', ignoring it a = foo 2 in b - ^ unexpected `in` keyword in arguments - ^ unexpected local variable or method, expecting end-of-input