Repository: ruby/prism (c)

Issue:
MatchRequiredNide is not scoping variables correctly
In this snippet `a` should have a depth of 1.  It is 0 like `b`:

```ruby
a = nil
proc {
  [0, 1] => [a, b]
}.call
p [a, defined?(b)]
```

AST:

```text
@ ProgramNode (location: (1,0)-(5,18))
├── locals: [:a]
└── statements:
    @ StatementsNode (location: (1,0)-(5,18))
    └── body: (length: 3)
        ├── @ LocalVariableWriteNode (location: (1,0)-(1,7))
        │   ├── name: :a
        │   ├── depth: 0
        │   ├── name_loc: (1,0)-(1,1) = "a"
        │   ├── value:
        │   │   @ NilNode (location: (1,4)-(1,7))
        │   └── operator_loc: (1,2)-(1,3) = "="
        ├── @ CallNode (location: (2,0)-(4,6))
        │   ├── receiver:
        │   │   @ CallNode (location: (2,0)-(4,1))
        │   │   ├── receiver: ∅
        │   │   ├── call_operator_loc: ∅
        │   │   ├── message_loc: (2,0)-(2,4) = "proc"
        │   │   ├── opening_loc: ∅
        │   │   ├── arguments: ∅
        │   │   ├── closing_loc: ∅
        │   │   ├── block:
        │   │   │   @ BlockNode (location: (2,5)-(4,1))
        │   │   │   ├── locals: [:a, :b]
        │   │   │   ├── parameters: ∅
        │   │   │   ├── body:
        │   │   │   │   @ StatementsNode (location: (3,2)-(3,18))
        │   │   │   │   └── body: (length: 1)
        │   │   │   │       └── @ MatchRequiredNode (location: (3,2)-(3,18))
        │   │   │   │           ├── value:
        │   │   │   │           │   @ ArrayNode (location: (3,2)-(3,8))
        │   │   │   │           │   ├── elements: (length: 2)
        │   │   │   │           │   │   ├── @ IntegerNode (location: (3,3)-(3,4))
        │   │   │   │           │   │   │   └── flags: decimal
        │   │   │   │           │   │   └── @ IntegerNode (location: (3,6)-(3,7))
        │   │   │   │           │   │       └── flags: decimal
        │   │   │   │           │   ├── opening_loc: (3,2)-(3,3) = "["
        │   │   │   │           │   └── closing_loc: (3,7)-(3,8) = "]"
        │   │   │   │           ├── pattern:
        │   │   │   │           │   @ ArrayPatternNode (location: (3,12)-(3,18))
        │   │   │   │           │   ├── constant: ∅
        │   │   │   │           │   ├── requireds: (length: 2)
        │   │   │   │           │   │   ├── @ LocalVariableTargetNode (location: (3,13)-(3,14))
        │   │   │   │           │   │   │   ├── name: :a
        │   │   │   │           │   │   │   └── depth: 0
        │   │   │   │           │   │   └── @ LocalVariableTargetNode (location: (3,16)-(3,17))
        │   │   │   │           │   │       ├── name: :b
        │   │   │   │           │   │       └── depth: 0
        │   │   │   │           │   ├── rest: ∅
        │   │   │   │           │   ├── posts: (length: 0)
        │   │   │   │           │   ├── opening_loc: (3,12)-(3,13) = "["
        │   │   │   │           │   └── closing_loc: (3,17)-(3,18) = "]"
        │   │   │   │           └── operator_loc: (3,9)-(3,11) = "=>"
        │   │   │   ├── opening_loc: (2,5)-(2,6) = "{"
        │   │   │   └── closing_loc: (4,0)-(4,1) = "}"
        │   │   ├── flags: ∅
        │   │   └── name: :proc
        │   ├── call_operator_loc: (4,1)-(4,2) = "."
        │   ├── message_loc: (4,2)-(4,6) = "call"
        │   ├── opening_loc: ∅
        │   ├── arguments: ∅
        │   ├── closing_loc: ∅
        │   ├── block: ∅
        │   ├── flags: ∅
        │   └── name: :call
        └── @ CallNode (location: (5,0)-(5,18))
            ├── receiver: ∅
            ├── call_operator_loc: ∅
            ├── message_loc: (5,0)-(5,1) = "p"
            ├── opening_loc: ∅
            ├── arguments:
            │   @ ArgumentsNode (location: (5,2)-(5,18))
            │   ├── arguments: (length: 1)
            │   │   └── @ ArrayNode (location: (5,2)-(5,18))
            │   │       ├── elements: (length: 2)
            │   │       │   ├── @ LocalVariableReadNode (location: (5,3)-(5,4))
            │   │       │   │   ├── name: :a
            │   │       │   │   └── depth: 0
            │   │       │   └── @ DefinedNode (location: (5,6)-(5,17))
            │   │       │       ├── lparen_loc: (5,14)-(5,15) = "("
            │   │       │       ├── value:
            │   │       │       │   @ CallNode (location: (5,15)-(5,16))
            │   │       │       │   ├── receiver: ∅
            │   │       │       │   ├── call_operator_loc: ∅
            │   │       │       │   ├── message_loc: (5,15)-(5,16) = "b"
            │   │       │       │   ├── opening_loc: ∅
            │   │       │       │   ├── arguments: ∅
            │   │       │       │   ├── closing_loc: ∅
            │   │       │       │   ├── block: ∅
            │   │       │       │   ├── flags: variable_call
            │   │       │       │   └── name: :b
            │   │       │       ├── rparen_loc: (5,16)-(5,17) = ")"
            │   │       │       └── keyword_loc: (5,6)-(5,14) = "defined?"
            │   │       ├── opening_loc: (5,2)-(5,3) = "["
            │   │       └── closing_loc: (5,17)-(5,18) = "]"
            │   └── flags: ∅
            ├── closing_loc: ∅
            ├── block: ∅
            ├── flags: ∅
            └── name: :p
```



Pull request:
Fix LocalVariableTargetNode depth in patterns
Fix #1821

Patch:
diff --git a/src/prism.c b/src/prism.c
index 95bfc5a8..7aee11c9 100644
--- a/src/prism.c
+++ b/src/prism.c
@@ -4136,6 +4136,21 @@ pm_local_variable_target_node_create(pm_parser_t *parser, const pm_token_t *name
     );
 }
 
+/**
+ * Allocate and initialize a new LocalVariableTargetNode node with the given depth.
+ */
+static pm_local_variable_target_node_t *
+pm_local_variable_target_node_create_depth(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) {
+    pm_refute_numbered_parameter(parser, name->start, name->end);
+
+    return pm_local_variable_target_node_create_values(
+        parser,
+        &(pm_location_t) { .start = name->start, .end = name->end },
+        pm_parser_constant_id_token(parser, name),
+        depth
+    );
+}
+
 /**
  * Allocate and initialize a new MatchPredicateNode node.
  */
@@ -13003,8 +13018,13 @@ parse_pattern_primitive(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
         case PM_TOKEN_IDENTIFIER:
         case PM_TOKEN_METHOD_NAME: {
             parser_lex(parser);
-            pm_parser_local_add_token(parser, &parser->previous);
-            return (pm_node_t *) pm_local_variable_target_node_create(parser, &parser->previous);
+            pm_token_t name = parser->previous;
+            int depth = pm_parser_local_depth(parser, &name);
+            if (depth < 0) {
+                depth = 0;
+                pm_parser_local_add_token(parser, &name);
+            }
+            return (pm_node_t *) pm_local_variable_target_node_create_depth(parser, &name, (uint32_t) depth);
         }
         case PM_TOKEN_BRACKET_LEFT_ARRAY: {
             pm_token_t opening = parser->current;
@@ -13321,9 +13341,13 @@ parse_pattern_primitives(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
 
         expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_PATTERN_IDENT_AFTER_HROCKET);
         pm_token_t identifier = parser->previous;
-        pm_parser_local_add_token(parser, &identifier);
+        int depth = pm_parser_local_depth(parser, &identifier);
+        if (depth < 0) {
+            depth = 0;
+            pm_parser_local_add_token(parser, &identifier);
+        }
 
-        pm_node_t *target = (pm_node_t *) pm_local_variable_target_node_create(parser, &identifier);
+        pm_node_t *target = (pm_node_t *) pm_local_variable_target_node_create_depth(parser, &identifier, (uint32_t) depth);
         node = (pm_node_t *) pm_capture_pattern_node_create(parser, node, target, &operator);
     }
 


Test patch:
diff --git a/test/prism/fixtures/patterns.txt b/test/prism/fixtures/patterns.txt
index 9f73e6ec..e4aa83bd 100644
--- a/test/prism/fixtures/patterns.txt
+++ b/test/prism/fixtures/patterns.txt
@@ -193,3 +193,8 @@ foo in A[
 
 foo in bar => baz
 foo => bar => baz
+
+foo, bar, baz = 1, 2
+foo do
+  [1, 2] => [foo, bar] => baz
+end
diff --git a/test/prism/snapshots/patterns.txt b/test/prism/snapshots/patterns.txt
index 665a345d..44e15fb6 100644
--- a/test/prism/snapshots/patterns.txt
+++ b/test/prism/snapshots/patterns.txt
@@ -1,8 +1,8 @@
-@ ProgramNode (location: (1,0)-(195,17))
-├── locals: [:bar, :baz, :qux, :b, :a]
+@ ProgramNode (location: (1,0)-(200,3))
+├── locals: [:bar, :baz, :qux, :b, :a, :foo]
 └── statements:
-    @ StatementsNode (location: (1,0)-(195,17))
-    └── body: (length: 172)
+    @ StatementsNode (location: (1,0)-(200,3))
+    └── body: (length: 174)
         ├── @ MatchRequiredNode (location: (1,0)-(1,10))
         │   ├── value:
         │   │   @ CallNode (location: (1,0)-(1,3))
@@ -4575,27 +4575,104 @@
         │   │   │   └── depth: 0
         │   │   └── operator_loc: (194,11)-(194,13) = "=>"
         │   └── operator_loc: (194,4)-(194,6) = "in"
-        └── @ MatchRequiredNode (location: (195,0)-(195,17))
-            ├── value:
-            │   @ CallNode (location: (195,0)-(195,3))
-            │   ├── receiver: ∅
-            │   ├── call_operator_loc: ∅
-            │   ├── name: :foo
-            │   ├── message_loc: (195,0)-(195,3) = "foo"
-            │   ├── opening_loc: ∅
-            │   ├── arguments: ∅
-            │   ├── closing_loc: ∅
-            │   ├── block: ∅
-            │   └── flags: variable_call
-            ├── pattern:
-            │   @ CapturePatternNode (location: (195,7)-(195,17))
-            │   ├── value:
-            │   │   @ LocalVariableTargetNode (location: (195,7)-(195,10))
-            │   │   ├── name: :bar
-            │   │   └── depth: 0
-            │   ├── target:
-            │   │   @ LocalVariableTargetNode (location: (195,14)-(195,17))
-            │   │   ├── name: :baz
-            │   │   └── depth: 0
-            │   └── operator_loc: (195,11)-(195,13) = "=>"
-            └── operator_loc: (195,4)-(195,6) = "=>"
+        ├── @ MatchRequiredNode (location: (195,0)-(195,17))
+        │   ├── value:
+        │   │   @ CallNode (location: (195,0)-(195,3))
+        │   │   ├── receiver: ∅
+        │   │   ├── call_operator_loc: ∅
+        │   │   ├── name: :foo
+        │   │   ├── message_loc: (195,0)-(195,3) = "foo"
+        │   │   ├── opening_loc: ∅
+        │   │   ├── arguments: ∅
+        │   │   ├── closing_loc: ∅
+        │   │   ├── block: ∅
+        │   │   └── flags: variable_call
+        │   ├── pattern:
+        │   │   @ CapturePatternNode (location: (195,7)-(195,17))
+        │   │   ├── value:
+        │   │   │   @ LocalVariableTargetNode (location: (195,7)-(195,10))
+        │   │   │   ├── name: :bar
+        │   │   │   └── depth: 0
+        │   │   ├── target:
+        │   │   │   @ LocalVariableTargetNode (location: (195,14)-(195,17))
+        │   │   │   ├── name: :baz
+        │   │   │   └── depth: 0
+        │   │   └── operator_loc: (195,11)-(195,13) = "=>"
+        │   └── operator_loc: (195,4)-(195,6) = "=>"
+        ├── @ MultiWriteNode (location: (197,0)-(197,20))
+        │   ├── lefts: (length: 3)
+        │   │   ├── @ LocalVariableTargetNode (location: (197,0)-(197,3))
+        │   │   │   ├── name: :foo
+        │   │   │   └── depth: 0
+        │   │   ├── @ LocalVariableTargetNode (location: (197,5)-(197,8))
+        │   │   │   ├── name: :bar
+        │   │   │   └── depth: 0
+        │   │   └── @ LocalVariableTargetNode (location: (197,10)-(197,13))
+        │   │       ├── name: :baz
+        │   │       └── depth: 0
+        │   ├── rest: ∅
+        │   ├── rights: (length: 0)
+        │   ├── lparen_loc: ∅
+        │   ├── rparen_loc: ∅
+        │   ├── operator_loc: (197,14)-(197,15) = "="
+        │   └── value:
+        │       @ ArrayNode (location: (197,16)-(197,20))
+        │       ├── elements: (length: 2)
+        │       │   ├── @ IntegerNode (location: (197,16)-(197,17))
+        │       │   │   └── flags: decimal
+        │       │   └── @ IntegerNode (location: (197,19)-(197,20))
+        │       │       └── flags: decimal
+        │       ├── opening_loc: ∅
+        │       ├── closing_loc: ∅
+        │       └── flags: ∅
+        └── @ CallNode (location: (198,0)-(200,3))
+            ├── receiver: ∅
+            ├── call_operator_loc: ∅
+            ├── name: :foo
+            ├── message_loc: (198,0)-(198,3) = "foo"
+            ├── opening_loc: ∅
+            ├── arguments: ∅
+            ├── closing_loc: ∅
+            ├── block:
+            │   @ BlockNode (location: (198,4)-(200,3))
+            │   ├── locals: []
+            │   ├── parameters: ∅
+            │   ├── body:
+            │   │   @ StatementsNode (location: (199,2)-(199,29))
+            │   │   └── body: (length: 1)
+            │   │       └── @ MatchRequiredNode (location: (199,2)-(199,29))
+            │   │           ├── value:
+            │   │           │   @ ArrayNode (location: (199,2)-(199,8))
+            │   │           │   ├── elements: (length: 2)
+            │   │           │   │   ├── @ IntegerNode (location: (199,3)-(199,4))
+            │   │           │   │   │   └── flags: decimal
+            │   │           │   │   └── @ IntegerNode (location: (199,6)-(199,7))
+            │   │           │   │       └── flags: decimal
+            │   │           │   ├── opening_loc: (199,2)-(199,3) = "["
+            │   │           │   ├── closing_loc: (199,7)-(199,8) = "]"
+            │   │           │   └── flags: ∅
+            │   │           ├── pattern:
+            │   │           │   @ CapturePatternNode (location: (199,12)-(199,29))
+            │   │           │   ├── value:
+            │   │           │   │   @ ArrayPatternNode (location: (199,12)-(199,22))
+            │   │           │   │   ├── constant: ∅
+            │   │           │   │   ├── requireds: (length: 2)
+            │   │           │   │   │   ├── @ LocalVariableTargetNode (location: (199,13)-(199,16))
+            │   │           │   │   │   │   ├── name: :foo
+            │   │           │   │   │   │   └── depth: 1
+            │   │           │   │   │   └── @ LocalVariableTargetNode (location: (199,18)-(199,21))
+            │   │           │   │   │       ├── name: :bar
+            │   │           │   │   │       └── depth: 1
+            │   │           │   │   ├── rest: ∅
+            │   │           │   │   ├── posts: (length: 0)
+            │   │           │   │   ├── opening_loc: (199,12)-(199,13) = "["
+            │   │           │   │   └── closing_loc: (199,21)-(199,22) = "]"
+            │   │           │   ├── target:
+            │   │           │   │   @ LocalVariableTargetNode (location: (199,26)-(199,29))
+            │   │           │   │   ├── name: :baz
+            │   │           │   │   └── depth: 1
+            │   │           │   └── operator_loc: (199,23)-(199,25) = "=>"
+            │   │           └── operator_loc: (199,9)-(199,11) = "=>"
+            │   ├── opening_loc: (198,4)-(198,6) = "do"
+            │   └── closing_loc: (200,0)-(200,3) = "end"
+            └── flags: ∅