Skip to content

Commit 3a0b6e4

Browse files
committed
Replace findFunctionCallArguments custom loop with PassedParameters::getParameters()
Delegates argument parsing to PHPCSUtils which handles named params (PHP 8+), closures, attributes, and has internal caching. Updates getVariablesInsideCompact() and processVariableAsPassByReferenceFunctionCall() to work with the PassedParameters ['start','end'] format; argument positions are now 1-based integers matching PHPCSUtils conventions. Removes stale Psalm baseline entries for the deleted $closePtr code.
1 parent 5b97e89 commit 3a0b6e4

3 files changed

Lines changed: 38 additions & 60 deletions

File tree

VariableAnalysis/Lib/Helpers.php

Lines changed: 33 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use PHPCSUtils\Utils\FunctionDeclarations;
1616
use PHPCSUtils\Utils\Lists;
1717
use PHPCSUtils\Utils\Parentheses;
18+
use PHPCSUtils\Utils\PassedParameters;
1819

1920
class Helpers
2021
{
@@ -336,7 +337,7 @@ public static function findFunctionCall(File $phpcsFile, $stackPtr)
336337
* @param File $phpcsFile
337338
* @param int $stackPtr
338339
*
339-
* @return array<int, array<int>>
340+
* @return array<int|string, array<string, int|string>>
340341
*/
341342
public static function findFunctionCallArguments(File $phpcsFile, $stackPtr)
342343
{
@@ -351,38 +352,7 @@ public static function findFunctionCallArguments(File $phpcsFile, $stackPtr)
351352
}
352353
}
353354

354-
// $stackPtr is the function name, find our brackets after it
355-
$openPtr = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
356-
if (($openPtr === false) || ($tokens[$openPtr]['code'] !== T_OPEN_PARENTHESIS)) {
357-
return [];
358-
}
359-
360-
if (!isset($tokens[$openPtr]['parenthesis_closer'])) {
361-
return [];
362-
}
363-
$closePtr = $tokens[$openPtr]['parenthesis_closer'];
364-
365-
$argPtrs = [];
366-
$lastPtr = $openPtr;
367-
$lastArgComma = $openPtr;
368-
$nextPtr = $phpcsFile->findNext([T_COMMA], $lastPtr + 1, $closePtr);
369-
while (is_int($nextPtr)) {
370-
if (self::findContainingOpeningBracket($phpcsFile, $nextPtr) === $openPtr) {
371-
// Comma is at our level of brackets, it's an argument delimiter.
372-
$range = range($lastArgComma + 1, $nextPtr - 1);
373-
array_push($argPtrs, $range);
374-
$lastArgComma = $nextPtr;
375-
}
376-
$lastPtr = $nextPtr;
377-
$nextPtr = $phpcsFile->findNext([T_COMMA], $lastPtr + 1, $closePtr);
378-
}
379-
$range = range($lastArgComma + 1, $closePtr - 1);
380-
$range = array_filter($range, function ($element) {
381-
return is_int($element);
382-
});
383-
array_push($argPtrs, $range);
384-
385-
return $argPtrs;
355+
return PassedParameters::getParameters($phpcsFile, $stackPtr);
386356
}
387357

388358
/**
@@ -457,9 +427,9 @@ public static function findVariableScope(File $phpcsFile, $stackPtr, $varName =
457427
/**
458428
* Return the variable names and positions of each variable targetted by a `compact()` call.
459429
*
460-
* @param File $phpcsFile
461-
* @param int $stackPtr
462-
* @param array<int, array<int>> $arguments The stack pointers of each argument; see findFunctionCallArguments
430+
* @param File $phpcsFile
431+
* @param int $stackPtr
432+
* @param array<int|string, array<string, int|string>> $arguments The parameters from PassedParameters::getParameters()
463433
*
464434
* @return array<VariableInfo> each variable's firstRead position and its name; other VariableInfo properties are not set!
465435
*/
@@ -468,36 +438,50 @@ public static function getVariablesInsideCompact(File $phpcsFile, $stackPtr, $ar
468438
$tokens = $phpcsFile->getTokens();
469439
$variablePositionsAndNames = [];
470440

471-
foreach ($arguments as $argumentPtrs) {
472-
$argumentPtrs = array_values(array_filter($argumentPtrs, function ($argumentPtr) use ($tokens) {
473-
return isset(Tokens::$emptyTokens[$tokens[$argumentPtr]['code']]) === false;
474-
}));
475-
if (empty($argumentPtrs)) {
476-
continue;
441+
foreach ($arguments as $param) {
442+
// Find the first non-empty token in this argument's range.
443+
$firstNonEmpty = null;
444+
$nonEmptyCount = 0;
445+
for ($i = (int)$param['start']; $i <= (int)$param['end']; $i++) {
446+
if (!isset(Tokens::$emptyTokens[$tokens[$i]['code']])) {
447+
if ($firstNonEmpty === null) {
448+
$firstNonEmpty = $i;
449+
}
450+
$nonEmptyCount++;
451+
}
477452
}
478-
if (!isset($tokens[$argumentPtrs[0]])) {
453+
454+
if ($firstNonEmpty === null) {
479455
continue;
480456
}
481-
$argumentFirstToken = $tokens[$argumentPtrs[0]];
457+
458+
$argumentFirstToken = $tokens[$firstNonEmpty];
459+
482460
if ($argumentFirstToken['code'] === T_ARRAY) {
483461
// It's an array argument, recurse.
484-
$arrayArguments = self::findFunctionCallArguments($phpcsFile, $argumentPtrs[0]);
485-
$variablePositionsAndNames = array_merge($variablePositionsAndNames, self::getVariablesInsideCompact($phpcsFile, $stackPtr, $arrayArguments));
462+
$arrayArguments = PassedParameters::getParameters($phpcsFile, $firstNonEmpty);
463+
$variablePositionsAndNames = array_merge(
464+
$variablePositionsAndNames,
465+
self::getVariablesInsideCompact($phpcsFile, $stackPtr, $arrayArguments)
466+
);
486467
continue;
487468
}
488-
if (count($argumentPtrs) > 1) {
469+
470+
if ($nonEmptyCount > 1) {
489471
// Complex argument, we can't handle it, ignore.
490472
continue;
491473
}
474+
492475
if ($argumentFirstToken['code'] === T_CONSTANT_ENCAPSED_STRING) {
493476
// Single-quoted string literal, ie compact('whatever').
494477
// Substr is to strip the enclosing single-quotes.
495478
$varName = substr($argumentFirstToken['content'], 1, -1);
496479
$variable = new VariableInfo($varName);
497-
$variable->firstRead = $argumentPtrs[0];
480+
$variable->firstRead = $firstNonEmpty;
498481
$variablePositionsAndNames[] = $variable;
499482
continue;
500483
}
484+
501485
if ($argumentFirstToken['code'] === T_DOUBLE_QUOTED_STRING) {
502486
// Double-quoted string literal.
503487
$regexp = Constants::getDoubleQuotedVarRegexp();
@@ -508,9 +492,8 @@ public static function getVariablesInsideCompact(File $phpcsFile, $stackPtr, $ar
508492
// Substr is to strip the enclosing double-quotes.
509493
$varName = substr($argumentFirstToken['content'], 1, -1);
510494
$variable = new VariableInfo($varName);
511-
$variable->firstRead = $argumentPtrs[0];
495+
$variable->firstRead = $firstNonEmpty;
512496
$variablePositionsAndNames[] = $variable;
513-
continue;
514497
}
515498
}
516499
return $variablePositionsAndNames;

VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,9 +1549,9 @@ protected function processVariableAsPassByReferenceFunctionCall(File $phpcsFile,
15491549

15501550
// We're within a function call arguments list, find which arg we are.
15511551
$argPos = false;
1552-
foreach ($argPtrs as $idx => $ptrs) {
1553-
if (in_array($stackPtr, $ptrs)) {
1554-
$argPos = $idx + 1;
1552+
foreach ($argPtrs as $idx => $param) {
1553+
if ($stackPtr >= $param['start'] && $stackPtr <= $param['end']) {
1554+
$argPos = $idx;
15551555
break;
15561556
}
15571557
}
@@ -1572,7 +1572,8 @@ protected function processVariableAsPassByReferenceFunctionCall(File $phpcsFile,
15721572

15731573
// Our argument position matches that of a pass-by-ref argument,
15741574
// check that we're the only part of the argument expression.
1575-
foreach ($argPtrs[$argPos - 1] as $ptr) {
1575+
$param = $argPtrs[$argPos];
1576+
for ($ptr = (int)$param['start']; $ptr <= (int)$param['end']; $ptr++) {
15761577
if ($ptr === $stackPtr) {
15771578
continue;
15781579
}

psalm-baseline.xml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,6 @@
1515
</PossiblyUnusedProperty>
1616
</file>
1717
<file src="VariableAnalysis/Lib/Helpers.php">
18-
<InvalidArgument>
19-
<code><![CDATA[$closePtr]]></code>
20-
</InvalidArgument>
21-
<InvalidOperand>
22-
<code><![CDATA[$closePtr]]></code>
23-
</InvalidOperand>
2418
<PossiblyFalseArgument>
2519
<code><![CDATA[$varName]]></code>
2620
<code><![CDATA[$varName]]></code>

0 commit comments

Comments
 (0)