Skip to content

Commit e005959

Browse files
committed
Recursively search for containing arrow function
1 parent 41b4101 commit e005959

File tree

1 file changed

+22
-49
lines changed

1 file changed

+22
-49
lines changed

VariableAnalysis/Lib/Helpers.php

Lines changed: 22 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)