@@ -545,6 +545,15 @@ public function resolveVariableToNode($var)
545
545
} else {
546
546
throw new \InvalidArgumentException ('$var must be Variable, Param or ClosureUse, not ' . get_class ($ var ));
547
547
}
548
+ if (empty ($ name )) {
549
+ return null ;
550
+ }
551
+
552
+ $ shouldDescend = function ($ nodeToDescand ) {
553
+ // Make sure not to decend into functions or classes (they represent a scope boundary)
554
+ return !($ nodeToDescand instanceof PhpParser \FunctionLike || $ nodeToDescand instanceof PhpParser \ClassLike);
555
+ };
556
+
548
557
// Traverse the AST up
549
558
do {
550
559
// If a function is met, check the parameters and use statements
@@ -569,39 +578,55 @@ public function resolveVariableToNode($var)
569
578
break ;
570
579
}
571
580
572
- // If we get to a ForeachStatement, check the keys and values
573
- if ($ n instanceof Node \Statement \ForeachStatement) {
574
- if ($ n ->foreachKey instanceof Node \Expression \Variable
575
- && $ n ->foreachKey ->expression ->getName () === $ name
576
- ) {
577
- return $ n ->foreachKey ;
578
- }
579
- if ($ n ->foreachValue
580
- && $ n ->foreachValue ->expression instanceof Node \Expression \Variable
581
- && $ n ->foreachValue ->expression ->getName () === $ name
582
- ) {
583
- return $ n ->foreachValue ;
584
- }
585
- }
586
-
587
- // Check each previous sibling node for a variable assignment to that variable
581
+ // Check each previous sibling node and their descendents for a variable assignment to that variable
582
+ // Each previous sibling could contain a declaration of the variable
588
583
while (($ prevSibling = $ n ->getPreviousSibling ()) !== null && $ n = $ prevSibling ) {
589
- if ($ n instanceof Node \Statement \ExpressionStatement) {
590
- $ n = $ n ->expression ;
591
- }
592
- if (
593
- // TODO - clean this up
594
- ($ n instanceof Node \Expression \AssignmentExpression && $ n ->operator ->kind === PhpParser \TokenKind::EqualsToken)
595
- && $ n ->leftOperand instanceof Node \Expression \Variable && $ n ->leftOperand ->getName () === $ name
596
- ) {
584
+
585
+ // Check the sibling itself
586
+ if (self ::isVariableDeclaration ($ n , $ name )) {
597
587
return $ n ;
598
588
}
589
+
590
+ // Check descendant of this sibling (e.g. the children of a previous if block)
591
+ foreach ($ n ->getDescendantNodes ($ shouldDescend ) as $ descendant ) {
592
+ if (self ::isVariableDeclaration ($ descendant , $ name )) {
593
+ return $ descendant ;
594
+ }
595
+ }
599
596
}
600
597
} while (isset ($ n ) && $ n = $ n ->parent );
601
598
// Return null if nothing was found
602
599
return null ;
603
600
}
604
601
602
+ /**
603
+ * Checks whether the given Node declares the given variable name
604
+ *
605
+ * @param Node $n The Node to check
606
+ * @param string $name The name of the wanted variable
607
+ * @return bool
608
+ */
609
+ private static function isVariableDeclaration (Node $ n , string $ name )
610
+ {
611
+ if (
612
+ // TODO - clean this up
613
+ ($ n instanceof Node \Expression \AssignmentExpression && $ n ->operator ->kind === PhpParser \TokenKind::EqualsToken)
614
+ && $ n ->leftOperand instanceof Node \Expression \Variable && $ n ->leftOperand ->getName () === $ name
615
+ ) {
616
+ return true ;
617
+ }
618
+
619
+ if (
620
+ ($ n instanceof Node \ForeachValue || $ n instanceof Node \ForeachKey)
621
+ && $ n ->expression instanceof Node \Expression \Variable
622
+ && $ n ->expression ->getName () === $ name
623
+ ) {
624
+ return true ;
625
+ }
626
+
627
+ return false ;
628
+ }
629
+
605
630
/**
606
631
* Given an expression node, resolves that expression recursively to a type.
607
632
* If the type could not be resolved, returns Types\Mixed_.
0 commit comments