Skip to content

Commit 8956f1d

Browse files
authored
Merge pull request #3722 from ruby/reverse-sync
Reverse sync from upstream
2 parents 63084bd + 87dffcf commit 8956f1d

4 files changed

Lines changed: 53 additions & 27 deletions

File tree

config.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,10 @@ errors:
217217
- PARAMETER_UNEXPECTED_FWD
218218
- PARAMETER_UNEXPECTED_NO_KW
219219
- PARAMETER_WILD_LOOSE_COMMA
220+
- PATTERN_ALTERNATIVE_AFTER_CAPTURE
220221
- PATTERN_ARRAY_MULTIPLE_RESTS
221222
- PATTERN_CAPTURE_DUPLICATE
223+
- PATTERN_CAPTURE_IN_ALTERNATIVE
222224
- PATTERN_EXPRESSION_AFTER_BRACKET
223225
- PATTERN_EXPRESSION_AFTER_COMMA
224226
- PATTERN_EXPRESSION_AFTER_HROCKET

src/prism.c

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16947,6 +16947,16 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1
1694716947
#define PM_PARSE_PATTERN_TOP 1
1694816948
#define PM_PARSE_PATTERN_MULTI 2
1694916949

16950+
/** Information used to track the state of captures in patterns. */
16951+
typedef struct {
16952+
/** Whether we're currently parsing an alternative pattern. This is used to
16953+
* disallow captures in alternative patterns. */
16954+
bool in_alternative_pattern;
16955+
/** Whether we've seen a capture in this pattern. This is used to disallow
16956+
* captures in alternative patterns. */
16957+
bool capture_in_pattern;
16958+
} pm_pattern_capturing_t;
16959+
1695016960
static pm_node_t *
1695116961
parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth);
1695216962

@@ -16956,13 +16966,16 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag
1695616966
* an error to the parser.
1695716967
*/
1695816968
static void
16959-
parse_pattern_capture(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_constant_id_t capture, const pm_location_t *location) {
16969+
parse_pattern_capture(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_constant_id_t capture, const pm_location_t *location, pm_pattern_capturing_t *capturing) {
1696016970
// Skip this capture if it starts with an underscore.
1696116971
if (*location->start == '_') return;
1696216972

1696316973
if (pm_constant_id_list_includes(captures, capture)) {
1696416974
pm_parser_err(parser, location->start, location->end, PM_ERR_PATTERN_CAPTURE_DUPLICATE);
16975+
} else if (capturing->in_alternative_pattern && parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5) {
16976+
pm_parser_err(parser, location->start, location->end, PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE);
1696516977
} else {
16978+
capturing->capture_in_pattern = true;
1696616979
pm_constant_id_list_append(captures, capture);
1696716980
}
1696816981
}
@@ -17091,7 +17104,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures
1709117104
* Parse a rest pattern.
1709217105
*/
1709317106
static pm_splat_node_t *
17094-
parse_pattern_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) {
17107+
parse_pattern_rest(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_pattern_capturing_t *capturing) {
1709517108
assert(parser->previous.type == PM_TOKEN_USTAR);
1709617109
pm_token_t operator = parser->previous;
1709717110
pm_node_t *name = NULL;
@@ -17108,7 +17121,7 @@ parse_pattern_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) {
1710817121
pm_parser_local_add(parser, constant_id, identifier.start, identifier.end, 0);
1710917122
}
1711017123

17111-
parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&identifier));
17124+
parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&identifier), capturing);
1711217125
name = (pm_node_t *) pm_local_variable_target_node_create(
1711317126
parser,
1711417127
&PM_LOCATION_TOKEN_VALUE(&identifier),
@@ -17125,7 +17138,7 @@ parse_pattern_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) {
1712517138
* Parse a keyword rest node.
1712617139
*/
1712717140
static pm_node_t *
17128-
parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) {
17141+
parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_pattern_capturing_t *capturing) {
1712917142
assert(parser->current.type == PM_TOKEN_USTAR_STAR);
1713017143
parser_lex(parser);
1713117144

@@ -17144,7 +17157,7 @@ parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures)
1714417157
pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
1714517158
}
1714617159

17147-
parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous));
17160+
parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous), capturing);
1714817161
value = (pm_node_t *) pm_local_variable_target_node_create(
1714917162
parser,
1715017163
&PM_LOCATION_TOKEN_VALUE(&parser->previous),
@@ -17188,7 +17201,7 @@ pm_slice_is_valid_local(const pm_parser_t *parser, const uint8_t *start, const u
1718817201
* value. This will use an implicit local variable target.
1718917202
*/
1719017203
static pm_node_t *
17191-
parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_symbol_node_t *key) {
17204+
parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_symbol_node_t *key, pm_pattern_capturing_t *capturing) {
1719217205
const pm_location_t *value_loc = &((pm_symbol_node_t *) key)->value_loc;
1719317206

1719417207
pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, value_loc->start, value_loc->end);
@@ -17208,7 +17221,7 @@ parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *ca
1720817221
pm_parser_local_add(parser, constant_id, value_loc->start, value_loc->end, 0);
1720917222
}
1721017223

17211-
parse_pattern_capture(parser, captures, constant_id, value_loc);
17224+
parse_pattern_capture(parser, captures, constant_id, value_loc, capturing);
1721217225
pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
1721317226
parser,
1721417227
value_loc,
@@ -17234,7 +17247,7 @@ parse_pattern_hash_key(pm_parser_t *parser, pm_static_literals_t *keys, pm_node_
1723417247
* Parse a hash pattern.
1723517248
*/
1723617249
static pm_hash_pattern_node_t *
17237-
parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *first_node, uint16_t depth) {
17250+
parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *first_node, pm_pattern_capturing_t *capturing, uint16_t depth) {
1723817251
pm_node_list_t assocs = { 0 };
1723917252
pm_static_literals_t keys = { 0 };
1724017253
pm_node_t *rest = NULL;
@@ -17252,7 +17265,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node
1725217265
if (match8(parser, PM_TOKEN_COMMA, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF)) {
1725317266
// Otherwise, we will create an implicit local variable
1725417267
// target for the value.
17255-
value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) first_node);
17268+
value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) first_node, capturing);
1725617269
} else {
1725717270
// Here we have a value for the first assoc in the list, so
1725817271
// we will parse it now.
@@ -17296,7 +17309,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node
1729617309
}
1729717310

1729817311
if (match1(parser, PM_TOKEN_USTAR_STAR)) {
17299-
pm_node_t *assoc = parse_pattern_keyword_rest(parser, captures);
17312+
pm_node_t *assoc = parse_pattern_keyword_rest(parser, captures, capturing);
1730017313

1730117314
if (rest == NULL) {
1730217315
rest = assoc;
@@ -17324,7 +17337,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node
1732417337
pm_node_t *value = NULL;
1732517338

1732617339
if (match7(parser, PM_TOKEN_COMMA, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
17327-
value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key);
17340+
value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key, capturing);
1732817341
} else {
1732917342
value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1));
1733017343
}
@@ -17351,7 +17364,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node
1735117364
* Parse a pattern expression primitive.
1735217365
*/
1735317366
static pm_node_t *
17354-
parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_diagnostic_id_t diag_id, uint16_t depth) {
17367+
parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_pattern_capturing_t *capturing, pm_diagnostic_id_t diag_id, uint16_t depth) {
1735517368
switch (parser->current.type) {
1735617369
case PM_TOKEN_IDENTIFIER:
1735717370
case PM_TOKEN_METHOD_NAME: {
@@ -17363,7 +17376,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm
1736317376
pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
1736417377
}
1736517378

17366-
parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous));
17379+
parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous), capturing);
1736717380
return (pm_node_t *) pm_local_variable_target_node_create(
1736817381
parser,
1736917382
&PM_LOCATION_TOKEN_VALUE(&parser->previous),
@@ -17447,7 +17460,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm
1744717460
first_node = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous);
1744817461
break;
1744917462
case PM_TOKEN_USTAR_STAR:
17450-
first_node = parse_pattern_keyword_rest(parser, captures);
17463+
first_node = parse_pattern_keyword_rest(parser, captures, capturing);
1745117464
break;
1745217465
case PM_TOKEN_STRING_BEGIN:
1745317466
first_node = parse_expression(parser, PM_BINDING_POWER_MAX, false, true, PM_ERR_PATTERN_HASH_KEY_LABEL, (uint16_t) (depth + 1));
@@ -17461,7 +17474,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm
1746117474
}
1746217475
}
1746317476

17464-
node = parse_pattern_hash(parser, captures, first_node, (uint16_t) (depth + 1));
17477+
node = parse_pattern_hash(parser, captures, first_node, capturing, (uint16_t) (depth + 1));
1746517478

1746617479
accept1(parser, PM_TOKEN_NEWLINE);
1746717480
expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_PATTERN_TERM_BRACE);
@@ -17629,10 +17642,18 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm
1762917642
static pm_node_t *
1763017643
parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *first_node, pm_diagnostic_id_t diag_id, uint16_t depth) {
1763117644
pm_node_t *node = first_node;
17645+
pm_pattern_capturing_t capturing = { false, false };
1763217646

1763317647
while ((node == NULL) || accept1(parser, PM_TOKEN_PIPE)) {
1763417648
pm_token_t operator = parser->previous;
1763517649

17650+
if (node) {
17651+
if (capturing.capture_in_pattern) {
17652+
pm_parser_err(parser, operator.start, operator.end, PM_ERR_PATTERN_ALTERNATIVE_AFTER_CAPTURE);
17653+
}
17654+
capturing.in_alternative_pattern = true;
17655+
}
17656+
1763617657
switch (parser->current.type) {
1763717658
case PM_TOKEN_IDENTIFIER:
1763817659
case PM_TOKEN_BRACKET_LEFT_ARRAY:
@@ -17644,9 +17665,9 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p
1764417665
case PM_TOKEN_UDOT_DOT_DOT:
1764517666
case PM_CASE_PRIMITIVE: {
1764617667
if (node == NULL) {
17647-
node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
17668+
node = parse_pattern_primitive(parser, captures, &capturing, diag_id, (uint16_t) (depth + 1));
1764817669
} else {
17649-
pm_node_t *right = parse_pattern_primitive(parser, captures, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, (uint16_t) (depth + 1));
17670+
pm_node_t *right = parse_pattern_primitive(parser, captures, &capturing, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, (uint16_t) (depth + 1));
1765017671
node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &operator);
1765117672
}
1765217673

@@ -17698,7 +17719,7 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p
1769817719
pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
1769917720
}
1770017721

17701-
parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous));
17722+
parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous), &capturing);
1770217723
pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
1770317724
parser,
1770417725
&PM_LOCATION_TOKEN_VALUE(&parser->previous),
@@ -17721,12 +17742,13 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag
1772117742

1772217743
bool leading_rest = false;
1772317744
bool trailing_rest = false;
17745+
pm_pattern_capturing_t capturing = { false, false };
1772417746

1772517747
switch (parser->current.type) {
1772617748
case PM_TOKEN_LABEL: {
1772717749
parser_lex(parser);
1772817750
pm_node_t *key = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous);
17729-
node = (pm_node_t *) parse_pattern_hash(parser, captures, key, (uint16_t) (depth + 1));
17751+
node = (pm_node_t *) parse_pattern_hash(parser, captures, key, &capturing, (uint16_t) (depth + 1));
1773017752

1773117753
if (!(flags & PM_PARSE_PATTERN_TOP)) {
1773217754
pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
@@ -17735,8 +17757,8 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag
1773517757
return node;
1773617758
}
1773717759
case PM_TOKEN_USTAR_STAR: {
17738-
node = parse_pattern_keyword_rest(parser, captures);
17739-
node = (pm_node_t *) parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1));
17760+
node = parse_pattern_keyword_rest(parser, captures, &capturing);
17761+
node = (pm_node_t *) parse_pattern_hash(parser, captures, node, &capturing, (uint16_t) (depth + 1));
1774017762

1774117763
if (!(flags & PM_PARSE_PATTERN_TOP)) {
1774217764
pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
@@ -17747,10 +17769,10 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag
1774717769
case PM_TOKEN_STRING_BEGIN: {
1774817770
// We need special handling for string beginnings because they could
1774917771
// be dynamic symbols leading to hash patterns.
17750-
node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
17772+
node = parse_pattern_primitive(parser, captures, &capturing, diag_id, (uint16_t) (depth + 1));
1775117773

1775217774
if (pm_symbol_node_label_p(node)) {
17753-
node = (pm_node_t *) parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1));
17775+
node = (pm_node_t *) parse_pattern_hash(parser, captures, node, &capturing, (uint16_t) (depth + 1));
1775417776

1775517777
if (!(flags & PM_PARSE_PATTERN_TOP)) {
1775617778
pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
@@ -17765,7 +17787,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag
1776517787
case PM_TOKEN_USTAR: {
1776617788
if (flags & (PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI)) {
1776717789
parser_lex(parser);
17768-
node = (pm_node_t *) parse_pattern_rest(parser, captures);
17790+
node = (pm_node_t *) parse_pattern_rest(parser, captures, &capturing);
1776917791
leading_rest = true;
1777017792
break;
1777117793
}
@@ -17779,7 +17801,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag
1777917801
// If we got a dynamic label symbol, then we need to treat it like the
1778017802
// beginning of a hash pattern.
1778117803
if (pm_symbol_node_label_p(node)) {
17782-
return (pm_node_t *) parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1));
17804+
return (pm_node_t *) parse_pattern_hash(parser, captures, node, &capturing, (uint16_t) (depth + 1));
1778317805
}
1778417806

1778517807
if ((flags & PM_PARSE_PATTERN_MULTI) && match1(parser, PM_TOKEN_COMMA)) {
@@ -17800,7 +17822,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag
1780017822
}
1780117823

1780217824
if (accept1(parser, PM_TOKEN_USTAR)) {
17803-
node = (pm_node_t *) parse_pattern_rest(parser, captures);
17825+
node = (pm_node_t *) parse_pattern_rest(parser, captures, &capturing);
1780417826

1780517827
// If we have already parsed a splat pattern, then this is an
1780617828
// error. We will continue to parse the rest of the patterns,

templates/src/diagnostic.c.erb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,10 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
299299
[PM_ERR_PARAMETER_UNEXPECTED_FWD] = { "unexpected `...` in parameters", PM_ERROR_LEVEL_SYNTAX },
300300
[PM_ERR_PARAMETER_WILD_LOOSE_COMMA] = { "unexpected `,` in parameters", PM_ERROR_LEVEL_SYNTAX },
301301
[PM_ERR_PARAMETER_UNEXPECTED_NO_KW] = { "unexpected **nil; no keywords marker disallowed after keywords", PM_ERROR_LEVEL_SYNTAX },
302+
[PM_ERR_PATTERN_ALTERNATIVE_AFTER_CAPTURE] = { "alternative pattern after variable capture", PM_ERROR_LEVEL_SYNTAX },
302303
[PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS] = { "unexpected multiple '*' rest patterns in an array pattern", PM_ERROR_LEVEL_SYNTAX },
303304
[PM_ERR_PATTERN_CAPTURE_DUPLICATE] = { "duplicated variable name", PM_ERROR_LEVEL_SYNTAX },
305+
[PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE] = { "variable capture in alternative pattern", PM_ERROR_LEVEL_SYNTAX },
304306
[PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET] = { "expected a pattern expression after the `[` operator", PM_ERROR_LEVEL_SYNTAX },
305307
[PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA] = { "expected a pattern expression after `,`", PM_ERROR_LEVEL_SYNTAX },
306308
[PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET] = { "expected a pattern expression after `=>`", PM_ERROR_LEVEL_SYNTAX },

test/prism/result/source_location_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def test_AliasMethodNode
1313
end
1414

1515
def test_AlternationPatternNode
16-
assert_location(AlternationPatternNode, "foo => bar | baz", 7...16, &:pattern)
16+
assert_location(AlternationPatternNode, "foo => 0 | 1", 7...12, &:pattern)
1717
end
1818

1919
def test_AndNode

0 commit comments

Comments
 (0)