diff --git a/Tests/VariableAnalysisSniff/IssetTest.php b/Tests/VariableAnalysisSniff/IssetTest.php new file mode 100644 index 00000000..736dcf97 --- /dev/null +++ b/Tests/VariableAnalysisSniff/IssetTest.php @@ -0,0 +1,18 @@ +getFixture('IssetFixture.php'); + $phpcsFile = $this->prepareLocalFileForSniffs($fixtureFile); + $phpcsFile->process(); + $lines = $this->getWarningLineNumbersFromFile($phpcsFile); + $expectedWarnings = [ + 4, + 23, // ideally this should not be a warning, but will be because it is difficult to know: https://github.com/sirbrillig/phpcs-variable-analysis/issues/202#issuecomment-688507314 + ]; + $this->assertEquals($expectedWarnings, $lines); + } +} diff --git a/Tests/VariableAnalysisSniff/fixtures/IssetFixture.php b/Tests/VariableAnalysisSniff/fixtures/IssetFixture.php new file mode 100644 index 00000000..e7c00495 --- /dev/null +++ b/Tests/VariableAnalysisSniff/fixtures/IssetFixture.php @@ -0,0 +1,37 @@ +getTokens(); + $token = $tokens[$stackPtr]; + if (empty($token['nested_parenthesis'])) { + return null; + } + $startingParenthesis = array_keys($token['nested_parenthesis']); + $startOfArguments = end($startingParenthesis); + if (! is_int($startOfArguments)) { + return null; + } + + $nonFunctionTokenTypes = array_values(Tokens::$emptyTokens); + $functionPtr = self::getIntOrNull($phpcsFile->findPrevious($nonFunctionTokenTypes, $startOfArguments - 1, null, true, null, true)); + if (! is_int($functionPtr) || ! isset($tokens[$functionPtr]['code'])) { + return null; + } + if ($tokens[$functionPtr]['code'] === 'function' || ($tokens[$functionPtr]['content'] === 'fn' && FunctionDeclarations::isArrowFunction($phpcsFile, $functionPtr))) { + return null; + } + return $functionPtr; + } + + /** + * @param File $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + public static function isVariableInsideIssetOrEmpty(File $phpcsFile, $stackPtr) { + $functionIndex = self::getFunctionIndexForFunctionCallArgument($phpcsFile, $stackPtr); + if (! is_int($functionIndex)) { + return false; + } + $tokens = $phpcsFile->getTokens(); + if (! isset($tokens[$functionIndex])) { + return false; + } + $allowedFunctionNames = [ + 'isset', + 'empty', + ]; + if (in_array($tokens[$functionIndex]['content'], $allowedFunctionNames, true)) { + return true; + } + return false; + } } diff --git a/VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php b/VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php index d04d0917..186b4c1d 100644 --- a/VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php +++ b/VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php @@ -1440,6 +1440,13 @@ protected function processVariable(File $phpcsFile, $stackPtr) { return; } + // Are we an isset or empty call? + if (Helpers::isVariableInsideIssetOrEmpty($phpcsFile, $stackPtr)) { + Helpers::debug('found isset or empty'); + $this->markVariableRead($varName, $stackPtr, $currScope); + return; + } + // OK, we don't appear to be a write to the var, assume we're a read. Helpers::debug('looks like a variable read'); $this->markVariableReadAndWarnIfUndefined($phpcsFile, $varName, $stackPtr, $currScope);