From df4a8121227599c8f280d1f19f408a5c8d52feec Mon Sep 17 00:00:00 2001 From: jrfnl Date: Tue, 10 Jun 2025 06:45:50 +0200 Subject: [PATCH] Tokenizer/PHP: bug fix for missing parenthesis owner for method named "fn" When a function is named "fn", the `function` keyword should be set as the parenthesis owner. While this works on PHPCS 3.x, I found while making PHPCSUtils compatible with 4.x, that this was broken on PHPCS 4.x for PHP 7.2 and 7.3 since commit da0d2247ec80bfb89a363dff905251fbaa754505. This was due to token being seen as a `T_FN` token after the initial tokenization fixing round, so the `Tokenizer::createTokenMap()` would receive the `T_FN` token and not set the parenthesis owner index. The additional token processing run after that, would realize this is not in actual fact an arrow function and undo the retokenization (from `T_FN` back to `T_STRING`), which leaves the parentheses for this function declaration without an owner. Fixed now by preventing the initial `T_STRING` to `T_FN` retokenization if the keyword is used in a context where the token should always be a `T_STRING`. --- src/Tokenizers/PHP.php | 2 ++ .../Tokenizer/CreateTokenMapParenthesesTest.inc | 10 ++++++++-- .../Tokenizer/CreateTokenMapParenthesesTest.php | 14 +++++++++++--- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/Tokenizers/PHP.php b/src/Tokenizers/PHP.php index 192fbc3de9..3246f8654e 100644 --- a/src/Tokenizers/PHP.php +++ b/src/Tokenizers/PHP.php @@ -2217,6 +2217,8 @@ protected function tokenize($string) if ($tokenIsArray === true && $token[0] === T_STRING && strtolower($token[1]) === 'fn' + && isset(static::T_STRING_CONTEXTS[$finalTokens[$lastNotEmptyToken]['code']]) === false + && $finalTokens[$lastNotEmptyToken]['content'] !== '&' ) { // Modify the original token stack so that // future checks (like looking for T_NULLABLE) can diff --git a/tests/Core/Tokenizers/Tokenizer/CreateTokenMapParenthesesTest.inc b/tests/Core/Tokenizers/Tokenizer/CreateTokenMapParenthesesTest.inc index 9cdc341797..853e94cedc 100644 --- a/tests/Core/Tokenizers/Tokenizer/CreateTokenMapParenthesesTest.inc +++ b/tests/Core/Tokenizers/Tokenizer/CreateTokenMapParenthesesTest.inc @@ -60,8 +60,14 @@ function name($a, $b/* testFunctionParenthesesCloser */) {} function &returnByRef ($a, $b/* testFunctionParenthesesCloserReturnByRef */) {} class ReservedKeyword { - /* testFunctionParenthesesOwnerKeywordName */ - public function match($a, $b/* testFunctionParenthesesCloserKeywordName */) {} + /* testFunctionParenthesesOwnerKeywordNameMatch */ + public function match($a, $b/* testFunctionParenthesesCloserKeywordNameMatch */) {} + + /* testFunctionParenthesesOwnerKeywordNameFn */ + public function fn($a, $b/* testFunctionParenthesesCloserKeywordNameFn */) {} + + /* testFunctionParenthesesOwnerKeywordNameFnReturnByRef */ + public function &fn($a, $b/* testFunctionParenthesesCloserKeywordNameFnReturnByRef */) {} } /* testClosureParenthesesOwner */ diff --git a/tests/Core/Tokenizers/Tokenizer/CreateTokenMapParenthesesTest.php b/tests/Core/Tokenizers/Tokenizer/CreateTokenMapParenthesesTest.php index 39c91fa879..f4068b5047 100644 --- a/tests/Core/Tokenizers/Tokenizer/CreateTokenMapParenthesesTest.php +++ b/tests/Core/Tokenizers/Tokenizer/CreateTokenMapParenthesesTest.php @@ -139,8 +139,16 @@ public static function dataParenthesesWithOwner() 'testMarker' => '/* testFunctionParenthesesOwnerReturnByRef */', 'tokenCode' => T_FUNCTION, ], - 'function declaration, keyword as function name' => [ - 'testMarker' => '/* testFunctionParenthesesOwnerKeywordName */', + 'function declaration, "match" as function name' => [ + 'testMarker' => '/* testFunctionParenthesesOwnerKeywordNameMatch */', + 'tokenCode' => T_FUNCTION, + ], + 'function declaration, "fn" as function name' => [ + 'testMarker' => '/* testFunctionParenthesesOwnerKeywordNameFn */', + 'tokenCode' => T_FUNCTION, + ], + 'function declaration, "&fn" as function name' => [ + 'testMarker' => '/* testFunctionParenthesesOwnerKeywordNameFnReturnByRef */', 'tokenCode' => T_FUNCTION, ], 'closure declaration' => [ @@ -276,7 +284,7 @@ public static function dataParenthesesWithoutOwner() /** - * Test parentheses owner tokens when used without parentheses (where possible) do *not* the "parenthesis_*" token indexes set. + * Test parentheses owner tokens when used without parentheses (where possible) do *not* have the "parenthesis_*" token indexes set. * * @param string $testMarker The comment prefacing the target token. * @param int|string $tokenCode The token code to look for.