Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
513 changes: 513 additions & 0 deletions snapshots/pattern_alternation_non_captures.txt

Large diffs are not rendered by default.

36 changes: 29 additions & 7 deletions src/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -17628,22 +17628,44 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm
}
}

/**
* Add an error for each variable captured by the given pattern. Only descend
* into nodes that make up the pattern itself: a lambda, a string interpolation,
* or a pinned expression can contain local variable targets of its own that
* are not captures of this pattern. A nested alternation is not visited
* either: its operands were already checked when it was parsed.
*/
static bool
parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
pm_parser_t *parser = (pm_parser_t *) data;

switch (PM_NODE_TYPE(node)) {
case PM_LOCAL_VARIABLE_TARGET_NODE: {
pm_parser_t *parser = (pm_parser_t *) data;
pm_parser_err(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE);
case PM_LOCAL_VARIABLE_TARGET_NODE:
// Underscore-prefixed names are not captures, see
// parse_pattern_capture.
if (peek_at(parser, parser->start + PM_NODE_START(node)) != '_') {
pm_parser_err(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE);
}
return false;
}
default:
case PM_ARRAY_PATTERN_NODE:
case PM_ASSOC_NODE:
case PM_ASSOC_SPLAT_NODE:
case PM_CAPTURE_PATTERN_NODE:
case PM_FIND_PATTERN_NODE:
case PM_HASH_PATTERN_NODE:
case PM_IMPLICIT_NODE:
case PM_PARENTHESES_NODE:
case PM_SPLAT_NODE:
return true;
default:
return false;
}
}

/**
* 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.
* Called when we are in an alternation and a variable has been captured
* somewhere in the pattern. That capture may be outside of the given node, so
* this only adds errors for the captures that are actually inside of it.
*/
static void
parse_pattern_alternation_error(pm_parser_t *parser, const pm_node_t *node) {
Expand Down
3 changes: 3 additions & 0 deletions test/prism/errors/pattern-capture-in-alt-find.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
1 => [x, [*, y, *] | 1]
^ variable capture in alternative pattern

3 changes: 3 additions & 0 deletions test/prism/errors/pattern-capture-in-alt-implicit.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
1 => [x, { a: } | 1]
^ variable capture in alternative pattern

3 changes: 3 additions & 0 deletions test/prism/errors/pattern-capture-in-alt-inner.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
1 => [x, (1 | y) | 2]
^ variable capture in alternative pattern

3 changes: 3 additions & 0 deletions test/prism/errors/pattern-capture-in-alt-kwsplat.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
1 => [x, { **y } | 1]
^ variable capture in alternative pattern

3 changes: 3 additions & 0 deletions test/prism/errors/pattern-capture-in-alt-nested.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
1 => 1 | [2 | 3, y]
^ variable capture in alternative pattern

3 changes: 3 additions & 0 deletions test/prism/errors/pattern-capture-in-alt-splat.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
1 => [x, [*y] | 1]
^ variable capture in alternative pattern

31 changes: 31 additions & 0 deletions test/prism/fixtures/pattern_alternation_non_captures.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
case foo
in [bar, 1 | _baz]
end

case foo
in [bar, _baz | 1]
end

case foo
in [bar, "#{case 1; in baz; end}" | 2]
end

case foo
in [bar, -> { case 1; in baz; end } | 2]
end

case foo
in [bar, ^(case 1; in baz; end) | 2]
end

case foo
in [bar, [*_baz] | 1]
end

case foo
in [bar, { **_baz } | 1]
end

case foo
in [bar, { _baz: } | 1]
end
2 changes: 2 additions & 0 deletions test/prism/fixtures_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class FixturesTest < TestCase
except << "4.1/void_value.txt"
# https://bugs.ruby-lang.org/issues/19107
except << "4.1/trailing_comma_after_method_arguments.txt"
# https://bugs.ruby-lang.org/issues/22310
except << "pattern_alternation_non_captures.txt"

Fixture.each_for_current_ruby(except: except) do |fixture|
define_method(fixture.test_name) { assert_valid_syntax(fixture.read) }
Expand Down
3 changes: 3 additions & 0 deletions test/prism/locals_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ class LocalsTest < TestCase

# https://bugs.ruby-lang.org/issues/19107
"4.1/trailing_comma_after_method_arguments.txt",

# https://bugs.ruby-lang.org/issues/22310
"pattern_alternation_non_captures.txt",
]

Fixture.each_for_current_ruby(except: except) do |fixture|
Expand Down
2 changes: 2 additions & 0 deletions test/prism/ruby/ripper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class RipperTest < TestCase
incorrect += [
# https://bugs.ruby-lang.org/issues/21945
"and_or_with_suffix.txt",
# https://bugs.ruby-lang.org/issues/22310
"pattern_alternation_non_captures.txt",
]
end

Expand Down
Loading