diff --git a/src/prism.c b/src/prism.c index 7e5d3e48b63..aec1d35e552 100644 --- a/src/prism.c +++ b/src/prism.c @@ -12443,7 +12443,8 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power) { bool parsed_bare_hash = false; while (!match2(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_EOF)) { - // Handle the case where we don't have a comma and we have a newline followed by a right bracket. + // Handle the case where we don't have a comma and we have a + // newline followed by a right bracket. if (accept1(parser, PM_TOKEN_NEWLINE) && match1(parser, PM_TOKEN_BRACKET_RIGHT)) { break; } @@ -12452,16 +12453,25 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power) { expect1(parser, PM_TOKEN_COMMA, PM_ERR_ARRAY_SEPARATOR); } - // If we have a right bracket immediately following a comma, this is - // allowed since it's a trailing comma. In this case we can break out of - // the loop. + // If we have a right bracket immediately following a comma, + // this is allowed since it's a trailing comma. In this case we + // can break out of the loop. if (match1(parser, PM_TOKEN_BRACKET_RIGHT)) break; pm_node_t *element; if (accept1(parser, PM_TOKEN_USTAR)) { pm_token_t operator = parser->previous; - pm_node_t *expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_ARRAY_EXPRESSION_AFTER_STAR); + pm_node_t *expression = NULL; + + if (match3(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_EOF)) { + if (pm_parser_local_depth(parser, &parser->previous) == -1) { + pm_parser_err_token(parser, &operator, PM_ERR_ARGUMENT_NO_FORWARDING_STAR); + } + } else { + expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, PM_ERR_ARRAY_EXPRESSION_AFTER_STAR); + } + element = (pm_node_t *) pm_splat_node_create(parser, &operator, expression); } else if (match2(parser, PM_TOKEN_LABEL, PM_TOKEN_USTAR_STAR)) { if (parsed_bare_hash) { diff --git a/test/prism/fixtures/methods.txt b/test/prism/fixtures/methods.txt index ac561363f89..8cdf3ddaee3 100644 --- a/test/prism/fixtures/methods.txt +++ b/test/prism/fixtures/methods.txt @@ -166,3 +166,5 @@ end foo = 1 def foo.bar; end + +def f(*); [*]; end diff --git a/test/prism/snapshots/methods.txt b/test/prism/snapshots/methods.txt index c4f2a0853cb..9b4d9116ec3 100644 --- a/test/prism/snapshots/methods.txt +++ b/test/prism/snapshots/methods.txt @@ -1,8 +1,8 @@ -@ ProgramNode (location: (1,0)-(168,16)) +@ ProgramNode (location: (1,0)-(170,18)) ├── locals: [:a, :c, :foo] └── statements: - @ StatementsNode (location: (1,0)-(168,16)) - └── body: (length: 62) + @ StatementsNode (location: (1,0)-(170,18)) + └── body: (length: 63) ├── @ DefNode (location: (1,0)-(2,3)) │ ├── name: :foo │ ├── name_loc: (1,4)-(1,7) = "foo" @@ -1637,19 +1637,53 @@ │ │ @ IntegerNode (location: (167,6)-(167,7)) │ │ └── flags: decimal │ └── operator_loc: (167,4)-(167,5) = "=" - └── @ DefNode (location: (168,0)-(168,16)) - ├── name: :bar - ├── name_loc: (168,8)-(168,11) = "bar" - ├── receiver: - │ @ LocalVariableReadNode (location: (168,4)-(168,7)) - │ ├── name: :foo - │ └── depth: 0 - ├── parameters: ∅ - ├── body: ∅ - ├── locals: [] - ├── def_keyword_loc: (168,0)-(168,3) = "def" - ├── operator_loc: (168,7)-(168,8) = "." - ├── lparen_loc: ∅ - ├── rparen_loc: ∅ + ├── @ DefNode (location: (168,0)-(168,16)) + │ ├── name: :bar + │ ├── name_loc: (168,8)-(168,11) = "bar" + │ ├── receiver: + │ │ @ LocalVariableReadNode (location: (168,4)-(168,7)) + │ │ ├── name: :foo + │ │ └── depth: 0 + │ ├── parameters: ∅ + │ ├── body: ∅ + │ ├── locals: [] + │ ├── def_keyword_loc: (168,0)-(168,3) = "def" + │ ├── operator_loc: (168,7)-(168,8) = "." + │ ├── lparen_loc: ∅ + │ ├── rparen_loc: ∅ + │ ├── equal_loc: ∅ + │ └── end_keyword_loc: (168,13)-(168,16) = "end" + └── @ DefNode (location: (170,0)-(170,18)) + ├── name: :f + ├── name_loc: (170,4)-(170,5) = "f" + ├── receiver: ∅ + ├── parameters: + │ @ ParametersNode (location: (170,6)-(170,7)) + │ ├── requireds: (length: 0) + │ ├── optionals: (length: 0) + │ ├── rest: + │ │ @ RestParameterNode (location: (170,6)-(170,7)) + │ │ ├── name: ∅ + │ │ ├── name_loc: ∅ + │ │ └── operator_loc: (170,6)-(170,7) = "*" + │ ├── posts: (length: 0) + │ ├── keywords: (length: 0) + │ ├── keyword_rest: ∅ + │ └── block: ∅ + ├── body: + │ @ StatementsNode (location: (170,10)-(170,13)) + │ └── body: (length: 1) + │ └── @ ArrayNode (location: (170,10)-(170,13)) + │ ├── elements: (length: 1) + │ │ └── @ SplatNode (location: (170,11)-(170,12)) + │ │ ├── operator_loc: (170,11)-(170,12) = "*" + │ │ └── expression: ∅ + │ ├── opening_loc: (170,10)-(170,11) = "[" + │ └── closing_loc: (170,12)-(170,13) = "]" + ├── locals: [:*] + ├── def_keyword_loc: (170,0)-(170,3) = "def" + ├── operator_loc: ∅ + ├── lparen_loc: (170,5)-(170,6) = "(" + ├── rparen_loc: (170,7)-(170,8) = ")" ├── equal_loc: ∅ - └── end_keyword_loc: (168,13)-(168,16) = "end" + └── end_keyword_loc: (170,15)-(170,18) = "end"