@@ -545,6 +545,15 @@ public function resolveVariableToNode($var)
545545 } else {
546546 throw new \InvalidArgumentException ('$var must be Variable, Param or ClosureUse, not ' . get_class ($ var ));
547547 }
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+
548557 // Traverse the AST up
549558 do {
550559 // If a function is met, check the parameters and use statements
@@ -569,39 +578,55 @@ public function resolveVariableToNode($var)
569578 break ;
570579 }
571580
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
588583 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 )) {
597587 return $ n ;
598588 }
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+ }
599596 }
600597 } while (isset ($ n ) && $ n = $ n ->parent );
601598 // Return null if nothing was found
602599 return null ;
603600 }
604601
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+
605630 /**
606631 * Given an expression node, resolves that expression recursively to a type.
607632 * If the type could not be resolved, returns Types\Mixed_.
0 commit comments