From 96274aa77069c70ef307eff7fb275555c25c1b6b Mon Sep 17 00:00:00 2001 From: Payton Swick Date: Fri, 12 Aug 2022 18:13:16 -0400 Subject: [PATCH] Use the same technique for finding list assignments as phpcs might See https://github.com/squizlabs/PHP_CodeSniffer/pull/3632 --- VariableAnalysis/Lib/Helpers.php | 85 +++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 17 deletions(-) diff --git a/VariableAnalysis/Lib/Helpers.php b/VariableAnalysis/Lib/Helpers.php index 66b02d1..ae6d3be 100644 --- a/VariableAnalysis/Lib/Helpers.php +++ b/VariableAnalysis/Lib/Helpers.php @@ -654,7 +654,70 @@ public static function getArrowFunctionOpenClose(File $phpcsFile, $stackPtr) } /** - * Return a list of indices for variables assigned within a list assignment + * Determine if a token is a list opener for list assignment/destructuring. + * + * The index provided can be either the opening square brace of a short list + * assignment like the first character of `[$a] = $b;` or the `list` token of + * an expression like `list($a) = $b;` or the opening parenthesis of that + * expression. + * + * @param File $phpcsFile + * @param int $listOpenerIndex + * + * @return bool + */ + private static function isListAssignment(File $phpcsFile, $listOpenerIndex) + { + $tokens = $phpcsFile->getTokens(); + // Match `[$a] = $b;` except for when the previous token is a parenthesis. + if ($tokens[$listOpenerIndex]['code'] === T_OPEN_SHORT_ARRAY) { + return true; + } + // Match `list($a) = $b;` + if ($tokens[$listOpenerIndex]['code'] === T_LIST) { + return true; + } + + // If $listOpenerIndex is the open parenthesis of `list($a) = $b;`, then + // match that too. + if ($tokens[$listOpenerIndex]['code'] === T_OPEN_PARENTHESIS) { + $previousTokenPtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $listOpenerIndex - 1, null, true); + if ( + isset($tokens[$previousTokenPtr]) + && $tokens[$previousTokenPtr]['code'] === T_LIST + ) { + return true; + } + return true; + } + + // If the list opener token is a square bracket that is preceeded by a + // close parenthesis that has an owner which is a scope opener, then this + // is a list assignment and not an array access. + // + // Match `if (true) [$a] = $b;` + if ($tokens[$listOpenerIndex]['code'] === T_OPEN_SQUARE_BRACKET) { + $previousTokenPtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $listOpenerIndex - 1, null, true); + if ( + isset($tokens[$previousTokenPtr]) + && $tokens[$previousTokenPtr]['code'] === T_CLOSE_PARENTHESIS + && isset($tokens[$previousTokenPtr]['parenthesis_owner']) + && isset(Tokens::$scopeOpeners[$tokens[$tokens[$previousTokenPtr]['parenthesis_owner']]['code']]) + ) { + return true; + } + } + + return false; + } + + /** + * Return a list of indices for variables assigned within a list assignment. + * + * The index provided can be either the opening square brace of a short list + * assignment like the first character of `[$a] = $b;` or the `list` token of + * an expression like `list($a) = $b;` or the opening parenthesis of that + * expression. * * @param File $phpcsFile * @param int $listOpenerIndex @@ -666,22 +729,6 @@ public static function getListAssignments(File $phpcsFile, $listOpenerIndex) $tokens = $phpcsFile->getTokens(); self::debug('getListAssignments', $listOpenerIndex, $tokens[$listOpenerIndex]); - // If the list opener token is preceeded by a variable or a bracket (a - // multidimensional array), this is an array access and not a list - // assignment. - $previousStatementPtr = self::getPreviousStatementPtr($phpcsFile, $listOpenerIndex); - $previousTokenPtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $listOpenerIndex - 1, $previousStatementPtr, true); - $arrayAccessEvidence = [ - T_VARIABLE, - T_CLOSE_SQUARE_BRACKET, - ]; - if ( - isset($tokens[$previousTokenPtr]) - && in_array($tokens[$previousTokenPtr]['code'], $arrayAccessEvidence, true) - ) { - return null; - } - // First find the end of the list $closePtr = null; if (isset($tokens[$listOpenerIndex]['parenthesis_closer'])) { @@ -735,6 +782,10 @@ public static function getListAssignments(File $phpcsFile, $listOpenerIndex) ++$currentPtr; } + if (! self::isListAssignment($phpcsFile, $listOpenerIndex)) { + return null; + } + return $variablePtrs; }