Skip to content

Commit f5df93f

Browse files
committed
Update sniff to handle T_NAME... tokens for functions
1 parent 18873d5 commit f5df93f

File tree

2 files changed

+92
-9
lines changed

2 files changed

+92
-9
lines changed

VariableAnalysis/Lib/Helpers.php

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public static function findContainingOpeningBracket(File $phpcsFile, $stackPtr)
9393
$tokens = $phpcsFile->getTokens();
9494
if (isset($tokens[$stackPtr]['nested_parenthesis'])) {
9595
/**
96-
* @var array<int|string|null>
96+
* @var list<int|string>
9797
*/
9898
$openPtrs = array_keys($tokens[$stackPtr]['nested_parenthesis']);
9999
return (int)end($openPtrs);
@@ -319,8 +319,22 @@ public static function findFunctionCall(File $phpcsFile, $stackPtr)
319319
if (is_int($openPtr)) {
320320
// First non-whitespace thing and see if it's a T_STRING function name
321321
$functionPtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $openPtr - 1, null, true, null, true);
322-
if (is_int($functionPtr) && $tokens[$functionPtr]['code'] === T_STRING) {
323-
return $functionPtr;
322+
if (is_int($functionPtr)) {
323+
$functionTokenCode = $tokens[$functionPtr]['code'];
324+
// In PHPCS 4.x, function names can be T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, or T_NAME_RELATIVE
325+
$validFunctionTokens = [T_STRING];
326+
if (defined('T_NAME_FULLY_QUALIFIED')) {
327+
$validFunctionTokens[] = T_NAME_FULLY_QUALIFIED;
328+
}
329+
if (defined('T_NAME_QUALIFIED')) {
330+
$validFunctionTokens[] = T_NAME_QUALIFIED;
331+
}
332+
if (defined('T_NAME_RELATIVE')) {
333+
$validFunctionTokens[] = T_NAME_RELATIVE;
334+
}
335+
if (in_array($functionTokenCode, $validFunctionTokens, true)) {
336+
return $functionPtr;
337+
}
324338
}
325339
}
326340
return null;
@@ -364,9 +378,6 @@ public static function findFunctionCallArguments(File $phpcsFile, $stackPtr)
364378
if (self::findContainingOpeningBracket($phpcsFile, $nextPtr) === $openPtr) {
365379
// Comma is at our level of brackets, it's an argument delimiter.
366380
$range = range($lastArgComma + 1, $nextPtr - 1);
367-
$range = array_filter($range, function ($element) {
368-
return is_int($element);
369-
});
370381
array_push($argPtrs, $range);
371382
$lastArgComma = $nextPtr;
372383
}
@@ -394,7 +405,8 @@ public static function getNextAssignPointer(File $phpcsFile, $stackPtr)
394405

395406
// Is the next non-whitespace an assignment?
396407
$nextPtr = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
397-
if (is_int($nextPtr)
408+
if (
409+
is_int($nextPtr)
398410
&& isset(Tokens::$assignmentTokens[$tokens[$nextPtr]['code']])
399411
// Ignore double arrow to prevent triggering on `foreach ( $array as $k => $v )`.
400412
&& $tokens[$nextPtr]['code'] !== T_DOUBLE_ARROW
@@ -549,6 +561,16 @@ public static function findVariableScopeExceptArrowFunctions(File $phpcsFile, $s
549561
T_HEREDOC,
550562
T_STRING,
551563
];
564+
// In PHPCS 4.x, function names can be T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, or T_NAME_RELATIVE
565+
if (defined('T_NAME_FULLY_QUALIFIED')) {
566+
$allowedTypes[] = T_NAME_FULLY_QUALIFIED;
567+
}
568+
if (defined('T_NAME_QUALIFIED')) {
569+
$allowedTypes[] = T_NAME_QUALIFIED;
570+
}
571+
if (defined('T_NAME_RELATIVE')) {
572+
$allowedTypes[] = T_NAME_RELATIVE;
573+
}
552574
if (! in_array($tokens[$stackPtr]['code'], $allowedTypes, true)) {
553575
throw new \Exception("Cannot find variable scope for non-variable {$tokens[$stackPtr]['type']}");
554576
}
@@ -1290,7 +1312,7 @@ public static function getFunctionIndexForFunctionCallArgument(File $phpcsFile,
12901312
return null;
12911313
}
12921314
/**
1293-
* @var array<int|string|null>
1315+
* @var list<int|string>
12941316
*/
12951317
$startingParenthesis = array_keys($token['nested_parenthesis']);
12961318
$startOfArguments = end($startingParenthesis);
@@ -1681,9 +1703,30 @@ public static function getFunctionNameWithNamespace(File $phpcsFile, $stackPtr)
16811703
$startOfScope = self::findVariableScope($phpcsFile, $stackPtr);
16821704
$functionName = $tokens[$stackPtr]['content'];
16831705

1706+
// In PHPCS 4.x, T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, and T_NAME_RELATIVE
1707+
// tokens already contain the full namespaced name, so we can return early.
1708+
if (defined('T_NAME_FULLY_QUALIFIED') && $tokens[$stackPtr]['code'] === T_NAME_FULLY_QUALIFIED) {
1709+
return $functionName;
1710+
}
1711+
if (defined('T_NAME_QUALIFIED') && $tokens[$stackPtr]['code'] === T_NAME_QUALIFIED) {
1712+
return $functionName;
1713+
}
1714+
if (defined('T_NAME_RELATIVE') && $tokens[$stackPtr]['code'] === T_NAME_RELATIVE) {
1715+
return $functionName;
1716+
}
1717+
16841718
// Move backwards from the token, collecting namespace separators and
16851719
// strings, until we encounter whitespace or something else.
16861720
$partOfNamespace = [T_NS_SEPARATOR, T_STRING];
1721+
if (defined('T_NAME_QUALIFIED')) {
1722+
$partOfNamespace[] = T_NAME_QUALIFIED;
1723+
}
1724+
if (defined('T_NAME_RELATIVE')) {
1725+
$partOfNamespace[] = T_NAME_RELATIVE;
1726+
}
1727+
if (defined('T_NAME_FULLY_QUALIFIED')) {
1728+
$partOfNamespace[] = T_NAME_FULLY_QUALIFIED;
1729+
}
16871730
for ($i = $stackPtr - 1; $i > $startOfScope; $i--) {
16881731
if (! in_array($tokens[$i]['code'], $partOfNamespace, true)) {
16891732
break;
@@ -1708,6 +1751,15 @@ private static function isTokenPossiblyPartOfTypehint(File $phpcsFile, $stackPtr
17081751
if ($token['code'] === 'PHPCS_T_NULLABLE') {
17091752
return true;
17101753
}
1754+
if (defined('T_NAME_QUALIFIED') && $token['code'] === T_NAME_QUALIFIED) {
1755+
return true;
1756+
}
1757+
if (defined('T_NAME_RELATIVE') && $token['code'] === T_NAME_RELATIVE) {
1758+
return true;
1759+
}
1760+
if (defined('T_NAME_FULLY_QUALIFIED') && $token['code'] === T_NAME_FULLY_QUALIFIED) {
1761+
return true;
1762+
}
17111763
if ($token['code'] === T_NS_SEPARATOR) {
17121764
return true;
17131765
}

VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1503,7 +1503,38 @@ protected function processVariableAsPassByReferenceFunctionCall(File $phpcsFile,
15031503

15041504
// Is our function a known pass-by-reference function?
15051505
$functionName = $tokens[$functionPtr]['content'];
1506-
$refArgs = $this->getPassByReferenceFunction($functionName);
1506+
1507+
// In PHPCS 4.x, T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, and T_NAME_RELATIVE
1508+
// tokens contain the full namespaced name. Extract just the base name for the
1509+
// first check so that 'my_function' in the config can match '\My\Namespace\my_function'.
1510+
$functionBaseName = $functionName;
1511+
if (defined('T_NAME_FULLY_QUALIFIED') && $tokens[$functionPtr]['code'] === T_NAME_FULLY_QUALIFIED) {
1512+
$lastBackslashPos = strrpos($functionName, '\\');
1513+
if ($lastBackslashPos !== false) {
1514+
$baseName = substr($functionName, $lastBackslashPos + 1);
1515+
if ($baseName !== false) {
1516+
$functionBaseName = $baseName;
1517+
}
1518+
}
1519+
} elseif (defined('T_NAME_QUALIFIED') && $tokens[$functionPtr]['code'] === T_NAME_QUALIFIED) {
1520+
$lastBackslashPos = strrpos($functionName, '\\');
1521+
if ($lastBackslashPos !== false) {
1522+
$baseName = substr($functionName, $lastBackslashPos + 1);
1523+
if ($baseName !== false) {
1524+
$functionBaseName = $baseName;
1525+
}
1526+
}
1527+
} elseif (defined('T_NAME_RELATIVE') && $tokens[$functionPtr]['code'] === T_NAME_RELATIVE) {
1528+
$lastBackslashPos = strrpos($functionName, '\\');
1529+
if ($lastBackslashPos !== false) {
1530+
$baseName = substr($functionName, $lastBackslashPos + 1);
1531+
if ($baseName !== false) {
1532+
$functionBaseName = $baseName;
1533+
}
1534+
}
1535+
}
1536+
1537+
$refArgs = $this->getPassByReferenceFunction($functionBaseName);
15071538
if (! $refArgs) {
15081539
// Check again with the fully namespaced function name.
15091540
$functionName = Helpers::getFunctionNameWithNamespace($phpcsFile, $functionPtr);

0 commit comments

Comments
 (0)