From 6d5ab56f285cfe0a5a0652775b8aae746a505e8a Mon Sep 17 00:00:00 2001 From: jrfnl Date: Fri, 11 Sep 2020 20:10:35 +0200 Subject: [PATCH] PassedParameters::hasParameters(): compatibility with the PHP 8 identifier name tokenization The `PassedParameters::hasParameters()` does a token based check on whether or not the passed token is one which is handled by this method, as well as the other methods in this class. That check now needs to take the PHP 8 identifier name tokens into account and should treat them as the same as `T_STRING`, i.e. as as suspected function call. This commit adjusts the `PassedParameters::hasParameters()` method to allow for these new tokens. Includes additional unit tests covering the different tokens. --- PHPCSUtils/Utils/PassedParameters.php | 31 +++++++++----- .../GetParameterCountTest.inc | 12 ++++++ .../GetParameterCountTest.php | 42 +++++++++++++++---- .../PassedParameters/HasParametersTest.inc | 13 ++++++ .../PassedParameters/HasParametersTest.php | 38 ++++++++++++++--- 5 files changed, 114 insertions(+), 22 deletions(-) diff --git a/PHPCSUtils/Utils/PassedParameters.php b/PHPCSUtils/Utils/PassedParameters.php index c813f6df..2dddcb16 100644 --- a/PHPCSUtils/Utils/PassedParameters.php +++ b/PHPCSUtils/Utils/PassedParameters.php @@ -13,6 +13,7 @@ use PHP_CodeSniffer\Exceptions\RuntimeException; use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Util\Tokens; +use PHPCSUtils\Tokens\Collections; use PHPCSUtils\Utils\Arrays; use PHPCSUtils\Utils\GetTokensAsString; @@ -64,7 +65,8 @@ class PassedParameters /** * Checks if any parameters have been passed. * - * - If passed a `T_STRING` or `T_VARIABLE` stack pointer, it will treat it as a function call. + * - If passed a `T_STRING`, `T_NAME_FULLY_QUALIFIED`, `T_NAME_RELATIVE`, `T_NAME_QUALIFIED` + * or `T_VARIABLE` stack pointer, it will treat it as a function call. * If a `T_STRING` or `T_VARIABLE` which is *not* a function call is passed, the behaviour is * undetermined. * - If passed a `T_SELF` or `T_STATIC` stack pointer, it will accept it as a @@ -75,10 +77,12 @@ class PassedParameters * language constructs have "parameters". * * @since 1.0.0 + * @since 1.0.0-alpha4 Added support for PHP 8.0 identifier name tokenization. * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. - * @param int $stackPtr The position of the `T_STRING`, `T_VARIABLE`, `T_ARRAY`, - * `T_OPEN_SHORT_ARRAY`, `T_ISSET`, or `T_UNSET` token. + * @param int $stackPtr The position of the `T_STRING`, PHP 8.0 identifier + * name token, `T_VARIABLE`, `T_ARRAY`, `T_OPEN_SHORT_ARRAY`, + * `T_ISSET`, or `T_UNSET` token. * * @return bool * @@ -89,7 +93,11 @@ public static function hasParameters(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); - if (isset($tokens[$stackPtr], self::$allowedConstructs[$tokens[$stackPtr]['code']]) === false) { + if (isset($tokens[$stackPtr]) === false + || (isset(self::$allowedConstructs[$tokens[$stackPtr]['code']]) === false + // Allow for the PHP 8.0 identifier name tokens. + && isset(Collections::nameTokens()[$tokens[$stackPtr]['code']]) === false) + ) { throw new RuntimeException( 'The hasParameters() method expects a function call, array, isset or unset token to be passed.' ); @@ -159,8 +167,9 @@ public static function hasParameters(File $phpcsFile, $stackPtr) * @since 1.0.0 * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. - * @param int $stackPtr The position of the `T_STRING`, `T_VARIABLE`, `T_ARRAY`, - * `T_OPEN_SHORT_ARRAY`, `T_ISSET`, or `T_UNSET` token. + * @param int $stackPtr The position of the `T_STRING`, PHP 8.0 identifier + * name token, `T_VARIABLE`, `T_ARRAY`, `T_OPEN_SHORT_ARRAY`, + * `T_ISSET`, or `T_UNSET` token. * * @return array A multi-dimentional array information on each parameter/array item. * The information gathered about each parameter/array item is in the following format: @@ -281,8 +290,9 @@ public static function getParameters(File $phpcsFile, $stackPtr) * @since 1.0.0 * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. - * @param int $stackPtr The position of the `T_STRING`, `T_VARIABLE`, `T_ARRAY`, - * `T_OPEN_SHORT_ARRAY`, `T_ISSET` or `T_UNSET` token. + * @param int $stackPtr The position of the `T_STRING`, PHP 8.0 identifier + * name token, `T_VARIABLE`, `T_ARRAY`, `T_OPEN_SHORT_ARRAY`, + * `T_ISSET`, or `T_UNSET` token. * @param int $paramOffset The 1-based index position of the parameter to retrieve. * * @return array|false Array with information on the parameter/array item at the specified offset. @@ -319,8 +329,9 @@ public static function getParameter(File $phpcsFile, $stackPtr, $paramOffset) * @since 1.0.0 * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. - * @param int $stackPtr The position of the `T_STRING`, `T_VARIABLE`, `T_ARRAY`, - * `T_OPEN_SHORT_ARRAY`, `T_ISSET` or `T_UNSET` token. + * @param int $stackPtr The position of the `T_STRING`, PHP 8.0 identifier + * name token, `T_VARIABLE`, `T_ARRAY`, `T_OPEN_SHORT_ARRAY`, + * `T_ISSET`, or `T_UNSET` token. * * @return int * diff --git a/Tests/Utils/PassedParameters/GetParameterCountTest.inc b/Tests/Utils/PassedParameters/GetParameterCountTest.inc index 63c66a72..b605ce01 100644 --- a/Tests/Utils/PassedParameters/GetParameterCountTest.inc +++ b/Tests/Utils/PassedParameters/GetParameterCountTest.inc @@ -150,6 +150,18 @@ json_encode(['a' => $a, 'b' => $b, 'c' => $c]); /* testFunctionCall47 */ json_encode(['a' => $a, 'b' => $b, 'c' => $c,] + ['c' => $c, 'd' => $d,]); +/* testFunctionCallFullyQualified */ +\myfunction( $a ); + +/* testFunctionCallFullyQualifiedWithNamespace */ +\My\Named\myfunction( $a ); + +/* testFunctionCallPartiallyQualified */ +Partially\Qualified\myfunction( $a ); + +/* testFunctionCallNamespaceOperator */ +namespace\myfunction( $a ); + /* testLongArray1 */ $foo = array( 1, 2, 3, 4, 5, 6, true ); diff --git a/Tests/Utils/PassedParameters/GetParameterCountTest.php b/Tests/Utils/PassedParameters/GetParameterCountTest.php index 82d57ed2..38364be5 100644 --- a/Tests/Utils/PassedParameters/GetParameterCountTest.php +++ b/Tests/Utils/PassedParameters/GetParameterCountTest.php @@ -11,6 +11,7 @@ namespace PHPCSUtils\Tests\Utils\PassedParameters; use PHPCSUtils\TestUtils\UtilityMethodTestCase; +use PHPCSUtils\Tokens\Collections; use PHPCSUtils\Utils\PassedParameters; /** @@ -32,17 +33,22 @@ class GetParameterCountTest extends UtilityMethodTestCase * * @dataProvider dataGetParameterCount * - * @param string $testMarker The comment which prefaces the target token in the test file. - * @param int $expected The expected parameter count. + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param int $expected The expected parameter count. + * @param string $targetContent Optional. The content of the target token to find. + * Defaults to null (ignore content). * * @return void */ - public function testGetParameterCount($testMarker, $expected) + public function testGetParameterCount($testMarker, $expected, $targetContent = null) { - $stackPtr = $this->getTargetToken( - $testMarker, - [\T_STRING, \T_ARRAY, \T_OPEN_SHORT_ARRAY, \T_ISSET, \T_UNSET] - ); + $targetTypes = Collections::nameTokens(); + $targetTypes[\T_ARRAY] = \T_ARRAY; + $targetTypes[\T_OPEN_SHORT_ARRAY] = \T_OPEN_SHORT_ARRAY; + $targetTypes[\T_ISSET] = \T_ISSET; + $targetTypes[\T_UNSET] = \T_UNSET; + + $stackPtr = $this->getTargetToken($testMarker, $targetTypes, $targetContent); $result = PassedParameters::getParameterCount(self::$phpcsFile, $stackPtr); $this->assertSame($expected, $result); } @@ -56,6 +62,8 @@ public function testGetParameterCount($testMarker, $expected) */ public function dataGetParameterCount() { + $php8Names = parent::usesPhp8NameTokens(); + return [ 'function-call-0' => [ '/* testFunctionCall0 */', @@ -249,6 +257,26 @@ public function dataGetParameterCount() '/* testFunctionCall47 */', 1, ], + 'function-call-fully-qualified' => [ + '/* testFunctionCallFullyQualified */', + 1, + ($php8Names === true) ? null : 'myfunction', + ], + 'function-call-fully-qualified-with-namespace' => [ + '/* testFunctionCallFullyQualifiedWithNamespace */', + 1, + ($php8Names === true) ? null : 'myfunction', + ], + 'function-call-partially-qualified' => [ + '/* testFunctionCallPartiallyQualified */', + 1, + ($php8Names === true) ? null : 'myfunction', + ], + 'function-call-namespace-operator' => [ + '/* testFunctionCallNamespaceOperator */', + 1, + ($php8Names === true) ? null : 'myfunction', + ], // Long arrays. 'long-array-1' => [ diff --git a/Tests/Utils/PassedParameters/HasParametersTest.inc b/Tests/Utils/PassedParameters/HasParametersTest.inc index 3c05a5c7..f814705c 100644 --- a/Tests/Utils/PassedParameters/HasParametersTest.inc +++ b/Tests/Utils/PassedParameters/HasParametersTest.inc @@ -42,6 +42,19 @@ class Bar { } } +/* testNoParamsFunctionCallFullyQualified */ +\myfunction( ); + +/* testHasParamsFunctionCallFullyQualifiedWithNamespace */ +\My\Named\myfunction( $a ); + +/* testNoParamsFunctionCallPartiallyQualified */ +Partially\Qualified\myfunction(); + +/* testHasParamsFunctionCallNamespaceOperator */ +namespace\myfunction( $a ); + + // Arrays: no parameters. /* testNoParamsLongArray1 */ diff --git a/Tests/Utils/PassedParameters/HasParametersTest.php b/Tests/Utils/PassedParameters/HasParametersTest.php index 36290a5e..0e74837b 100644 --- a/Tests/Utils/PassedParameters/HasParametersTest.php +++ b/Tests/Utils/PassedParameters/HasParametersTest.php @@ -93,15 +93,17 @@ public function testNotAShortArray() * * @dataProvider dataHasParameters * - * @param string $testMarker The comment which prefaces the target token in the test file. - * @param int|string $targetType The type of token to look for. - * @param bool $expected Whether or not the function/array has parameters/values. + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param int|string $targetType The type of token to look for. + * @param bool $expected Whether or not the function/array has parameters/values. + * @param string $targetContent Optional. The content of the target token to find. + * Defaults to null (ignore content). * * @return void */ - public function testHasParameters($testMarker, $targetType, $expected) + public function testHasParameters($testMarker, $targetType, $expected, $targetContent = null) { - $stackPtr = $this->getTargetToken($testMarker, $targetType); + $stackPtr = $this->getTargetToken($testMarker, $targetType, $targetContent); $result = PassedParameters::hasParameters(self::$phpcsFile, $stackPtr); $this->assertSame($expected, $result); } @@ -115,6 +117,8 @@ public function testHasParameters($testMarker, $targetType, $expected) */ public function dataHasParameters() { + $php8Names = parent::usesPhp8NameTokens(); + return [ // Function calls. 'no-params-function-call-1' => [ @@ -153,6 +157,30 @@ public function dataHasParameters() [\T_SELF, \T_STRING], true, ], + 'no-params-function-call-fully-qualified' => [ + '/* testNoParamsFunctionCallFullyQualified */', + ($php8Names === true) ? \T_NAME_FULLY_QUALIFIED : \T_STRING, + false, + ($php8Names === true) ? null : 'myfunction', + ], + 'has-params-function-call-fully-qualified-with-namespace' => [ + '/* testHasParamsFunctionCallFullyQualifiedWithNamespace */', + ($php8Names === true) ? \T_NAME_FULLY_QUALIFIED : \T_STRING, + true, + ($php8Names === true) ? null : 'myfunction', + ], + 'no-params-function-call-partially-qualified' => [ + '/* testNoParamsFunctionCallPartiallyQualified */', + ($php8Names === true) ? \T_NAME_QUALIFIED : \T_STRING, + false, + ($php8Names === true) ? null : 'myfunction', + ], + 'has-params-function-call-namespace-operator-relative' => [ + '/* testHasParamsFunctionCallNamespaceOperator */', + ($php8Names === true) ? \T_NAME_RELATIVE : \T_STRING, + true, + ($php8Names === true) ? null : 'myfunction', + ], // Arrays. 'no-params-long-array-1' => [