From cf69afb6379445d94e68352eadcc95b1c4954bc6 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 24 Dec 2020 19:39:58 +0100 Subject: [PATCH] PHP 8.0 | Squiz/ScopeKeywordSpacing: fix false positive on static as return type This adds some additional safeguards to the sniff to prevent it from triggering when `static` is used in a return type declaration, as allowed since PHP 8.0. Includes unit tests. Fixes 3188 --- .../WhiteSpace/ScopeKeywordSpacingSniff.php | 34 +++++++++++++++---- .../ScopeKeywordSpacingUnitTest.inc | 16 ++++++++- .../ScopeKeywordSpacingUnitTest.inc.fixed | 14 ++++++++ .../ScopeKeywordSpacingUnitTest.php | 1 + 4 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/Standards/Squiz/Sniffs/WhiteSpace/ScopeKeywordSpacingSniff.php b/src/Standards/Squiz/Sniffs/WhiteSpace/ScopeKeywordSpacingSniff.php index e0fab85a8d..de92697db0 100644 --- a/src/Standards/Squiz/Sniffs/WhiteSpace/ScopeKeywordSpacingSniff.php +++ b/src/Standards/Squiz/Sniffs/WhiteSpace/ScopeKeywordSpacingSniff.php @@ -51,13 +51,33 @@ public function process(File $phpcsFile, $stackPtr) $prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); $nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); - if ($tokens[$stackPtr]['code'] === T_STATIC - && (($nextToken === false || $tokens[$nextToken]['code'] === T_DOUBLE_COLON) - || $tokens[$prevToken]['code'] === T_NEW) - ) { - // Late static binding, e.g., static:: OR new static() usage or live coding. - return; - } + if ($tokens[$stackPtr]['code'] === T_STATIC) { + if (($nextToken === false || $tokens[$nextToken]['code'] === T_DOUBLE_COLON) + || $tokens[$prevToken]['code'] === T_NEW + ) { + // Late static binding, e.g., static:: OR new static() usage or live coding. + return; + } + + if ($prevToken !== false + && $tokens[$prevToken]['code'] === T_TYPE_UNION + ) { + // Not a scope keyword, but a union return type. + return; + } + + if ($prevToken !== false + && $tokens[$prevToken]['code'] === T_COLON + ) { + $prevPrevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevToken - 1), null, true); + if ($prevPrevToken !== false + && $tokens[$prevPrevToken]['code'] === T_CLOSE_PARENTHESIS + ) { + // Not a scope keyword, but a return type. + return; + } + } + }//end if if ($tokens[$prevToken]['code'] === T_AS) { // Trait visibility change, e.g., "use HelloWorld { sayHello as private; }". diff --git a/src/Standards/Squiz/Tests/WhiteSpace/ScopeKeywordSpacingUnitTest.inc b/src/Standards/Squiz/Tests/WhiteSpace/ScopeKeywordSpacingUnitTest.inc index 791cc83f93..50a7a5cca8 100644 --- a/src/Standards/Squiz/Tests/WhiteSpace/ScopeKeywordSpacingUnitTest.inc +++ b/src/Standards/Squiz/Tests/WhiteSpace/ScopeKeywordSpacingUnitTest.inc @@ -25,7 +25,7 @@ class MyClass public static$var = null; - public + public static $var = null; } @@ -82,3 +82,17 @@ class MyOtherClass $varS, $varT } + +// Issue #3188 - static as return type. +public static function fCreate($attributes = []): static +{ + return static::factory()->create($attributes); +} + +// Also account for static used within union types. +public function fCreate($attributes = []): object|static +{ +} + +// Ensure that static as a scope keyword when preceeded by a colon which is not for a type dclaration is still handled. +$callback = $cond ? get_fn_name() : static function ($a) { return $a * 10; }; diff --git a/src/Standards/Squiz/Tests/WhiteSpace/ScopeKeywordSpacingUnitTest.inc.fixed b/src/Standards/Squiz/Tests/WhiteSpace/ScopeKeywordSpacingUnitTest.inc.fixed index a4b792b3c5..5a29fb7c4d 100644 --- a/src/Standards/Squiz/Tests/WhiteSpace/ScopeKeywordSpacingUnitTest.inc.fixed +++ b/src/Standards/Squiz/Tests/WhiteSpace/ScopeKeywordSpacingUnitTest.inc.fixed @@ -77,3 +77,17 @@ class MyOtherClass $varS, $varT } + +// Issue #3188 - static as return type. +public static function fCreate($attributes = []): static +{ + return static::factory()->create($attributes); +} + +// Also account for static used within union types. +public function fCreate($attributes = []): object|static +{ +} + +// Ensure that static as a scope keyword when preceeded by a colon which is not for a type dclaration is still handled. +$callback = $cond ? get_fn_name() : static function ($a) { return $a * 10; }; diff --git a/src/Standards/Squiz/Tests/WhiteSpace/ScopeKeywordSpacingUnitTest.php b/src/Standards/Squiz/Tests/WhiteSpace/ScopeKeywordSpacingUnitTest.php index c24dcc49d7..d94e53fbcb 100644 --- a/src/Standards/Squiz/Tests/WhiteSpace/ScopeKeywordSpacingUnitTest.php +++ b/src/Standards/Squiz/Tests/WhiteSpace/ScopeKeywordSpacingUnitTest.php @@ -38,6 +38,7 @@ public function getErrorList() 64 => 1, 67 => 1, 71 => 1, + 98 => 1, ]; }//end getErrorList()