Skip to content

Commit

Permalink
Merge pull request #362 from PHPCSStandards/php-8.1/passedparams-prev…
Browse files Browse the repository at this point in the history
…ent-false-pos-first-class-callables

PHP 8.1 | PassedParameters::hasParameters(): prevent false positives for first class callable declarations
  • Loading branch information
jrfnl committed Oct 15, 2022
2 parents 81d22d0 + 97dc369 commit 7472c96
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 1 deletion.
7 changes: 6 additions & 1 deletion PHPCSUtils/Utils/PassedParameters.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class PassedParameters
*
* @since 1.0.0
* @since 1.0.0-alpha4 Added support for PHP 8.0 identifier name tokenization.
* @since 1.0.0-alpha4 Added defensive coding against PHP 8.1 first class callables
* being passed as if they were function calls.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
* @param int $stackPtr The position of function call name,
Expand Down Expand Up @@ -138,8 +140,11 @@ public static function hasParameters(File $phpcsFile, $stackPtr, $isShortArray =
return false;
}

$ignore = Tokens::$emptyTokens;
$ignore[\T_ELLIPSIS] = \T_ELLIPSIS; // Prevent PHP 8.1 first class callables from being seen as function calls.

$closeParenthesis = $tokens[$next]['parenthesis_closer'];
$nextNextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), ($closeParenthesis + 1), true);
$nextNextNonEmpty = $phpcsFile->findNext($ignore, ($next + 1), ($closeParenthesis + 1), true);

if ($nextNextNonEmpty === $closeParenthesis) {
// No parameters.
Expand Down
11 changes: 11 additions & 0 deletions Tests/Utils/PassedParameters/HasParametersTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,17 @@ $anon = new class() {};
/* testHasParamsAnonClass */
$anon = new class( $param1, $param2 ) {};

/* testPHP81FirstClassCallableNotFunctionCallGlobalFunction */
$fn = strlen(...);

/* testPHP81FirstClassCallableNotFunctionCallOOMethod */
$fn = $this->method(
...
);

/* testPHP81FirstClassCallableNotFunctionCallVariableStaticOOMethod */
$fn = $name1::$name2( /*comment*/ ...);

// Intentional parse error.
/* testNoCloseParenthesis */
$array = array(1, 2, 3
Expand Down
18 changes: 18 additions & 0 deletions Tests/Utils/PassedParameters/HasParametersTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,24 @@ public function dataHasParameters()
'expected' => true,
],

// PHP 8.1 first class callables are callbacks, not function calls.
'no-params-php81-first-class-callable-global-function' => [
'testMarker' => '/* testPHP81FirstClassCallableNotFunctionCallGlobalFunction */',
'targetType' => \T_STRING,
'expected' => false,
],
'no-params-php81-first-class-callable-oo-method' => [
'testMarker' => '/* testPHP81FirstClassCallableNotFunctionCallOOMethod */',
'targetType' => \T_STRING,
'expected' => false,
],
'no-params-php81-first-class-callable-variable-static-oo-method' => [
'testMarker' => '/* testPHP81FirstClassCallableNotFunctionCallVariableStaticOOMethod */',
'targetType' => \T_VARIABLE,
'expected' => false,
'targetContent' => '$name2',
],

// Defensive coding against parse errors and live coding.
'defense-in-depth-no-close-parens' => [
'testMarker' => '/* testNoCloseParenthesis */',
Expand Down

0 comments on commit 7472c96

Please sign in to comment.