From c03e113ed423ba3e3eeb68a12dc6689597a9b8bd Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Sun, 16 Nov 2025 16:42:08 -0500 Subject: [PATCH] Revisit variable capture syntax error When we know we are in an alternation, and we know we have captured variables, add a syntax error by visiting the pattern subtree and finding the local variable target nodes and adding an error. --- config.yml | 1 - src/prism.c | 113 ++++++++++-------- templates/src/diagnostic.c.erb | 1 - .../errors/pattern-capture-in-alt-array.txt | 4 + .../errors/pattern-capture-in-alt-hash.txt | 3 + .../errors/pattern-capture-in-alt-name.txt | 3 + .../errors/pattern-capture-in-alt-top.txt | 4 + 7 files changed, 74 insertions(+), 55 deletions(-) create mode 100644 test/prism/errors/pattern-capture-in-alt-array.txt create mode 100644 test/prism/errors/pattern-capture-in-alt-hash.txt create mode 100644 test/prism/errors/pattern-capture-in-alt-name.txt create mode 100644 test/prism/errors/pattern-capture-in-alt-top.txt diff --git a/config.yml b/config.yml index b5d0e9d0b9..e57aa85044 100644 --- a/config.yml +++ b/config.yml @@ -217,7 +217,6 @@ errors: - PARAMETER_UNEXPECTED_FWD - PARAMETER_UNEXPECTED_NO_KW - PARAMETER_WILD_LOOSE_COMMA - - PATTERN_ALTERNATIVE_AFTER_CAPTURE - PATTERN_ARRAY_MULTIPLE_RESTS - PATTERN_CAPTURE_DUPLICATE - PATTERN_CAPTURE_IN_ALTERNATIVE diff --git a/src/prism.c b/src/prism.c index 59d602dadb..a87932f1b7 100644 --- a/src/prism.c +++ b/src/prism.c @@ -16947,16 +16947,6 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1 #define PM_PARSE_PATTERN_TOP 1 #define PM_PARSE_PATTERN_MULTI 2 -/** Information used to track the state of captures in patterns. */ -typedef struct { - /** Whether we're currently parsing an alternative pattern. This is used to - * disallow captures in alternative patterns. */ - bool in_alternative_pattern; - /** Whether we've seen a capture in this pattern. This is used to disallow - * captures in alternative patterns. */ - bool capture_in_pattern; -} pm_pattern_capturing_t; - static pm_node_t * parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth); @@ -16966,16 +16956,13 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag * an error to the parser. */ static void -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) { +parse_pattern_capture(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_constant_id_t capture, const pm_location_t *location) { // Skip this capture if it starts with an underscore. if (*location->start == '_') return; if (pm_constant_id_list_includes(captures, capture)) { pm_parser_err(parser, location->start, location->end, PM_ERR_PATTERN_CAPTURE_DUPLICATE); - } else if (capturing->in_alternative_pattern && parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5) { - pm_parser_err(parser, location->start, location->end, PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE); } else { - capturing->capture_in_pattern = true; pm_constant_id_list_append(captures, capture); } } @@ -17104,7 +17091,7 @@ parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures * Parse a rest pattern. */ static pm_splat_node_t * -parse_pattern_rest(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_pattern_capturing_t *capturing) { +parse_pattern_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) { assert(parser->previous.type == PM_TOKEN_USTAR); pm_token_t operator = parser->previous; pm_node_t *name = NULL; @@ -17121,7 +17108,7 @@ parse_pattern_rest(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_patt pm_parser_local_add(parser, constant_id, identifier.start, identifier.end, 0); } - parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&identifier), capturing); + parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&identifier)); name = (pm_node_t *) pm_local_variable_target_node_create( parser, &PM_LOCATION_TOKEN_VALUE(&identifier), @@ -17138,7 +17125,7 @@ parse_pattern_rest(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_patt * Parse a keyword rest node. */ static pm_node_t * -parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_pattern_capturing_t *capturing) { +parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) { assert(parser->current.type == PM_TOKEN_USTAR_STAR); parser_lex(parser); @@ -17157,7 +17144,7 @@ parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0); } - parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous), capturing); + parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous)); value = (pm_node_t *) pm_local_variable_target_node_create( parser, &PM_LOCATION_TOKEN_VALUE(&parser->previous), @@ -17201,7 +17188,7 @@ pm_slice_is_valid_local(const pm_parser_t *parser, const uint8_t *start, const u * value. This will use an implicit local variable target. */ static pm_node_t * -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) { +parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_symbol_node_t *key) { const pm_location_t *value_loc = &((pm_symbol_node_t *) key)->value_loc; pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, value_loc->start, value_loc->end); @@ -17221,7 +17208,7 @@ parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *ca pm_parser_local_add(parser, constant_id, value_loc->start, value_loc->end, 0); } - parse_pattern_capture(parser, captures, constant_id, value_loc, capturing); + parse_pattern_capture(parser, captures, constant_id, value_loc); pm_local_variable_target_node_t *target = pm_local_variable_target_node_create( parser, value_loc, @@ -17247,7 +17234,7 @@ parse_pattern_hash_key(pm_parser_t *parser, pm_static_literals_t *keys, pm_node_ * Parse a hash pattern. */ static pm_hash_pattern_node_t * -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) { +parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *first_node, uint16_t depth) { pm_node_list_t assocs = { 0 }; pm_static_literals_t keys = { 0 }; pm_node_t *rest = NULL; @@ -17265,7 +17252,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node 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)) { // Otherwise, we will create an implicit local variable // target for the value. - value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) first_node, capturing); + value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) first_node); } else { // Here we have a value for the first assoc in the list, so // we will parse it now. @@ -17309,7 +17296,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node } if (match1(parser, PM_TOKEN_USTAR_STAR)) { - pm_node_t *assoc = parse_pattern_keyword_rest(parser, captures, capturing); + pm_node_t *assoc = parse_pattern_keyword_rest(parser, captures); if (rest == NULL) { rest = assoc; @@ -17337,7 +17324,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node pm_node_t *value = NULL; 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)) { - value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key, capturing); + value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key); } else { value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1)); } @@ -17364,7 +17351,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node * Parse a pattern expression primitive. */ static pm_node_t * -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) { +parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_diagnostic_id_t diag_id, uint16_t depth) { switch (parser->current.type) { case PM_TOKEN_IDENTIFIER: case PM_TOKEN_METHOD_NAME: { @@ -17376,7 +17363,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0); } - parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous), capturing); + parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous)); return (pm_node_t *) pm_local_variable_target_node_create( parser, &PM_LOCATION_TOKEN_VALUE(&parser->previous), @@ -17460,7 +17447,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm first_node = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous); break; case PM_TOKEN_USTAR_STAR: - first_node = parse_pattern_keyword_rest(parser, captures, capturing); + first_node = parse_pattern_keyword_rest(parser, captures); break; case PM_TOKEN_STRING_BEGIN: first_node = parse_expression(parser, PM_BINDING_POWER_MAX, false, true, PM_ERR_PATTERN_HASH_KEY_LABEL, (uint16_t) (depth + 1)); @@ -17474,7 +17461,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm } } - node = parse_pattern_hash(parser, captures, first_node, capturing, (uint16_t) (depth + 1)); + node = parse_pattern_hash(parser, captures, first_node, (uint16_t) (depth + 1)); accept1(parser, PM_TOKEN_NEWLINE); expect1(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_PATTERN_TERM_BRACE); @@ -17635,6 +17622,26 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm } } +static bool +parse_pattern_alternation_error_each(const pm_node_t *node, void *data) { + switch (PM_NODE_TYPE(node)) { + case PM_LOCAL_VARIABLE_TARGET_NODE: + pm_parser_err((pm_parser_t *) data, node->location.start, node->location.end, PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE); + return false; + default: + return true; + } +} + +/** + * When we get here, we know that we already have a syntax error, because we + * know we have captured a variable and that we are in an alternation. + */ +static void +parse_pattern_alternation_error(pm_parser_t *parser, const pm_node_t *node) { + pm_visit_node(node, parse_pattern_alternation_error_each, parser); +} + /** * Parse any number of primitives joined by alternation and ended optionally by * assignment. @@ -17642,16 +17649,11 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm static pm_node_t * 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) { pm_node_t *node = first_node; - pm_pattern_capturing_t capturing = { false, false }; - - while ((node == NULL) || accept1(parser, PM_TOKEN_PIPE)) { - pm_token_t operator = parser->previous; + bool alternation = false; - if (node) { - if (capturing.capture_in_pattern) { - pm_parser_err(parser, operator.start, operator.end, PM_ERR_PATTERN_ALTERNATIVE_AFTER_CAPTURE); - } - capturing.in_alternative_pattern = true; + while ((node == NULL) || (alternation = accept1(parser, PM_TOKEN_PIPE))) { + if (alternation && !PM_NODE_TYPE_P(node, PM_ALTERNATION_PATTERN_NODE) && captures->size) { + parse_pattern_alternation_error(parser, node); } switch (parser->current.type) { @@ -17664,10 +17666,13 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p case PM_TOKEN_UDOT_DOT: case PM_TOKEN_UDOT_DOT_DOT: case PM_CASE_PRIMITIVE: { - if (node == NULL) { - node = parse_pattern_primitive(parser, captures, &capturing, diag_id, (uint16_t) (depth + 1)); + if (!alternation) { + node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1)); } else { - pm_node_t *right = parse_pattern_primitive(parser, captures, &capturing, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, (uint16_t) (depth + 1)); + pm_token_t operator = parser->previous; + pm_node_t *right = parse_pattern_primitive(parser, captures, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, (uint16_t) (depth + 1)); + + if (captures->size) parse_pattern_alternation_error(parser, right); node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &operator); } @@ -17675,6 +17680,7 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p } case PM_TOKEN_PARENTHESIS_LEFT: case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: { + pm_token_t operator = parser->previous; pm_token_t opening = parser->current; parser_lex(parser); @@ -17683,9 +17689,10 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN); pm_node_t *right = (pm_node_t *) pm_parentheses_node_create(parser, &opening, body, &parser->previous, 0); - if (node == NULL) { + if (!alternation) { node = right; } else { + if (captures->size) parse_pattern_alternation_error(parser, right); node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &operator); } @@ -17695,10 +17702,11 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p pm_parser_err_current(parser, diag_id); pm_node_t *right = (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end); - if (node == NULL) { + if (!alternation) { node = right; } else { - node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &operator); + if (captures->size) parse_pattern_alternation_error(parser, right); + node = (pm_node_t *) pm_alternation_pattern_node_create(parser, node, right, &parser->previous); } break; @@ -17719,7 +17727,7 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0); } - parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous), &capturing); + parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous)); pm_local_variable_target_node_t *target = pm_local_variable_target_node_create( parser, &PM_LOCATION_TOKEN_VALUE(&parser->previous), @@ -17742,13 +17750,12 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag bool leading_rest = false; bool trailing_rest = false; - pm_pattern_capturing_t capturing = { false, false }; switch (parser->current.type) { case PM_TOKEN_LABEL: { parser_lex(parser); pm_node_t *key = (pm_node_t *) pm_symbol_node_label_create(parser, &parser->previous); - node = (pm_node_t *) parse_pattern_hash(parser, captures, key, &capturing, (uint16_t) (depth + 1)); + node = (pm_node_t *) parse_pattern_hash(parser, captures, key, (uint16_t) (depth + 1)); if (!(flags & PM_PARSE_PATTERN_TOP)) { pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT); @@ -17757,8 +17764,8 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag return node; } case PM_TOKEN_USTAR_STAR: { - node = parse_pattern_keyword_rest(parser, captures, &capturing); - node = (pm_node_t *) parse_pattern_hash(parser, captures, node, &capturing, (uint16_t) (depth + 1)); + node = parse_pattern_keyword_rest(parser, captures); + node = (pm_node_t *) parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)); if (!(flags & PM_PARSE_PATTERN_TOP)) { pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT); @@ -17769,10 +17776,10 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag case PM_TOKEN_STRING_BEGIN: { // We need special handling for string beginnings because they could // be dynamic symbols leading to hash patterns. - node = parse_pattern_primitive(parser, captures, &capturing, diag_id, (uint16_t) (depth + 1)); + node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1)); if (pm_symbol_node_label_p(node)) { - node = (pm_node_t *) parse_pattern_hash(parser, captures, node, &capturing, (uint16_t) (depth + 1)); + node = (pm_node_t *) parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)); if (!(flags & PM_PARSE_PATTERN_TOP)) { pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT); @@ -17787,7 +17794,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag case PM_TOKEN_USTAR: { if (flags & (PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI)) { parser_lex(parser); - node = (pm_node_t *) parse_pattern_rest(parser, captures, &capturing); + node = (pm_node_t *) parse_pattern_rest(parser, captures); leading_rest = true; break; } @@ -17801,7 +17808,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag // If we got a dynamic label symbol, then we need to treat it like the // beginning of a hash pattern. if (pm_symbol_node_label_p(node)) { - return (pm_node_t *) parse_pattern_hash(parser, captures, node, &capturing, (uint16_t) (depth + 1)); + return (pm_node_t *) parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)); } if ((flags & PM_PARSE_PATTERN_MULTI) && match1(parser, PM_TOKEN_COMMA)) { @@ -17822,7 +17829,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag } if (accept1(parser, PM_TOKEN_USTAR)) { - node = (pm_node_t *) parse_pattern_rest(parser, captures, &capturing); + node = (pm_node_t *) parse_pattern_rest(parser, captures); // If we have already parsed a splat pattern, then this is an // error. We will continue to parse the rest of the patterns, diff --git a/templates/src/diagnostic.c.erb b/templates/src/diagnostic.c.erb index 87e1f341f6..121dd4b2b6 100644 --- a/templates/src/diagnostic.c.erb +++ b/templates/src/diagnostic.c.erb @@ -299,7 +299,6 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_PARAMETER_UNEXPECTED_FWD] = { "unexpected `...` in parameters", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PARAMETER_WILD_LOOSE_COMMA] = { "unexpected `,` in parameters", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PARAMETER_UNEXPECTED_NO_KW] = { "unexpected **nil; no keywords marker disallowed after keywords", PM_ERROR_LEVEL_SYNTAX }, - [PM_ERR_PATTERN_ALTERNATIVE_AFTER_CAPTURE] = { "alternative pattern after variable capture", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS] = { "unexpected multiple '*' rest patterns in an array pattern", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_CAPTURE_DUPLICATE] = { "duplicated variable name", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE] = { "variable capture in alternative pattern", PM_ERROR_LEVEL_SYNTAX }, diff --git a/test/prism/errors/pattern-capture-in-alt-array.txt b/test/prism/errors/pattern-capture-in-alt-array.txt new file mode 100644 index 0000000000..5cb59fa328 --- /dev/null +++ b/test/prism/errors/pattern-capture-in-alt-array.txt @@ -0,0 +1,4 @@ +1 => [a, b] | 2 + ^ variable capture in alternative pattern + ^ variable capture in alternative pattern + diff --git a/test/prism/errors/pattern-capture-in-alt-hash.txt b/test/prism/errors/pattern-capture-in-alt-hash.txt new file mode 100644 index 0000000000..150b3baecc --- /dev/null +++ b/test/prism/errors/pattern-capture-in-alt-hash.txt @@ -0,0 +1,3 @@ +1 => { a: b } | 2 + ^ variable capture in alternative pattern + diff --git a/test/prism/errors/pattern-capture-in-alt-name.txt b/test/prism/errors/pattern-capture-in-alt-name.txt new file mode 100644 index 0000000000..cbf2bae85f --- /dev/null +++ b/test/prism/errors/pattern-capture-in-alt-name.txt @@ -0,0 +1,3 @@ +1 => (2 => b) | 2 + ^ variable capture in alternative pattern + diff --git a/test/prism/errors/pattern-capture-in-alt-top.txt b/test/prism/errors/pattern-capture-in-alt-top.txt new file mode 100644 index 0000000000..bdf3a7f637 --- /dev/null +++ b/test/prism/errors/pattern-capture-in-alt-top.txt @@ -0,0 +1,4 @@ +1 => a | b + ^ variable capture in alternative pattern + ^ variable capture in alternative pattern +