From c58e746ec7a68903c346a64401275823d3e94aec Mon Sep 17 00:00:00 2001 From: Greg Sherwood Date: Wed, 20 Nov 2019 08:36:50 +1100 Subject: [PATCH] Fixed detection of T_NULLABLE for arrow functions (ref #2708) --- .../Tests/PHP/DisallowInlineIfUnitTest.inc | 2 ++ src/Tokenizers/PHP.php | 12 ++++++-- tests/Core/Tokenizer/BackfillFnTokenTest.inc | 3 ++ tests/Core/Tokenizer/BackfillFnTokenTest.php | 28 +++++++++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/Standards/Squiz/Tests/PHP/DisallowInlineIfUnitTest.inc b/src/Standards/Squiz/Tests/PHP/DisallowInlineIfUnitTest.inc index b444b08723..07ddec2bfd 100644 --- a/src/Standards/Squiz/Tests/PHP/DisallowInlineIfUnitTest.inc +++ b/src/Standards/Squiz/Tests/PHP/DisallowInlineIfUnitTest.inc @@ -12,3 +12,5 @@ function foo(string $bar, array $baz, ?MyClass $object) : MyClass {} class Example { public ?int $scalarType; } + +$a = fn(?\DateTime $x) : ?\DateTime => $x; diff --git a/src/Tokenizers/PHP.php b/src/Tokenizers/PHP.php index 9b959da019..8b9114e319 100644 --- a/src/Tokenizers/PHP.php +++ b/src/Tokenizers/PHP.php @@ -1073,6 +1073,7 @@ protected function tokenize($string) } if ($tokenType === T_FUNCTION + || $tokenType === T_FN || isset(Util\Tokens::$methodPrefixes[$tokenType]) === true ) { if (PHP_CODESNIFFER_VERBOSITY > 1) { @@ -1285,6 +1286,11 @@ function return types. We want to keep the parenthesis map clean, && $token[0] === T_STRING && strtolower($token[1]) === 'fn' ) { + // Modify the original token stack so that + // future checks (like looking for T_NULLABLE) can + // detect the T_FN token more easily. + $tokens[$stackPtr][0] = T_FN; + $finalTokens[$newStackPtr] = [ 'content' => $token[1], 'code' => T_FN, @@ -1702,8 +1708,10 @@ protected function processAdditional() if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS) { $ignore = Util\Tokens::$emptyTokens; $ignore += [ - T_STRING => T_STRING, - T_COLON => T_COLON, + T_STRING => T_STRING, + T_COLON => T_COLON, + T_NS_SEPARATOR => T_COLON, + T_NULLABLE => T_COLON, ]; $closer = $this->tokens[$x]['parenthesis_closer']; diff --git a/tests/Core/Tokenizer/BackfillFnTokenTest.inc b/tests/Core/Tokenizer/BackfillFnTokenTest.inc index ecdba61552..024299b158 100644 --- a/tests/Core/Tokenizer/BackfillFnTokenTest.inc +++ b/tests/Core/Tokenizer/BackfillFnTokenTest.inc @@ -54,3 +54,6 @@ $a = [ /* testYield */ $a = fn($x) => yield 'k' => $x; + +/* testNullableNamespace */ +$a = fn(?\DateTime $x) : ?\DateTime => $x; diff --git a/tests/Core/Tokenizer/BackfillFnTokenTest.php b/tests/Core/Tokenizer/BackfillFnTokenTest.php index 4527b8bb6c..98ff709d40 100644 --- a/tests/Core/Tokenizer/BackfillFnTokenTest.php +++ b/tests/Core/Tokenizer/BackfillFnTokenTest.php @@ -431,6 +431,34 @@ public function testYield() }//end testYield() + /** + * Test arrow functions that use nullable namespace types. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testNullableNamespace() + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken('/* testNullableNamespace */', T_FN); + $this->backfillHelper($token); + + $this->assertSame($tokens[$token]['scope_opener'], ($token + 15), 'Scope opener is not the arrow token'); + $this->assertSame($tokens[$token]['scope_closer'], ($token + 18), 'Scope closer is not the semicolon token'); + + $opener = $tokens[$token]['scope_opener']; + $this->assertSame($tokens[$opener]['scope_opener'], ($token + 15), 'Opener scope opener is not the arrow token'); + $this->assertSame($tokens[$opener]['scope_closer'], ($token + 18), 'Opener scope closer is not the semicolon token'); + + $closer = $tokens[$token]['scope_opener']; + $this->assertSame($tokens[$closer]['scope_opener'], ($token + 15), 'Closer scope opener is not the arrow token'); + $this->assertSame($tokens[$closer]['scope_closer'], ($token + 18), 'Closer scope closer is not the semicolon token'); + + }//end testNullableNamespace() + + /** * Test that anonymous class tokens without parenthesis do not get assigned a parenthesis owner. *