@@ -428,17 +428,20 @@ public static function findVariableScope(File $phpcsFile, $stackPtr, $varName =
428
428
$ token = $ tokens [$ stackPtr ];
429
429
$ varName = isset ($ varName ) ? $ varName : self ::normalizeVarName ($ token ['content ' ]);
430
430
431
- $ arrowFunctionIndex = self ::getContainingArrowFunctionIndex ($ phpcsFile , $ stackPtr );
432
- $ isTokenInsideArrowFunctionBody = is_int ($ arrowFunctionIndex );
433
- if ($ isTokenInsideArrowFunctionBody ) {
434
- // Get the list of variables defined by the arrow function
435
- // If this matches any of them, the scope is the arrow function,
436
- // otherwise, it uses the enclosing scope.
437
- if ($ arrowFunctionIndex ) {
438
- $ variableNames = self ::getVariablesDefinedByArrowFunction ($ phpcsFile , $ arrowFunctionIndex );
439
- self ::debug ('findVariableScope: looking for ' , $ varName , 'in arrow function variables ' , $ variableNames );
440
- if (in_array ($ varName , $ variableNames , true )) {
441
- return $ arrowFunctionIndex ;
431
+ $ enclosingScopeIndex = self ::findVariableScopeExceptArrowFunctions ($ phpcsFile , $ stackPtr );
432
+ if ($ enclosingScopeIndex ) {
433
+ $ arrowFunctionIndex = self ::getContainingArrowFunctionIndex ($ phpcsFile , $ stackPtr , $ enclosingScopeIndex );
434
+ $ isTokenInsideArrowFunctionBody = is_int ($ arrowFunctionIndex );
435
+ if ($ isTokenInsideArrowFunctionBody ) {
436
+ // Get the list of variables defined by the arrow function
437
+ // If this matches any of them, the scope is the arrow function,
438
+ // otherwise, it uses the enclosing scope.
439
+ if ($ arrowFunctionIndex ) {
440
+ $ variableNames = self ::getVariablesDefinedByArrowFunction ($ phpcsFile , $ arrowFunctionIndex );
441
+ self ::debug ('findVariableScope: looking for ' , $ varName , 'in arrow function variables ' , $ variableNames );
442
+ if (in_array ($ varName , $ variableNames , true )) {
443
+ return $ arrowFunctionIndex ;
444
+ }
442
445
}
443
446
}
444
447
}
@@ -635,53 +638,60 @@ public static function isTokenInsideArrowFunctionDefinition(File $phpcsFile, $st
635
638
/**
636
639
* @param File $phpcsFile
637
640
* @param int $stackPtr
641
+ * @param int $enclosingScopeIndex
638
642
*
639
643
* @return ?int
640
644
*/
641
- public static function getContainingArrowFunctionIndex (File $ phpcsFile , $ stackPtr )
645
+ public static function getContainingArrowFunctionIndex (File $ phpcsFile , $ stackPtr, $ enclosingScopeIndex )
642
646
{
643
- $ arrowFunctionIndex = self ::getPreviousArrowFunctionIndex ($ phpcsFile , $ stackPtr );
647
+ $ arrowFunctionIndex = self ::getPreviousArrowFunctionIndex ($ phpcsFile , $ stackPtr, $ enclosingScopeIndex );
644
648
if (! is_int ($ arrowFunctionIndex )) {
645
649
return null ;
646
650
}
647
651
$ arrowFunctionInfo = self ::getArrowFunctionOpenClose ($ phpcsFile , $ arrowFunctionIndex );
648
652
if (! $ arrowFunctionInfo ) {
649
653
return null ;
650
654
}
651
- $ arrowFunctionScopeStart = $ arrowFunctionInfo ['scope_opener ' ];
652
- $ arrowFunctionScopeEnd = $ arrowFunctionInfo ['scope_closer ' ];
653
- if ($ stackPtr > $ arrowFunctionScopeStart && $ stackPtr < $ arrowFunctionScopeEnd ) {
655
+
656
+ // We found the closest arrow function before this token. If the token is
657
+ // within the scope of that arrow function, then return it.
658
+ if ($ stackPtr > $ arrowFunctionInfo ['scope_opener ' ] && $ stackPtr < $ arrowFunctionInfo ['scope_closer ' ]) {
654
659
return $ arrowFunctionIndex ;
655
660
}
661
+
662
+ // If the token is after the scope of the closest arrow function, we may
663
+ // still be inside the scope of a nested arrow function, so we need to
664
+ // search further back until we are certain there are no more arrow
665
+ // functions.
666
+ if ($ stackPtr > $ arrowFunctionInfo ['scope_closer ' ]) {
667
+ return self ::getContainingArrowFunctionIndex ($ phpcsFile , $ arrowFunctionIndex , $ enclosingScopeIndex );
668
+ }
669
+
656
670
return null ;
657
671
}
658
672
659
673
/**
674
+ * Move back from the stackPtr to the start of the enclosing scope until we
675
+ * find a 'fn' token that starts an arrow function, returning the index of
676
+ * that token. Returns null if there are no arrow functions before stackPtr.
677
+ *
678
+ * Note that this does not guarantee that stackPtr is inside the arrow
679
+ * function scope we find!
680
+ *
660
681
* @param File $phpcsFile
661
682
* @param int $stackPtr
683
+ * @param int $enclosingScopeIndex
662
684
*
663
685
* @return ?int
664
686
*/
665
- private static function getPreviousArrowFunctionIndex (File $ phpcsFile , $ stackPtr )
687
+ private static function getPreviousArrowFunctionIndex (File $ phpcsFile , $ stackPtr, $ enclosingScopeIndex )
666
688
{
667
689
$ tokens = $ phpcsFile ->getTokens ();
668
- $ enclosingScopeIndex = self ::findVariableScopeExceptArrowFunctions ($ phpcsFile , $ stackPtr );
669
690
for ($ index = $ stackPtr - 1 ; $ index > $ enclosingScopeIndex ; $ index --) {
670
691
$ token = $ tokens [$ index ];
671
692
if ($ token ['content ' ] === 'fn ' && self ::isArrowFunction ($ phpcsFile , $ index )) {
672
693
return $ index ;
673
694
}
674
- // If we find a token that would close an arrow function scope before we
675
- // find a token that would open an arrow function scope, then we've found
676
- // a nested arrow function and we should ignore it, move back before THAT
677
- // arrow function's scope, and continue to search.
678
- $ arrowFunctionStartIndex = $ phpcsFile ->findPrevious ([T_FN ], $ index , $ enclosingScopeIndex );
679
- if (is_int ($ arrowFunctionStartIndex )) {
680
- $ openClose = self ::getArrowFunctionOpenClose ($ phpcsFile , $ arrowFunctionStartIndex );
681
- if ($ openClose && $ openClose ['scope_closer ' ] === $ index ) {
682
- $ index = $ openClose ['scope_opener ' ];
683
- }
684
- }
685
695
}
686
696
return null ;
687
697
}
0 commit comments