@@ -428,7 +428,8 @@ public static function findVariableScope(File $phpcsFile, $stackPtr, $varName =
428428 $ token = $ tokens [$ stackPtr ];
429429 $ varName = isset ($ varName ) ? $ varName : self ::normalizeVarName ($ token ['content ' ]);
430430
431- $ arrowFunctionIndex = self ::getContainingArrowFunctionIndex ($ phpcsFile , $ stackPtr );
431+ $ enclosingScopeIndex = self ::findVariableScopeExceptArrowFunctions ($ phpcsFile , $ stackPtr );
432+ $ arrowFunctionIndex = self ::getContainingArrowFunctionIndex ($ phpcsFile , $ stackPtr , $ enclosingScopeIndex );
432433 $ isTokenInsideArrowFunctionBody = is_int ($ arrowFunctionIndex );
433434 if ($ isTokenInsideArrowFunctionBody ) {
434435 // Get the list of variables defined by the arrow function
@@ -635,88 +636,60 @@ public static function isTokenInsideArrowFunctionDefinition(File $phpcsFile, $st
635636 /**
636637 * @param File $phpcsFile
637638 * @param int $stackPtr
639+ * @param int $enclosingScopeIndex
638640 *
639641 * @return ?int
640642 */
641- public static function getContainingArrowFunctionIndex (File $ phpcsFile , $ stackPtr )
643+ public static function getContainingArrowFunctionIndex (File $ phpcsFile , $ stackPtr, $ enclosingScopeIndex )
642644 {
643- if (! self ::isTokenInsideArrowFunction ($ phpcsFile , $ stackPtr )) {
644- return null ;
645- }
646- $ arrowFunctionIndex = self ::getPreviousArrowFunctionIndex ($ phpcsFile , $ stackPtr );
645+ $ arrowFunctionIndex = self ::getPreviousArrowFunctionIndex ($ phpcsFile , $ stackPtr , $ enclosingScopeIndex );
647646 if (! is_int ($ arrowFunctionIndex )) {
648647 return null ;
649648 }
650649 $ arrowFunctionInfo = self ::getArrowFunctionOpenClose ($ phpcsFile , $ arrowFunctionIndex );
651650 if (! $ arrowFunctionInfo ) {
652651 return null ;
653652 }
654- $ arrowFunctionScopeStart = $ arrowFunctionInfo ['scope_opener ' ];
655- $ arrowFunctionScopeEnd = $ arrowFunctionInfo ['scope_closer ' ];
656- if ($ stackPtr > $ arrowFunctionScopeStart && $ stackPtr < $ arrowFunctionScopeEnd ) {
653+
654+ // We found the closest arrow function before this token. If the token is
655+ // within the scope of that arrow function, then return it.
656+ if ($ stackPtr > $ arrowFunctionInfo ['scope_opener ' ] && $ stackPtr < $ arrowFunctionInfo ['scope_closer ' ]) {
657657 return $ arrowFunctionIndex ;
658658 }
659- return null ;
660- }
661659
662- /**
663- * Move back from the stackPtr to the start of the enclosing scope until we
664- * find a 'fn' token that starts an arrow function, returning true if we find
665- * one.
666- *
667- * @param File $phpcsFile
668- * @param int $stackPtr
669- *
670- * @return bool
671- */
672- private static function isTokenInsideArrowFunction (File $ phpcsFile , $ stackPtr )
673- {
674- $ tokens = $ phpcsFile ->getTokens ();
675- $ enclosingScopeIndex = self ::findVariableScopeExceptArrowFunctions ($ phpcsFile , $ stackPtr );
676- for ($ index = $ stackPtr - 1 ; $ index > $ enclosingScopeIndex ; $ index --) {
677- $ token = $ tokens [$ index ];
678- if ($ token ['content ' ] === 'fn ' && self ::isArrowFunction ($ phpcsFile , $ index )) {
679- return true ;
680- }
660+ // If the token is after the scope of the closest arrow function, we may
661+ // still be inside the scope of a nested arrow function, so we need to
662+ // search further back until we are certain there are no more arrow
663+ // functions.
664+ if ($ stackPtr > $ arrowFunctionInfo ['scope_closer ' ]) {
665+ return self ::getContainingArrowFunctionIndex ($ phpcsFile , $ arrowFunctionIndex , $ enclosingScopeIndex );
681666 }
682- return false ;
667+
668+ return null ;
683669 }
684670
685671 /**
686672 * Move back from the stackPtr to the start of the enclosing scope until we
687673 * find a 'fn' token that starts an arrow function, returning the index of
688- * that token. Returns null if we are not inside an arrow function .
674+ * that token. Returns null if there are no arrow functions before stackPtr .
689675 *
690- * NOTE: This is used to find arrow function scope but is not fast because it
691- * needs to identify nested arrow functions also. Please use
692- * `isTokenInsideArrowFunction()` instead if you just want to know if we are
693- * inside an arrow function.
676+ * Note that this does not guarantee that stackPtr is inside the arrow
677+ * function scope we find!
694678 *
695679 * @param File $phpcsFile
696680 * @param int $stackPtr
681+ * @param int $enclosingScopeIndex
697682 *
698683 * @return ?int
699684 */
700- private static function getPreviousArrowFunctionIndex (File $ phpcsFile , $ stackPtr )
685+ private static function getPreviousArrowFunctionIndex (File $ phpcsFile , $ stackPtr, $ enclosingScopeIndex )
701686 {
702687 $ tokens = $ phpcsFile ->getTokens ();
703- $ enclosingScopeIndex = self ::findVariableScopeExceptArrowFunctions ($ phpcsFile , $ stackPtr );
704688 for ($ index = $ stackPtr - 1 ; $ index > $ enclosingScopeIndex ; $ index --) {
705689 $ token = $ tokens [$ index ];
706690 if ($ token ['content ' ] === 'fn ' && self ::isArrowFunction ($ phpcsFile , $ index )) {
707691 return $ index ;
708692 }
709- // If we find a token that would close an arrow function scope before we
710- // find a token that would open an arrow function scope, then we've found
711- // a nested arrow function and we should ignore it, move back before THAT
712- // arrow function's scope, and continue to search.
713- $ arrowFunctionStartIndex = $ phpcsFile ->findPrevious ([T_FN ], $ index , $ enclosingScopeIndex );
714- if (is_int ($ arrowFunctionStartIndex )) {
715- $ openClose = self ::getArrowFunctionOpenClose ($ phpcsFile , $ arrowFunctionStartIndex );
716- if ($ openClose && $ openClose ['scope_closer ' ] === $ index ) {
717- $ index = $ openClose ['scope_opener ' ];
718- }
719- }
720693 }
721694 return null ;
722695 }
0 commit comments