@@ -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+
1695016960static pm_node_t *
1695116961parse_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 */
1695816968static 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 */
1709317106static 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 */
1712717140static 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 */
1719017203static 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 */
1723617249static 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 */
1735317366static 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
1762917642static pm_node_t *
1763017643parse_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,
0 commit comments