diff --git a/src/Files/File.php b/src/Files/File.php index 3ed8f635d4..d93e9799c9 100644 --- a/src/Files/File.php +++ b/src/Files/File.php @@ -1415,7 +1415,10 @@ public function getMethodParameters($stackPtr) } break; case T_STRING: - // This is a string, so it may be a type hint, but it could + case T_NAME_QUALIFIED: + case T_NAME_FULLY_QUALIFIED: + case T_NAME_RELATIVE: + // This is an identifier name, so it may be a type declaration, but it could // also be a constant used as a default value. $prevComma = false; for ($t = $i; $t >= $opener; $t--) { @@ -1673,8 +1676,8 @@ public function getMethodProperties($stackPtr) $scopeOpener = $this->tokens[$stackPtr]['scope_opener']; } - $valid = [ - T_STRING => T_STRING, + $valid = Tokens::$nameTokens; + $valid += [ T_CALLABLE => T_CALLABLE, T_SELF => T_SELF, T_PARENT => T_PARENT, @@ -1682,8 +1685,6 @@ public function getMethodProperties($stackPtr) T_FALSE => T_FALSE, T_TRUE => T_TRUE, T_NULL => T_NULL, - T_NAMESPACE => T_NAMESPACE, - T_NS_SEPARATOR => T_NS_SEPARATOR, T_TYPE_UNION => T_TYPE_UNION, T_TYPE_INTERSECTION => T_TYPE_INTERSECTION, T_TYPE_OPEN_PARENTHESIS => T_TYPE_OPEN_PARENTHESIS, @@ -1876,16 +1877,14 @@ public function getMemberProperties($stackPtr) if ($i < $stackPtr) { // We've found a type. - $valid = [ - T_STRING => T_STRING, + $valid = Tokens::$nameTokens; + $valid += [ T_CALLABLE => T_CALLABLE, T_SELF => T_SELF, T_PARENT => T_PARENT, T_FALSE => T_FALSE, T_TRUE => T_TRUE, T_NULL => T_NULL, - T_NAMESPACE => T_NAMESPACE, - T_NS_SEPARATOR => T_NS_SEPARATOR, T_TYPE_UNION => T_TYPE_UNION, T_TYPE_INTERSECTION => T_TYPE_INTERSECTION, T_TYPE_OPEN_PARENTHESIS => T_TYPE_OPEN_PARENTHESIS, @@ -2086,12 +2085,10 @@ public function isReference($stackPtr) return true; } else { $skip = Tokens::$emptyTokens; - $skip[] = T_NS_SEPARATOR; + $skip += Tokens::$nameTokens; $skip[] = T_SELF; $skip[] = T_PARENT; $skip[] = T_STATIC; - $skip[] = T_STRING; - $skip[] = T_NAMESPACE; $skip[] = T_DOUBLE_COLON; $nextSignificantAfter = $this->findNext( @@ -2781,11 +2778,8 @@ public function findExtendedClassName($stackPtr) return false; } - $find = [ - T_NS_SEPARATOR, - T_STRING, - T_WHITESPACE, - ]; + $find = Tokens::$nameTokens; + $find[] = T_WHITESPACE; $end = $this->findNext($find, ($extendsIndex + 1), ($classOpenerIndex + 1), true); $name = $this->getTokensAsString(($extendsIndex + 1), ($end - $extendsIndex - 1)); @@ -2833,12 +2827,9 @@ public function findImplementedInterfaceNames($stackPtr) return false; } - $find = [ - T_NS_SEPARATOR, - T_STRING, - T_WHITESPACE, - T_COMMA, - ]; + $find = Tokens::$nameTokens; + $find[] = T_WHITESPACE; + $find[] = T_COMMA; $end = $this->findNext($find, ($implementsIndex + 1), ($classOpenerIndex + 1), true); $name = $this->getTokensAsString(($implementsIndex + 1), ($end - $implementsIndex - 1)); diff --git a/src/Standards/Generic/Sniffs/Classes/DuplicateClassNameSniff.php b/src/Standards/Generic/Sniffs/Classes/DuplicateClassNameSniff.php index dd0454797d..cfa03af7df 100644 --- a/src/Standards/Generic/Sniffs/Classes/DuplicateClassNameSniff.php +++ b/src/Standards/Generic/Sniffs/Classes/DuplicateClassNameSniff.php @@ -63,24 +63,16 @@ public function process(File $phpcsFile, $stackPtr) // Keep track of what namespace we are in. if ($tokens[$stackPtr]['code'] === T_NAMESPACE) { $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); - if ($nextNonEmpty !== false - // Ignore namespace keyword used as operator. - && $tokens[$nextNonEmpty]['code'] !== T_NS_SEPARATOR - ) { - $namespace = ''; - for ($i = $nextNonEmpty; $i < $phpcsFile->numTokens; $i++) { - if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) { - continue; - } - - if ($tokens[$i]['code'] !== T_STRING && $tokens[$i]['code'] !== T_NS_SEPARATOR) { - break; - } - - $namespace .= $tokens[$i]['content']; + if ($nextNonEmpty !== false) { + if ($tokens[$nextNonEmpty]['code'] === T_STRING + || $tokens[$nextNonEmpty]['code'] === T_NAME_QUALIFIED + ) { + $namespace = $tokens[$nextNonEmpty]['content']; + } else if ($tokens[$nextNonEmpty]['code'] === T_OPEN_CURLY_BRACKET) { + $namespace = ''; } - $stackPtr = $i; + $stackPtr = $nextNonEmpty; } } else { $name = $phpcsFile->getDeclarationName($stackPtr); diff --git a/src/Standards/Generic/Sniffs/CodeAnalysis/ForLoopWithTestFunctionCallSniff.php b/src/Standards/Generic/Sniffs/CodeAnalysis/ForLoopWithTestFunctionCallSniff.php index 0374a8f757..b2915e5002 100644 --- a/src/Standards/Generic/Sniffs/CodeAnalysis/ForLoopWithTestFunctionCallSniff.php +++ b/src/Standards/Generic/Sniffs/CodeAnalysis/ForLoopWithTestFunctionCallSniff.php @@ -80,7 +80,7 @@ public function process(File $phpcsFile, $stackPtr) continue; } else if ($position > 1) { break; - } else if ($code !== T_VARIABLE && $code !== T_STRING) { + } else if ($code !== T_VARIABLE && isset(Tokens::$nameTokens[$code]) === false) { continue; } diff --git a/src/Standards/Generic/Sniffs/ControlStructures/DisallowYodaConditionsSniff.php b/src/Standards/Generic/Sniffs/ControlStructures/DisallowYodaConditionsSniff.php index 666b1916e7..6a26b1f1ea 100644 --- a/src/Standards/Generic/Sniffs/ControlStructures/DisallowYodaConditionsSniff.php +++ b/src/Standards/Generic/Sniffs/ControlStructures/DisallowYodaConditionsSniff.php @@ -88,7 +88,7 @@ public function process(File $phpcsFile, $stackPtr) ); if ($beforeOpeningParenthesisIndex === false || $tokens[$beforeOpeningParenthesisIndex]['code'] !== T_ARRAY) { - if ($tokens[$beforeOpeningParenthesisIndex]['code'] === T_STRING) { + if (isset(Tokens::$nameTokens[$tokens[$beforeOpeningParenthesisIndex]['code']]) === true) { return; } diff --git a/src/Standards/Generic/Sniffs/Functions/FunctionCallArgumentSpacingSniff.php b/src/Standards/Generic/Sniffs/Functions/FunctionCallArgumentSpacingSniff.php index bf93c55338..9339f0c36f 100644 --- a/src/Standards/Generic/Sniffs/Functions/FunctionCallArgumentSpacingSniff.php +++ b/src/Standards/Generic/Sniffs/Functions/FunctionCallArgumentSpacingSniff.php @@ -24,17 +24,17 @@ class FunctionCallArgumentSpacingSniff implements Sniff */ public function register() { - return[ - T_STRING, - T_ISSET, - T_UNSET, - T_SELF, - T_STATIC, - T_PARENT, - T_VARIABLE, - T_CLOSE_CURLY_BRACKET, - T_CLOSE_PARENTHESIS, - ]; + $targets = Tokens::$nameTokens; + $targets[] = T_ISSET; + $targets[] = T_UNSET; + $targets[] = T_SELF; + $targets[] = T_STATIC; + $targets[] = T_PARENT; + $targets[] = T_VARIABLE; + $targets[] = T_CLOSE_CURLY_BRACKET; + $targets[] = T_CLOSE_PARENTHESIS; + + return $targets; }//end register() diff --git a/src/Standards/Generic/Sniffs/NamingConventions/UpperCaseConstantNameSniff.php b/src/Standards/Generic/Sniffs/NamingConventions/UpperCaseConstantNameSniff.php index 41b5b61240..95884c206c 100644 --- a/src/Standards/Generic/Sniffs/NamingConventions/UpperCaseConstantNameSniff.php +++ b/src/Standards/Generic/Sniffs/NamingConventions/UpperCaseConstantNameSniff.php @@ -26,6 +26,7 @@ public function register() { return [ T_STRING, + T_NAME_FULLY_QUALIFIED, T_CONST, ]; @@ -83,7 +84,7 @@ public function process(File $phpcsFile, $stackPtr) }//end if // Only interested in define statements now. - if (strtolower($tokens[$stackPtr]['content']) !== 'define') { + if (strtolower(ltrim($tokens[$stackPtr]['content'], '\\')) !== 'define') { return; } diff --git a/src/Standards/Generic/Sniffs/PHP/ForbiddenFunctionsSniff.php b/src/Standards/Generic/Sniffs/PHP/ForbiddenFunctionsSniff.php index b5fe8e28ba..1b733dc134 100644 --- a/src/Standards/Generic/Sniffs/PHP/ForbiddenFunctionsSniff.php +++ b/src/Standards/Generic/Sniffs/PHP/ForbiddenFunctionsSniff.php @@ -70,7 +70,10 @@ public function register() $this->forbiddenFunctionNames[$i] = '/'.$name.'/i'; } - return [T_STRING]; + return [ + T_STRING, + T_NAME_FULLY_QUALIFIED, + ]; } // If we are not pattern matching, we need to work out what @@ -102,7 +105,10 @@ public function register() $this->forbiddenFunctionNames = array_map('strtolower', $this->forbiddenFunctionNames); $this->forbiddenFunctions = array_combine($this->forbiddenFunctionNames, $this->forbiddenFunctions); - return array_unique($register); + $targets = array_unique($register); + $targets[] = T_NAME_FULLY_QUALIFIED; + + return $targets; }//end register() @@ -132,22 +138,11 @@ public function process(File $phpcsFile, $stackPtr) T_AS => true, T_NEW => true, T_INSTEADOF => true, - T_NS_SEPARATOR => true, T_IMPLEMENTS => true, ]; $prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); - // If function call is directly preceded by a NS_SEPARATOR it points to the - // global namespace, so we should still catch it. - if ($tokens[$prevToken]['code'] === T_NS_SEPARATOR) { - $prevToken = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevToken - 1), null, true); - if ($tokens[$prevToken]['code'] === T_STRING) { - // Not in the global namespace. - return; - } - } - if (isset($ignore[$tokens[$prevToken]['code']]) === true) { // Not a call to a PHP function. return; @@ -159,7 +154,9 @@ public function process(File $phpcsFile, $stackPtr) return; } - if ($tokens[$stackPtr]['code'] === T_STRING && $tokens[$nextToken]['code'] !== T_OPEN_PARENTHESIS) { + if (($tokens[$stackPtr]['code'] === T_STRING || $tokens[$stackPtr]['code'] === T_NAME_FULLY_QUALIFIED) + && $tokens[$nextToken]['code'] !== T_OPEN_PARENTHESIS + ) { // Not a call to a PHP function. return; } @@ -170,8 +167,11 @@ public function process(File $phpcsFile, $stackPtr) } $function = strtolower($tokens[$stackPtr]['content']); - $pattern = null; + if ($tokens[$stackPtr]['code'] === T_NAME_FULLY_QUALIFIED) { + $function = ltrim($function, '\\'); + } + $pattern = null; if ($this->patternMatch === true) { $count = 0; $pattern = preg_replace( @@ -194,7 +194,7 @@ public function process(File $phpcsFile, $stackPtr) } }//end if - $this->addError($phpcsFile, $stackPtr, $tokens[$stackPtr]['content'], $pattern); + $this->addError($phpcsFile, $stackPtr, $function, $pattern); }//end process() diff --git a/src/Standards/Generic/Sniffs/PHP/SAPIUsageSniff.php b/src/Standards/Generic/Sniffs/PHP/SAPIUsageSniff.php index 54b6e69ee4..555e9cdeb2 100644 --- a/src/Standards/Generic/Sniffs/PHP/SAPIUsageSniff.php +++ b/src/Standards/Generic/Sniffs/PHP/SAPIUsageSniff.php @@ -23,7 +23,10 @@ class SAPIUsageSniff implements Sniff */ public function register() { - return [T_STRING]; + return [ + T_STRING, + T_NAME_FULLY_QUALIFIED, + ]; }//end register() @@ -41,6 +44,11 @@ public function process(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); + $function = strtolower(ltrim($tokens[$stackPtr]['content'], '\\')); + if ($function !== 'php_sapi_name') { + return; + } + $ignore = [ T_DOUBLE_COLON => true, T_OBJECT_OPERATOR => true, @@ -55,11 +63,8 @@ public function process(File $phpcsFile, $stackPtr) return; } - $function = strtolower($tokens[$stackPtr]['content']); - if ($function === 'php_sapi_name') { - $error = 'Use the PHP_SAPI constant instead of calling php_sapi_name()'; - $phpcsFile->addError($error, $stackPtr, 'FunctionFound'); - } + $error = 'Use the PHP_SAPI constant instead of calling php_sapi_name()'; + $phpcsFile->addError($error, $stackPtr, 'FunctionFound'); }//end process() diff --git a/src/Standards/Generic/Sniffs/WhiteSpace/IncrementDecrementSpacingSniff.php b/src/Standards/Generic/Sniffs/WhiteSpace/IncrementDecrementSpacingSniff.php index 413a3f01bf..106fd75b2b 100644 --- a/src/Standards/Generic/Sniffs/WhiteSpace/IncrementDecrementSpacingSniff.php +++ b/src/Standards/Generic/Sniffs/WhiteSpace/IncrementDecrementSpacingSniff.php @@ -53,7 +53,8 @@ public function process(File $phpcsFile, $stackPtr) // Is this a pre-increment/decrement ? $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); if ($nextNonEmpty !== false - && ($tokens[$nextNonEmpty]['code'] === T_VARIABLE || $tokens[$nextNonEmpty]['code'] === T_STRING) + && ($tokens[$nextNonEmpty]['code'] === T_VARIABLE + || isset(Tokens::$nameTokens[$tokens[$nextNonEmpty]['code']]) === true) ) { if ($nextNonEmpty === ($stackPtr + 1)) { $phpcsFile->recordMetric($stackPtr, 'Spacing between in/decrementor and variable', 0); @@ -106,7 +107,7 @@ public function process(File $phpcsFile, $stackPtr) $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); if ($prevNonEmpty !== false && ($tokens[$prevNonEmpty]['code'] === T_VARIABLE - || $tokens[$prevNonEmpty]['code'] === T_STRING + || isset(Tokens::$nameTokens[$tokens[$prevNonEmpty]['code']]) === true || $tokens[$prevNonEmpty]['code'] === T_CLOSE_SQUARE_BRACKET) ) { if ($prevNonEmpty === ($stackPtr - 1)) { diff --git a/src/Standards/Generic/Sniffs/WhiteSpace/LanguageConstructSpacingSniff.php b/src/Standards/Generic/Sniffs/WhiteSpace/LanguageConstructSpacingSniff.php index 90b3117991..5b6131e8e4 100644 --- a/src/Standards/Generic/Sniffs/WhiteSpace/LanguageConstructSpacingSniff.php +++ b/src/Standards/Generic/Sniffs/WhiteSpace/LanguageConstructSpacingSniff.php @@ -73,8 +73,9 @@ public function process(File $phpcsFile, $stackPtr) $content = $tokens[$stackPtr]['content']; if ($tokens[$stackPtr]['code'] === T_NAMESPACE) { $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); - if ($nextNonEmpty !== false && $tokens[$nextNonEmpty]['code'] === T_NS_SEPARATOR) { + if ($nextNonEmpty !== false && $tokens[$nextNonEmpty]['code'] === T_NAME_FULLY_QUALIFIED) { // Namespace keyword used as operator, not as the language construct. + // Note: in PHP >= 8 namespaced names no longer allow for whitespace/comments between the parts (parse error). return; } } diff --git a/src/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.8.inc b/src/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.8.inc index 4ae5578269..ae6742d890 100644 --- a/src/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.8.inc +++ b/src/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.8.inc @@ -3,6 +3,6 @@ namespace Foo\Bar; class MyClass {} interface YourInterface {} -namespace Foo /*comment*/ \ /*comment*/ Bar; +namespace /*comment*/ Foo\Bar; class MyClass {} interface YourInterface {} diff --git a/src/Standards/Generic/Tests/CodeAnalysis/ForLoopWithTestFunctionCallUnitTest.1.inc b/src/Standards/Generic/Tests/CodeAnalysis/ForLoopWithTestFunctionCallUnitTest.1.inc index 31f1a64199..5c361290de 100644 --- a/src/Standards/Generic/Tests/CodeAnalysis/ForLoopWithTestFunctionCallUnitTest.1.inc +++ b/src/Standards/Generic/Tests/CodeAnalysis/ForLoopWithTestFunctionCallUnitTest.1.inc @@ -93,3 +93,7 @@ endfor; for ($i = 0; $i < 10; $i = increment($i)) {} for ($i = initialValue(); $i < 10; $i = increment($i)) {} + +for ($i = 0; \Fully\qualified($value); $i++) {} +for ($i = 0; Partially\qualified($value); $i++) {} +for ($i = 0; namespace\relatived($value); $i++) {} diff --git a/src/Standards/Generic/Tests/CodeAnalysis/ForLoopWithTestFunctionCallUnitTest.php b/src/Standards/Generic/Tests/CodeAnalysis/ForLoopWithTestFunctionCallUnitTest.php index 74690b9f22..bf214f1ca8 100644 --- a/src/Standards/Generic/Tests/CodeAnalysis/ForLoopWithTestFunctionCallUnitTest.php +++ b/src/Standards/Generic/Tests/CodeAnalysis/ForLoopWithTestFunctionCallUnitTest.php @@ -64,6 +64,9 @@ public function getWarningList($testFile='') 66 => 1, 72 => 1, 81 => 1, + 97 => 1, + 98 => 1, + 99 => 1, ]; default: return []; diff --git a/src/Standards/Generic/Tests/ControlStructures/DisallowYodaConditionsUnitTest.inc b/src/Standards/Generic/Tests/ControlStructures/DisallowYodaConditionsUnitTest.inc index 27053c4d65..b9cad4d3ea 100644 --- a/src/Standards/Generic/Tests/ControlStructures/DisallowYodaConditionsUnitTest.inc +++ b/src/Standards/Generic/Tests/ControlStructures/DisallowYodaConditionsUnitTest.inc @@ -185,3 +185,10 @@ echo match ($text) { 1 >= $value; 1 <= $value; 1 <=> $value; + +if(\Fully\qualified('foo') === false){} +if(true === \Fully\qualified(10)){} +if(Partially\qualified('foo') === 10){} +if(1.5 === Partially\qualified(true)){} +if(namespace\relative(false) === null){} +if('string' === namespace\relative(null)){} diff --git a/src/Standards/Generic/Tests/ControlStructures/DisallowYodaConditionsUnitTest.php b/src/Standards/Generic/Tests/ControlStructures/DisallowYodaConditionsUnitTest.php index db231cdc48..510bd7d52e 100644 --- a/src/Standards/Generic/Tests/ControlStructures/DisallowYodaConditionsUnitTest.php +++ b/src/Standards/Generic/Tests/ControlStructures/DisallowYodaConditionsUnitTest.php @@ -72,6 +72,9 @@ public function getErrorList() 185 => 1, 186 => 1, 187 => 1, + 190 => 1, + 192 => 1, + 194 => 1, ]; }//end getErrorList() diff --git a/src/Standards/Generic/Tests/Functions/FunctionCallArgumentSpacingUnitTest.1.inc b/src/Standards/Generic/Tests/Functions/FunctionCallArgumentSpacingUnitTest.1.inc index 393ee10b66..a7aa8de6fe 100644 --- a/src/Standards/Generic/Tests/Functions/FunctionCallArgumentSpacingUnitTest.1.inc +++ b/src/Standards/Generic/Tests/Functions/FunctionCallArgumentSpacingUnitTest.1.inc @@ -197,3 +197,7 @@ $foobar = functionCallMatchParam( } , // But check the spacing again once the match expression has finished. $args ); + +$result = \Fully\qualified($arg1,$arg2); +$result = Partially\qualified($arg1 , $arg2); +$result = namespace\relative($arg1 , $arg2); diff --git a/src/Standards/Generic/Tests/Functions/FunctionCallArgumentSpacingUnitTest.1.inc.fixed b/src/Standards/Generic/Tests/Functions/FunctionCallArgumentSpacingUnitTest.1.inc.fixed index a50f695e45..1122576310 100644 --- a/src/Standards/Generic/Tests/Functions/FunctionCallArgumentSpacingUnitTest.1.inc.fixed +++ b/src/Standards/Generic/Tests/Functions/FunctionCallArgumentSpacingUnitTest.1.inc.fixed @@ -197,3 +197,7 @@ $foobar = functionCallMatchParam( }, // But check the spacing again once the match expression has finished. $args ); + +$result = \Fully\qualified($arg1, $arg2); +$result = Partially\qualified($arg1, $arg2); +$result = namespace\relative($arg1, $arg2); diff --git a/src/Standards/Generic/Tests/Functions/FunctionCallArgumentSpacingUnitTest.php b/src/Standards/Generic/Tests/Functions/FunctionCallArgumentSpacingUnitTest.php index a068ee6b48..752db95c0f 100644 --- a/src/Standards/Generic/Tests/Functions/FunctionCallArgumentSpacingUnitTest.php +++ b/src/Standards/Generic/Tests/Functions/FunctionCallArgumentSpacingUnitTest.php @@ -69,6 +69,9 @@ public function getErrorList($testFile='') 190 => 2, 191 => 2, 197 => 1, + 201 => 1, + 202 => 1, + 203 => 2, ]; default: diff --git a/src/Standards/Generic/Tests/NamingConventions/UpperCaseConstantNameUnitTest.1.inc b/src/Standards/Generic/Tests/NamingConventions/UpperCaseConstantNameUnitTest.1.inc index c5af2349cb..a1060b7e58 100644 --- a/src/Standards/Generic/Tests/NamingConventions/UpperCaseConstantNameUnitTest.1.inc +++ b/src/Standards/Generic/Tests/NamingConventions/UpperCaseConstantNameUnitTest.1.inc @@ -89,3 +89,6 @@ class MyClass { $a = ($cond) ? DEFINE : SOMETHING_ELSE; $object = new Define('value'); + +\define('VALID_NAME', true); +\define('invalidName', true); diff --git a/src/Standards/Generic/Tests/NamingConventions/UpperCaseConstantNameUnitTest.php b/src/Standards/Generic/Tests/NamingConventions/UpperCaseConstantNameUnitTest.php index 3249f72256..749b539691 100644 --- a/src/Standards/Generic/Tests/NamingConventions/UpperCaseConstantNameUnitTest.php +++ b/src/Standards/Generic/Tests/NamingConventions/UpperCaseConstantNameUnitTest.php @@ -48,10 +48,12 @@ public function getErrorList($testFile='') 51 => 1, 71 => 1, 73 => 1, + 94 => 1, ]; + default: return []; - } + }//end switch }//end getErrorList() diff --git a/src/Standards/Generic/Tests/PHP/SAPIUsageUnitTest.inc b/src/Standards/Generic/Tests/PHP/SAPIUsageUnitTest.inc index f0f350f374..ae232f4599 100644 --- a/src/Standards/Generic/Tests/PHP/SAPIUsageUnitTest.inc +++ b/src/Standards/Generic/Tests/PHP/SAPIUsageUnitTest.inc @@ -3,3 +3,6 @@ if (php_sapi_name() !== 'cli') {} if (PHP_SAPI !== 'cli') {} if ($object->php_sapi_name() === true) {} if ($object?->php_sapi_name() === true) {} +if (\php_sapi_name() !== 'cli') {} +if (\NotPHPNative\php_sapi_name() !== 'cli') {} +if (Qualified\php_sapi_name() !== 'cli') {} diff --git a/src/Standards/Generic/Tests/PHP/SAPIUsageUnitTest.php b/src/Standards/Generic/Tests/PHP/SAPIUsageUnitTest.php index 42eac06ed2..31b0d31e1e 100644 --- a/src/Standards/Generic/Tests/PHP/SAPIUsageUnitTest.php +++ b/src/Standards/Generic/Tests/PHP/SAPIUsageUnitTest.php @@ -30,7 +30,10 @@ final class SAPIUsageUnitTest extends AbstractSniffTestCase */ public function getErrorList() { - return [2 => 1]; + return [ + 2 => 1, + 6 => 1, + ]; }//end getErrorList() diff --git a/src/Standards/Generic/Tests/WhiteSpace/ArbitraryParenthesesSpacingUnitTest.1.inc b/src/Standards/Generic/Tests/WhiteSpace/ArbitraryParenthesesSpacingUnitTest.1.inc index 4ce050840f..b531b36648 100644 --- a/src/Standards/Generic/Tests/WhiteSpace/ArbitraryParenthesesSpacingUnitTest.1.inc +++ b/src/Standards/Generic/Tests/WhiteSpace/ArbitraryParenthesesSpacingUnitTest.1.inc @@ -190,3 +190,13 @@ switch ( $type ) { default: break; } + +function NonArbitraryParenthesesNameSpacedNames( $foo, $bar ) { + $b = \Fully\Qualified( $something ); + $b = Partially\Qualified( $something ) {}; + $c = namespace\Relative( $arg1 , $arg2 = array( ) ); + + $a = new \Fully\Qualified( $something ); + $b = new Partially\Qualified( $something ); + $c = new namespace\Relative( $something ); +} diff --git a/src/Standards/Generic/Tests/WhiteSpace/ArbitraryParenthesesSpacingUnitTest.1.inc.fixed b/src/Standards/Generic/Tests/WhiteSpace/ArbitraryParenthesesSpacingUnitTest.1.inc.fixed index a002280811..ef3b372f2d 100644 --- a/src/Standards/Generic/Tests/WhiteSpace/ArbitraryParenthesesSpacingUnitTest.1.inc.fixed +++ b/src/Standards/Generic/Tests/WhiteSpace/ArbitraryParenthesesSpacingUnitTest.1.inc.fixed @@ -178,3 +178,13 @@ switch ( $type ) { default: break; } + +function NonArbitraryParenthesesNameSpacedNames( $foo, $bar ) { + $b = \Fully\Qualified( $something ); + $b = Partially\Qualified( $something ) {}; + $c = namespace\Relative( $arg1 , $arg2 = array( ) ); + + $a = new \Fully\Qualified( $something ); + $b = new Partially\Qualified( $something ); + $c = new namespace\Relative( $something ); +} diff --git a/src/Standards/Generic/Tests/WhiteSpace/IncrementDecrementSpacingUnitTest.inc b/src/Standards/Generic/Tests/WhiteSpace/IncrementDecrementSpacingUnitTest.inc index 535053b0d0..b8bc9a5e54 100644 --- a/src/Standards/Generic/Tests/WhiteSpace/IncrementDecrementSpacingUnitTest.inc +++ b/src/Standards/Generic/Tests/WhiteSpace/IncrementDecrementSpacingUnitTest.inc @@ -41,3 +41,18 @@ getObject()->count getObject()->count++; ++ getObject()->count; ++getObject()->count; + +++ +\Fully\Qualified()->count; +++\Fully\Qualified()->count; +-- Partially\Qualified()->count; +--Partially\Qualified()->count; +++ namespace\relative()->count; +++namespace\relative()->count; + +++\ClassName::$prop; +++ \ClassName::$prop; +++Relative\ClassName::$prop; +++ Relative\ClassName::$prop; +--namespace\Relative\ClassName::$prop; +-- /*comment*/ namespace\Relative\ClassName::$prop; diff --git a/src/Standards/Generic/Tests/WhiteSpace/IncrementDecrementSpacingUnitTest.inc.fixed b/src/Standards/Generic/Tests/WhiteSpace/IncrementDecrementSpacingUnitTest.inc.fixed index c30332b25e..252934d058 100644 --- a/src/Standards/Generic/Tests/WhiteSpace/IncrementDecrementSpacingUnitTest.inc.fixed +++ b/src/Standards/Generic/Tests/WhiteSpace/IncrementDecrementSpacingUnitTest.inc.fixed @@ -39,3 +39,17 @@ getObject()->count++; getObject()->count++; ++getObject()->count; ++getObject()->count; + +++\Fully\Qualified()->count; +++\Fully\Qualified()->count; +--Partially\Qualified()->count; +--Partially\Qualified()->count; +++namespace\relative()->count; +++namespace\relative()->count; + +++\ClassName::$prop; +++\ClassName::$prop; +++Relative\ClassName::$prop; +++Relative\ClassName::$prop; +--namespace\Relative\ClassName::$prop; +-- /*comment*/ namespace\Relative\ClassName::$prop; diff --git a/src/Standards/Generic/Tests/WhiteSpace/IncrementDecrementSpacingUnitTest.php b/src/Standards/Generic/Tests/WhiteSpace/IncrementDecrementSpacingUnitTest.php index 1ba6de3f11..e5890e3273 100644 --- a/src/Standards/Generic/Tests/WhiteSpace/IncrementDecrementSpacingUnitTest.php +++ b/src/Standards/Generic/Tests/WhiteSpace/IncrementDecrementSpacingUnitTest.php @@ -49,6 +49,12 @@ public function getErrorList() 37 => 1, 40 => 1, 42 => 1, + 45 => 1, + 48 => 1, + 50 => 1, + 54 => 1, + 56 => 1, + 58 => 1, ]; }//end getErrorList() diff --git a/src/Standards/PEAR/Sniffs/ControlStructures/MultiLineConditionSniff.php b/src/Standards/PEAR/Sniffs/ControlStructures/MultiLineConditionSniff.php index 323850d4fd..5715a2ab15 100644 --- a/src/Standards/PEAR/Sniffs/ControlStructures/MultiLineConditionSniff.php +++ b/src/Standards/PEAR/Sniffs/ControlStructures/MultiLineConditionSniff.php @@ -204,7 +204,7 @@ public function process(File $phpcsFile, $stackPtr) $prevLine = $tokens[$i]['line']; }//end if - if ($tokens[$i]['code'] === T_STRING) { + if (isset(Tokens::$nameTokens[$tokens[$i]['code']]) === true) { $next = $phpcsFile->findNext(T_WHITESPACE, ($i + 1), null, true); if ($tokens[$next]['code'] === T_OPEN_PARENTHESIS) { // This is a function call, so skip to the end as they diff --git a/src/Standards/PEAR/Tests/ControlStructures/MultiLineConditionUnitTest.inc b/src/Standards/PEAR/Tests/ControlStructures/MultiLineConditionUnitTest.inc index a7a3c69d70..fdf5375334 100644 --- a/src/Standards/PEAR/Tests/ControlStructures/MultiLineConditionUnitTest.inc +++ b/src/Standards/PEAR/Tests/ControlStructures/MultiLineConditionUnitTest.inc @@ -249,3 +249,26 @@ if ($IPP->errorCode() == 401 ) { return false; } + +// Should be no errors even though lines are not exactly aligned together. +// Multi-line function call takes precedence. +if ($a === true + && \Fully\Qualified( + $key, $value2 + ) +) { +} + +if ($a === true + && Partially\qualified( + $key, $value2 + ) +) { +} + +if ($a === true + && namespace\relative( + $key, $value2 + ) +) { +} diff --git a/src/Standards/PEAR/Tests/ControlStructures/MultiLineConditionUnitTest.inc.fixed b/src/Standards/PEAR/Tests/ControlStructures/MultiLineConditionUnitTest.inc.fixed index 7d56c4618d..ef146b10aa 100644 --- a/src/Standards/PEAR/Tests/ControlStructures/MultiLineConditionUnitTest.inc.fixed +++ b/src/Standards/PEAR/Tests/ControlStructures/MultiLineConditionUnitTest.inc.fixed @@ -245,3 +245,26 @@ if ($IPP->errorCode() == 401 ) { return false; } + +// Should be no errors even though lines are not exactly aligned together. +// Multi-line function call takes precedence. +if ($a === true + && \Fully\Qualified( + $key, $value2 + ) +) { +} + +if ($a === true + && Partially\qualified( + $key, $value2 + ) +) { +} + +if ($a === true + && namespace\relative( + $key, $value2 + ) +) { +} diff --git a/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.inc b/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.inc index fddd3cba18..d73725bfc6 100644 --- a/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.inc +++ b/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.inc @@ -317,7 +317,7 @@ foo( $e ); -test( +Partially\Qualified\test( 1,2,3,4 ); @@ -374,7 +374,7 @@ return (function ($a, $b) { function foo() { - Bar( + \Bar( function () { } ); @@ -390,7 +390,7 @@ $deprecated_functions = [ $deprecated_functions = [ 'the_category_ID' - => function_call( // 9 spaces, not 8. This is the problem line. + => namespace\function_call( // 9 spaces, not 8. This is the problem line. $a, $b ), @@ -574,3 +574,17 @@ content 'my_file.php' ); ?> + + function_call( // 9 spaces, not 8. This is the problem line. + => namespace\function_call( // 9 spaces, not 8. This is the problem line. $a, $b ), @@ -589,3 +589,20 @@ content 'my_file.php' ); ?> + + 1, 573 => 1, 574 => 1, + 579 => 3, + 580 => 2, + 581 => 1, + 583 => 2, + 584 => 1, + 586 => 2, ]; }//end getErrorList() diff --git a/src/Standards/PSR1/Sniffs/Files/SideEffectsSniff.php b/src/Standards/PSR1/Sniffs/Files/SideEffectsSniff.php index 68a18d8c76..c807d8725c 100644 --- a/src/Standards/PSR1/Sniffs/Files/SideEffectsSniff.php +++ b/src/Standards/PSR1/Sniffs/Files/SideEffectsSniff.php @@ -201,8 +201,9 @@ private function searchForConflict($phpcsFile, $start, $end, $tokens) $i = $tokens[$i]['scope_closer']; continue; - } else if ($tokens[$i]['code'] === T_STRING - && strtolower($tokens[$i]['content']) === 'define' + } else if (($tokens[$i]['code'] === T_STRING + || $tokens[$i]['code'] === T_NAME_FULLY_QUALIFIED) + && strtolower(ltrim($tokens[$i]['content'], '\\')) === 'define' ) { $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($i - 1), null, true); if ($tokens[$prev]['code'] !== T_OBJECT_OPERATOR @@ -226,8 +227,9 @@ private function searchForConflict($phpcsFile, $start, $end, $tokens) // Special case for defined() as it can be used to see // if a constant (a symbol) should be defined or not and // doesn't need to use a full conditional block. - if ($tokens[$i]['code'] === T_STRING - && strtolower($tokens[$i]['content']) === 'defined' + if (($tokens[$i]['code'] === T_STRING + || $tokens[$i]['code'] === T_NAME_FULLY_QUALIFIED) + && strtolower(ltrim($tokens[$i]['content'], '\\')) === 'defined' ) { $openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), null, true); if ($openBracket !== false diff --git a/src/Standards/PSR1/Tests/Files/SideEffectsUnitTest.16.inc b/src/Standards/PSR1/Tests/Files/SideEffectsUnitTest.16.inc index 588ece5840..a4fe468824 100644 --- a/src/Standards/PSR1/Tests/Files/SideEffectsUnitTest.16.inc +++ b/src/Standards/PSR1/Tests/Files/SideEffectsUnitTest.16.inc @@ -1,2 +1,2 @@ defined('MINSIZE') or define("MAXSIZE", 100); +$my?->defined('MINSIZE') or \define("MAXSIZE", 100); diff --git a/src/Standards/PSR1/Tests/Files/SideEffectsUnitTest.17.inc b/src/Standards/PSR1/Tests/Files/SideEffectsUnitTest.17.inc index ec81bfedac..238a4c68f9 100644 --- a/src/Standards/PSR1/Tests/Files/SideEffectsUnitTest.17.inc +++ b/src/Standards/PSR1/Tests/Files/SideEffectsUnitTest.17.inc @@ -1,8 +1,8 @@ getTokens(); // Find the class name. - $allowed = [ - T_STRING => T_STRING, - T_NS_SEPARATOR => T_NS_SEPARATOR, + $allowed = Tokens::$nameTokens; + $allowed += [ T_SELF => T_SELF, T_STATIC => T_STATIC, T_PARENT => T_PARENT, diff --git a/src/Standards/PSR12/Sniffs/Files/ImportStatementSniff.php b/src/Standards/PSR12/Sniffs/Files/ImportStatementSniff.php index 4572855f8e..fbd08a3adf 100644 --- a/src/Standards/PSR12/Sniffs/Files/ImportStatementSniff.php +++ b/src/Standards/PSR12/Sniffs/Files/ImportStatementSniff.php @@ -60,7 +60,7 @@ public function process(File $phpcsFile, $stackPtr) $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true); } - if ($tokens[$next]['code'] !== T_NS_SEPARATOR) { + if ($tokens[$next]['code'] !== T_NAME_FULLY_QUALIFIED) { return; } @@ -68,7 +68,7 @@ public function process(File $phpcsFile, $stackPtr) $fix = $phpcsFile->addFixableError($error, $next, 'LeadingSlash'); if ($fix === true) { - $phpcsFile->fixer->replaceToken($next, ''); + $phpcsFile->fixer->replaceToken($next, ltrim($tokens[$next]['content'], '\\')); } }//end process() diff --git a/src/Standards/PSR12/Sniffs/Functions/NullableTypeDeclarationSniff.php b/src/Standards/PSR12/Sniffs/Functions/NullableTypeDeclarationSniff.php index 4314194d4c..693510edc5 100644 --- a/src/Standards/PSR12/Sniffs/Functions/NullableTypeDeclarationSniff.php +++ b/src/Standards/PSR12/Sniffs/Functions/NullableTypeDeclarationSniff.php @@ -21,15 +21,17 @@ class NullableTypeDeclarationSniff implements Sniff * @var array */ private $validTokens = [ - T_STRING => true, - T_NS_SEPARATOR => true, - T_CALLABLE => true, - T_SELF => true, - T_PARENT => true, - T_STATIC => true, - T_NULL => true, - T_FALSE => true, - T_TRUE => true, + T_STRING => true, + T_NAME_QUALIFIED => true, + T_NAME_FULLY_QUALIFIED => true, + T_NAME_RELATIVE => true, + T_CALLABLE => true, + T_SELF => true, + T_PARENT => true, + T_STATIC => true, + T_NULL => true, + T_FALSE => true, + T_TRUE => true, ]; diff --git a/src/Standards/PSR12/Sniffs/Namespaces/CompoundNamespaceDepthSniff.php b/src/Standards/PSR12/Sniffs/Namespaces/CompoundNamespaceDepthSniff.php index 34e7f461a3..a67f3b70ef 100644 --- a/src/Standards/PSR12/Sniffs/Namespaces/CompoundNamespaceDepthSniff.php +++ b/src/Standards/PSR12/Sniffs/Namespaces/CompoundNamespaceDepthSniff.php @@ -62,6 +62,11 @@ public function process(File $phpcsFile, $stackPtr) continue; } + if ($tokens[$i]['code'] === T_NAME_FULLY_QUALIFIED || $tokens[$i]['code'] === T_NAME_QUALIFIED) { + $depth += substr_count($tokens[$i]['content'], '\\'); + continue; + } + if ($i === $end || $tokens[$i]['code'] === T_COMMA) { // End of a namespace. if ($depth > $this->maxDepth) { @@ -72,7 +77,7 @@ public function process(File $phpcsFile, $stackPtr) $depth = 1; } - } + }//end for }//end process() diff --git a/src/Standards/PSR12/Tests/Classes/AnonClassDeclarationUnitTest.inc b/src/Standards/PSR12/Tests/Classes/AnonClassDeclarationUnitTest.inc index 386b12c226..3f08cb9530 100644 --- a/src/Standards/PSR12/Tests/Classes/AnonClassDeclarationUnitTest.inc +++ b/src/Standards/PSR12/Tests/Classes/AnonClassDeclarationUnitTest.inc @@ -50,15 +50,15 @@ $instance6 = new class extends \Foo }; $instance7 = new class extends \Foo implements - \One, \Two, - \Three, + \One, Two, + Partially\Three, \Four, - \Five { + namespace\Five { // Class content }; if ($foo) { - $instance8 = new class extends \Foo implements + $instance8 = new class extends namespace\Foo implements \One, \Five { // Class content @@ -68,7 +68,7 @@ if ($foo) { $instance9 = new class ( $one, $two -) extends \Foo implements \One { +) extends Partially\Foo implements \One { // Class content }; diff --git a/src/Standards/PSR12/Tests/Classes/AnonClassDeclarationUnitTest.inc.fixed b/src/Standards/PSR12/Tests/Classes/AnonClassDeclarationUnitTest.inc.fixed index 28b38f7086..fce832f7b7 100644 --- a/src/Standards/PSR12/Tests/Classes/AnonClassDeclarationUnitTest.inc.fixed +++ b/src/Standards/PSR12/Tests/Classes/AnonClassDeclarationUnitTest.inc.fixed @@ -50,16 +50,16 @@ $instance6 = new class extends \Foo implements \HandleableInterface { $instance7 = new class extends \Foo implements \One, - \Two, - \Three, + Two, + Partially\Three, \Four, - \Five + namespace\Five { // Class content }; if ($foo) { - $instance8 = new class extends \Foo implements + $instance8 = new class extends namespace\Foo implements \One, \Five { @@ -70,7 +70,7 @@ if ($foo) { $instance9 = new class ( $one, $two -) extends \Foo implements \One { +) extends Partially\Foo implements \One { // Class content }; diff --git a/src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.inc b/src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.inc index 264116779f..ee2156cfd3 100644 --- a/src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.inc +++ b/src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.inc @@ -49,3 +49,8 @@ $foo = new parent; // PHP 8.3: safeguard that the sniff ignores anonymous classes, even when declared as readonly. $anon = new readonly class {}; $anon = new #[MyAttribute] readonly class {}; + +$foo = new \FQN\Bar(); +$foo = new namespace\Bar(); +$foo = new \FQN\Bar; +$foo = new namespace\Bar; diff --git a/src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.inc.fixed b/src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.inc.fixed index 1e580f45d9..cf6233d53f 100644 --- a/src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.inc.fixed +++ b/src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.inc.fixed @@ -49,3 +49,8 @@ $foo = new parent(); // PHP 8.3: safeguard that the sniff ignores anonymous classes, even when declared as readonly. $anon = new readonly class {}; $anon = new #[MyAttribute] readonly class {}; + +$foo = new \FQN\Bar(); +$foo = new namespace\Bar(); +$foo = new \FQN\Bar(); +$foo = new namespace\Bar(); diff --git a/src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.php b/src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.php index 816cb3b2b4..1881b73ccd 100644 --- a/src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.php +++ b/src/Standards/PSR12/Tests/Classes/ClassInstantiationUnitTest.php @@ -49,6 +49,8 @@ public function getErrorList() 37 => 1, 38 => 1, 47 => 1, + 55 => 1, + 56 => 1, ]; }//end getErrorList() diff --git a/src/Standards/PSR12/Tests/Functions/NullableTypeDeclarationUnitTest.inc b/src/Standards/PSR12/Tests/Functions/NullableTypeDeclarationUnitTest.inc index 056d74c3f2..d0a12f4697 100644 --- a/src/Standards/PSR12/Tests/Functions/NullableTypeDeclarationUnitTest.inc +++ b/src/Standards/PSR12/Tests/Functions/NullableTypeDeclarationUnitTest.inc @@ -8,10 +8,10 @@ class MyClass { { } - public function validWithFQN( + public function validWithQualifiedNames( ?\MyNameSpace\MyArray $array = null, - ?\MyNameSpace\MyObject $object - ): ?\MyNameSpace\MyObject + ?MyNameSpace\MyObject $object + ): ?namespace\MyObject { } @@ -26,10 +26,10 @@ class MyClass { { } - public function invalidWithFQNTooMuchWhitespace( + public function invalidWithQualifiedNamesTooMuchWhitespace( ? \MyNameSpace\MyArray $array = null, - ? \MyNameSpace\MyObject - ):? \MyNameSpace\MyObject + ? MyNameSpace\MyObject + ):? namespace\MyObject { } } diff --git a/src/Standards/PSR12/Tests/Functions/NullableTypeDeclarationUnitTest.inc.fixed b/src/Standards/PSR12/Tests/Functions/NullableTypeDeclarationUnitTest.inc.fixed index 6cc418d074..a8ffe2136c 100644 --- a/src/Standards/PSR12/Tests/Functions/NullableTypeDeclarationUnitTest.inc.fixed +++ b/src/Standards/PSR12/Tests/Functions/NullableTypeDeclarationUnitTest.inc.fixed @@ -8,10 +8,10 @@ class MyClass { { } - public function validWithFQN( + public function validWithQualifiedNames( ?\MyNameSpace\MyArray $array = null, - ?\MyNameSpace\MyObject $object - ): ?\MyNameSpace\MyObject + ?MyNameSpace\MyObject $object + ): ?namespace\MyObject { } @@ -26,10 +26,10 @@ class MyClass { { } - public function invalidWithFQNTooMuchWhitespace( + public function invalidWithQualifiedNamesTooMuchWhitespace( ?\MyNameSpace\MyArray $array = null, - ?\MyNameSpace\MyObject - ):?\MyNameSpace\MyObject + ?MyNameSpace\MyObject + ):?namespace\MyObject { } } diff --git a/src/Standards/PSR12/Tests/Namespaces/CompoundNamespaceDepthUnitTest.inc b/src/Standards/PSR12/Tests/Namespaces/CompoundNamespaceDepthUnitTest.inc index 454f7fc252..8f025cb101 100644 --- a/src/Standards/PSR12/Tests/Namespaces/CompoundNamespaceDepthUnitTest.inc +++ b/src/Standards/PSR12/Tests/Namespaces/CompoundNamespaceDepthUnitTest.inc @@ -12,6 +12,7 @@ use Vendor\Package\SomeNamespace\{ ClassZ, }; +// Parse error in PHP 8.0+, but will be detected correctly cross-version anyway. use Vendor\Package\SomeNamespace\{ SubnamespaceOne /* comment */ \AnotherNamespace // comment diff --git a/src/Standards/PSR12/Tests/Namespaces/CompoundNamespaceDepthUnitTest.php b/src/Standards/PSR12/Tests/Namespaces/CompoundNamespaceDepthUnitTest.php index 787b4dec9f..bff6560993 100644 --- a/src/Standards/PSR12/Tests/Namespaces/CompoundNamespaceDepthUnitTest.php +++ b/src/Standards/PSR12/Tests/Namespaces/CompoundNamespaceDepthUnitTest.php @@ -32,8 +32,8 @@ public function getErrorList() { return [ 10 => 1, - 18 => 1, - 21 => 1, + 19 => 1, + 22 => 1, ]; }//end getErrorList() diff --git a/src/Standards/PSR2/Sniffs/Classes/ClassDeclarationSniff.php b/src/Standards/PSR2/Sniffs/Classes/ClassDeclarationSniff.php index 887c552edc..bfef222f34 100644 --- a/src/Standards/PSR2/Sniffs/Classes/ClassDeclarationSniff.php +++ b/src/Standards/PSR2/Sniffs/Classes/ClassDeclarationSniff.php @@ -280,10 +280,8 @@ public function processOpen(File $phpcsFile, $stackPtr) } } - $find = [ - T_STRING, - $keywordTokenType, - ]; + $find = Tokens::$nameTokens; + $find[$keywordTokenType] = $keywordTokenType; if ($className !== null) { $start = $className; @@ -311,17 +309,9 @@ public function processOpen(File $phpcsFile, $stackPtr) continue; } - if ($checkingImplements === true - && $multiLineImplements === true - && ($tokens[($className - 1)]['code'] !== T_NS_SEPARATOR - || ($tokens[($className - 2)]['code'] !== T_STRING - && $tokens[($className - 2)]['code'] !== T_NAMESPACE)) - ) { + if ($checkingImplements === true && $multiLineImplements === true) { $prev = $phpcsFile->findPrevious( - [ - T_NS_SEPARATOR, - T_WHITESPACE, - ], + T_WHITESPACE, ($className - 1), $implements, true @@ -400,15 +390,8 @@ public function processOpen(File $phpcsFile, $stackPtr) } } }//end if - } else if ($tokens[($className - 1)]['code'] !== T_NS_SEPARATOR - || ($tokens[($className - 2)]['code'] !== T_STRING - && $tokens[($className - 2)]['code'] !== T_NAMESPACE) - ) { - // Not part of a longer fully qualified or namespace relative class name. - if ($tokens[($className - 1)]['code'] === T_COMMA - || ($tokens[($className - 1)]['code'] === T_NS_SEPARATOR - && $tokens[($className - 2)]['code'] === T_COMMA) - ) { + } else { + if ($tokens[($className - 1)]['code'] === T_COMMA) { $error = 'Expected 1 space before "%s"; 0 found'; $data = [$tokens[$className]['content']]; $fix = $phpcsFile->addFixableError($error, ($nextComma + 1), 'NoSpaceBeforeName', $data); @@ -416,11 +399,7 @@ public function processOpen(File $phpcsFile, $stackPtr) $phpcsFile->fixer->addContentBefore(($nextComma + 1), ' '); } } else { - if ($tokens[($className - 1)]['code'] === T_NS_SEPARATOR) { - $prev = ($className - 2); - } else { - $prev = ($className - 1); - } + $prev = ($className - 1); $last = $phpcsFile->findPrevious(T_WHITESPACE, $prev, null, true); $content = $phpcsFile->getTokensAsString(($last + 1), ($prev - $last)); @@ -452,7 +431,6 @@ public function processOpen(File $phpcsFile, $stackPtr) }//end if if ($checkingImplements === true - && $tokens[($className + 1)]['code'] !== T_NS_SEPARATOR && $tokens[($className + 1)]['code'] !== T_COMMA ) { if ($n !== ($classCount - 1)) { diff --git a/src/Standards/PSR2/Sniffs/Namespaces/NamespaceDeclarationSniff.php b/src/Standards/PSR2/Sniffs/Namespaces/NamespaceDeclarationSniff.php index 7b1dad199f..e414a495e2 100644 --- a/src/Standards/PSR2/Sniffs/Namespaces/NamespaceDeclarationSniff.php +++ b/src/Standards/PSR2/Sniffs/Namespaces/NamespaceDeclarationSniff.php @@ -11,7 +11,6 @@ use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Sniffs\Sniff; -use PHP_CodeSniffer\Util\Tokens; class NamespaceDeclarationSniff implements Sniff { @@ -42,12 +41,6 @@ public function process(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); - $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); - if ($tokens[$nextNonEmpty]['code'] === T_NS_SEPARATOR) { - // Namespace keyword as operator. Not a declaration. - return; - } - $end = $phpcsFile->findEndOfStatement($stackPtr); for ($i = ($end + 1); $i < ($phpcsFile->numTokens - 1); $i++) { if ($tokens[$i]['line'] === $tokens[$end]['line']) { diff --git a/src/Standards/Squiz/Sniffs/Classes/SelfMemberReferenceSniff.php b/src/Standards/Squiz/Sniffs/Classes/SelfMemberReferenceSniff.php index 8ee4de45a8..9c50e1587d 100644 --- a/src/Standards/Squiz/Sniffs/Classes/SelfMemberReferenceSniff.php +++ b/src/Standards/Squiz/Sniffs/Classes/SelfMemberReferenceSniff.php @@ -75,48 +75,40 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop return; } - } else if ($tokens[$calledClassName]['code'] === T_STRING) { - // If the class is called with a namespace prefix, build fully qualified - // namespace calls for both current scope class and requested class. - $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($calledClassName - 1), null, true); - if ($prevNonEmpty !== false && $tokens[$prevNonEmpty]['code'] === T_NS_SEPARATOR) { - $declarationName = $this->getDeclarationNameWithNamespace($tokens, $calledClassName); - $declarationName = ltrim($declarationName, '\\'); - $fullQualifiedClassName = $this->getNamespaceOfScope($phpcsFile, $currScope); - if ($fullQualifiedClassName === '\\') { - $fullQualifiedClassName = ''; - } else { - $fullQualifiedClassName .= '\\'; - } + } else if (isset(Tokens::$nameTokens[$tokens[$calledClassName]['code']]) === true) { + // Work out the fully qualified name for both the class declaration + // as well as the class usage to see if they match. + $namespaceName = $this->getNamespaceName($phpcsFile, $currScope); + if ($namespaceName === '\\') { + $namespaceName = ''; + } + + $declarationName = $namespaceName.'\\'.$phpcsFile->getDeclarationName($currScope); + $inlineName = ''; + + switch ($tokens[$calledClassName]['code']) { + case T_NAME_FULLY_QUALIFIED: + $inlineName = $tokens[$calledClassName]['content']; + break; + + case T_NAME_QUALIFIED: + case T_STRING: + $inlineName = $namespaceName.'\\'.$tokens[$calledClassName]['content']; + break; - $fullQualifiedClassName .= $phpcsFile->getDeclarationName($currScope); - } else { - $declarationName = $phpcsFile->getDeclarationName($currScope); - $fullQualifiedClassName = $tokens[$calledClassName]['content']; + case T_NAME_RELATIVE: + $inlineName = $namespaceName.substr($tokens[$calledClassName]['content'], 9); + break; } - if ($declarationName === $fullQualifiedClassName) { + if ($declarationName === $inlineName) { // Class name is the same as the current class, which is not allowed. $error = 'Must use "self::" for local static member reference'; $fix = $phpcsFile->addFixableError($error, $calledClassName, 'NotUsed'); if ($fix === true) { $phpcsFile->fixer->beginChangeset(); - - $currentPointer = ($stackPtr - 1); - while ($tokens[$currentPointer]['code'] === T_NS_SEPARATOR - || $tokens[$currentPointer]['code'] === T_STRING - || isset(Tokens::$emptyTokens[$tokens[$currentPointer]['code']]) === true - ) { - if (isset(Tokens::$emptyTokens[$tokens[$currentPointer]['code']]) === true) { - --$currentPointer; - continue; - } - - $phpcsFile->fixer->replaceToken($currentPointer, ''); - --$currentPointer; - } - + $phpcsFile->fixer->replaceToken($calledClassName, ''); $phpcsFile->fixer->replaceToken($stackPtr, 'self::'); $phpcsFile->fixer->endChangeset(); @@ -180,38 +172,7 @@ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr) /** - * Returns the declaration names for classes/interfaces/functions with a namespace. - * - * @param array $tokens Token stack for this file. - * @param int $stackPtr The position where the namespace building will start. - * - * @return string - */ - protected function getDeclarationNameWithNamespace(array $tokens, $stackPtr) - { - $nameParts = []; - $currentPointer = $stackPtr; - while ($tokens[$currentPointer]['code'] === T_NS_SEPARATOR - || $tokens[$currentPointer]['code'] === T_STRING - || isset(Tokens::$emptyTokens[$tokens[$currentPointer]['code']]) === true - ) { - if (isset(Tokens::$emptyTokens[$tokens[$currentPointer]['code']]) === true) { - --$currentPointer; - continue; - } - - $nameParts[] = $tokens[$currentPointer]['content']; - --$currentPointer; - } - - $nameParts = array_reverse($nameParts); - return implode('', $nameParts); - - }//end getDeclarationNameWithNamespace() - - - /** - * Returns the namespace declaration of a file. + * Returns the namespace name of the current scope. * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. * @param int $stackPtr The position where the search for the @@ -219,30 +180,25 @@ protected function getDeclarationNameWithNamespace(array $tokens, $stackPtr) * * @return string */ - protected function getNamespaceOfScope(File $phpcsFile, $stackPtr) + protected function getNamespaceName(File $phpcsFile, $stackPtr) { - $namespace = '\\'; - $tokens = $phpcsFile->getTokens(); + $namespace = '\\'; + $namespaceDeclaration = $phpcsFile->findPrevious(T_NAMESPACE, $stackPtr); - while (($namespaceDeclaration = $phpcsFile->findPrevious(T_NAMESPACE, $stackPtr)) !== false) { + if ($namespaceDeclaration !== false) { + $tokens = $phpcsFile->getTokens(); $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($namespaceDeclaration + 1), null, true); - if ($tokens[$nextNonEmpty]['code'] === T_NS_SEPARATOR) { - // Namespace operator. Ignore. - $stackPtr = ($namespaceDeclaration - 1); - continue; + if ($nextNonEmpty !== false + && ($tokens[$nextNonEmpty]['code'] === T_NAME_QUALIFIED + || $tokens[$nextNonEmpty]['code'] === T_STRING) + ) { + $namespace .= $tokens[$nextNonEmpty]['content']; } - - $endOfNamespaceDeclaration = $phpcsFile->findNext([T_SEMICOLON, T_OPEN_CURLY_BRACKET, T_CLOSE_TAG], $namespaceDeclaration); - $namespace = $this->getDeclarationNameWithNamespace( - $phpcsFile->getTokens(), - ($endOfNamespaceDeclaration - 1) - ); - break; } return $namespace; - }//end getNamespaceOfScope() + }//end getNamespaceName() }//end class diff --git a/src/Standards/Squiz/Sniffs/Commenting/FunctionCommentThrowTagSniff.php b/src/Standards/Squiz/Sniffs/Commenting/FunctionCommentThrowTagSniff.php index b6604a7fc6..ba2e7b2643 100644 --- a/src/Standards/Squiz/Sniffs/Commenting/FunctionCommentThrowTagSniff.php +++ b/src/Standards/Squiz/Sniffs/Commenting/FunctionCommentThrowTagSniff.php @@ -106,44 +106,30 @@ public function process(File $phpcsFile, $stackPtr) $nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($currPos + 1), null, true); if ($tokens[$nextToken]['code'] === T_NEW - || $tokens[$nextToken]['code'] === T_NS_SEPARATOR - || $tokens[$nextToken]['code'] === T_STRING + || isset(Tokens::$nameTokens[$tokens[$nextToken]['code']]) === true ) { if ($tokens[$nextToken]['code'] === T_NEW) { $currException = $phpcsFile->findNext( - [ - T_NS_SEPARATOR, - T_STRING, - ], - $currPos, + Tokens::$emptyTokens, + ($nextToken + 1), $stackPtrEnd, - false, - null, true ); } else { $currException = $nextToken; } - if ($currException !== false) { - $endException = $phpcsFile->findNext( - [ - T_NS_SEPARATOR, - T_STRING, - ], - ($currException + 1), - $stackPtrEnd, - true, - null, - true - ); - - if ($endException === false) { - $thrownExceptions[] = $tokens[$currException]['content']; + if ($currException !== false + && isset(Tokens::$nameTokens[$tokens[$currException]['code']]) === true + ) { + if ($tokens[$currException]['code'] === T_NAME_RELATIVE) { + // Strip the `namespace\` prefix off the exception name + // to prevent confusing the name comparison. + $thrownExceptions[] = substr($tokens[$currException]['content'], 10); } else { - $thrownExceptions[] = $phpcsFile->getTokensAsString($currException, ($endException - $currException)); + $thrownExceptions[] = $tokens[$currException]['content']; } - }//end if + } } else if ($tokens[$nextToken]['code'] === T_VARIABLE) { // Find the nearest catch block in this scope and, if the caught var // matches our re-thrown var, use the exception types being caught as diff --git a/src/Standards/Squiz/Sniffs/Commenting/VariableCommentSniff.php b/src/Standards/Squiz/Sniffs/Commenting/VariableCommentSniff.php index 6da78013ab..3eb10dd5ae 100644 --- a/src/Standards/Squiz/Sniffs/Commenting/VariableCommentSniff.php +++ b/src/Standards/Squiz/Sniffs/Commenting/VariableCommentSniff.php @@ -41,18 +41,15 @@ public function __construct() public function processMemberVar(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); - $ignore = [ - T_PUBLIC => T_PUBLIC, - T_PRIVATE => T_PRIVATE, - T_PROTECTED => T_PROTECTED, + + $ignore = Tokens::$scopeModifiers; + $ignore += Tokens::$nameTokens; + $ignore += [ T_VAR => T_VAR, T_STATIC => T_STATIC, T_READONLY => T_READONLY, T_FINAL => T_FINAL, T_WHITESPACE => T_WHITESPACE, - T_STRING => T_STRING, - T_NS_SEPARATOR => T_NS_SEPARATOR, - T_NAMESPACE => T_NAMESPACE, T_NULLABLE => T_NULLABLE, T_TYPE_UNION => T_TYPE_UNION, T_TYPE_INTERSECTION => T_TYPE_INTERSECTION, diff --git a/src/Standards/Squiz/Sniffs/Formatting/OperatorBracketSniff.php b/src/Standards/Squiz/Sniffs/Formatting/OperatorBracketSniff.php index aefb01da5d..d53cb2d15e 100644 --- a/src/Standards/Squiz/Sniffs/Formatting/OperatorBracketSniff.php +++ b/src/Standards/Squiz/Sniffs/Formatting/OperatorBracketSniff.php @@ -115,13 +115,13 @@ public function process(File $phpcsFile, $stackPtr) } // Tokens that are allowed inside a bracketed operation. - $allowed = [ + $allowed = Tokens::$nameTokens; + $allowed += Tokens::$operators; + $allowed += [ T_VARIABLE => T_VARIABLE, T_LNUMBER => T_LNUMBER, T_DNUMBER => T_DNUMBER, - T_STRING => T_STRING, T_WHITESPACE => T_WHITESPACE, - T_NS_SEPARATOR => T_NS_SEPARATOR, T_SELF => T_SELF, T_STATIC => T_STATIC, T_PARENT => T_PARENT, @@ -134,8 +134,6 @@ public function process(File $phpcsFile, $stackPtr) T_BITWISE_NOT => T_BITWISE_NOT, ]; - $allowed += Tokens::$operators; - $lastBracket = false; if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) { $parenthesis = array_reverse($tokens[$stackPtr]['nested_parenthesis'], true); @@ -149,7 +147,10 @@ public function process(File $phpcsFile, $stackPtr) break; } - if ($prevCode === T_STRING || $prevCode === T_SWITCH || $prevCode === T_MATCH) { + if (isset(Tokens::$nameTokens[$prevCode]) === true + || $prevCode === T_SWITCH + || $prevCode === T_MATCH + ) { // We allow simple operations to not be bracketed. // For example, ceil($one / $two). for ($prev = ($stackPtr - 1); $prev > $bracket; $prev--) { @@ -260,11 +261,9 @@ public function addMissingBracketsError($phpcsFile, $stackPtr) T_VARIABLE => true, T_LNUMBER => true, T_DNUMBER => true, - T_STRING => true, T_CONSTANT_ENCAPSED_STRING => true, T_DOUBLE_QUOTED_STRING => true, T_WHITESPACE => true, - T_NS_SEPARATOR => true, T_SELF => true, T_STATIC => true, T_OBJECT_OPERATOR => true, @@ -281,6 +280,7 @@ public function addMissingBracketsError($phpcsFile, $stackPtr) if (isset(Tokens::$emptyTokens[$tokens[$before]['code']]) === true || isset(Tokens::$operators[$tokens[$before]['code']]) === true || isset(Tokens::$castTokens[$tokens[$before]['code']]) === true + || isset(Tokens::$nameTokens[$tokens[$before]['code']]) === true || isset($allowed[$tokens[$before]['code']]) === true ) { continue; @@ -315,6 +315,7 @@ public function addMissingBracketsError($phpcsFile, $stackPtr) if (isset(Tokens::$emptyTokens[$tokens[$after]['code']]) === true || isset(Tokens::$operators[$tokens[$after]['code']]) === true || isset(Tokens::$castTokens[$tokens[$after]['code']]) === true + || isset(Tokens::$nameTokens[$tokens[$after]['code']]) === true || isset($allowed[$tokens[$after]['code']]) === true ) { continue; diff --git a/src/Standards/Squiz/Sniffs/PHP/DisallowComparisonAssignmentSniff.php b/src/Standards/Squiz/Sniffs/PHP/DisallowComparisonAssignmentSniff.php index 57dabbf12e..d29fbaba9f 100644 --- a/src/Standards/Squiz/Sniffs/PHP/DisallowComparisonAssignmentSniff.php +++ b/src/Standards/Squiz/Sniffs/PHP/DisallowComparisonAssignmentSniff.php @@ -68,18 +68,16 @@ public function process(File $phpcsFile, $stackPtr) } // Ignore function calls. - $ignore = [ - T_NULLSAFE_OBJECT_OPERATOR, - T_OBJECT_OPERATOR, - T_STRING, - T_VARIABLE, - T_WHITESPACE, - ]; + $ignore = Tokens::$nameTokens; + $ignore[] = T_NULLSAFE_OBJECT_OPERATOR; + $ignore[] = T_OBJECT_OPERATOR; + $ignore[] = T_VARIABLE; + $ignore[] = T_WHITESPACE; $next = $phpcsFile->findNext($ignore, ($stackPtr + 1), null, true); if ($tokens[$next]['code'] === T_CLOSURE || ($tokens[$next]['code'] === T_OPEN_PARENTHESIS - && $tokens[($next - 1)]['code'] === T_STRING) + && isset(Tokens::$nameTokens[$tokens[($next - 1)]['code']]) === true) ) { // Code will look like: $var = myFunction( // and will be ignored. diff --git a/src/Standards/Squiz/Sniffs/PHP/DisallowMultipleAssignmentsSniff.php b/src/Standards/Squiz/Sniffs/PHP/DisallowMultipleAssignmentsSniff.php index 86ecde9a06..3dcd24b782 100644 --- a/src/Standards/Squiz/Sniffs/PHP/DisallowMultipleAssignmentsSniff.php +++ b/src/Standards/Squiz/Sniffs/PHP/DisallowMultipleAssignmentsSniff.php @@ -124,10 +124,9 @@ public function process(File $phpcsFile, $stackPtr) $start = $phpcsFile->findStartOfStatement($varToken); - $allowed = Tokens::$emptyTokens; + $allowed = Tokens::$emptyTokens; + $allowed += Tokens::$nameTokens; - $allowed[T_STRING] = T_STRING; - $allowed[T_NS_SEPARATOR] = T_NS_SEPARATOR; $allowed[T_DOUBLE_COLON] = T_DOUBLE_COLON; $allowed[T_ASPERAND] = T_ASPERAND; $allowed[T_DOLLAR] = T_DOLLAR; diff --git a/src/Standards/Squiz/Sniffs/PHP/DisallowSizeFunctionsInLoopsSniff.php b/src/Standards/Squiz/Sniffs/PHP/DisallowSizeFunctionsInLoopsSniff.php index 95142b5f3d..b95dd61cce 100644 --- a/src/Standards/Squiz/Sniffs/PHP/DisallowSizeFunctionsInLoopsSniff.php +++ b/src/Standards/Squiz/Sniffs/PHP/DisallowSizeFunctionsInLoopsSniff.php @@ -67,8 +67,8 @@ public function process(File $phpcsFile, $stackPtr) } for ($i = ($start + 1); $i < $end; $i++) { - if ($tokens[$i]['code'] === T_STRING - && isset($this->forbiddenFunctions[$tokens[$i]['content']]) === true + if (($tokens[$i]['code'] === T_STRING || $tokens[$i]['code'] === T_NAME_FULLY_QUALIFIED) + && isset($this->forbiddenFunctions[ltrim($tokens[$i]['content'], '\\')]) === true ) { $functionName = $tokens[$i]['content']; diff --git a/src/Standards/Squiz/Sniffs/PHP/LowercasePHPFunctionsSniff.php b/src/Standards/Squiz/Sniffs/PHP/LowercasePHPFunctionsSniff.php index 0b671d6291..44fbf3ab50 100644 --- a/src/Standards/Squiz/Sniffs/PHP/LowercasePHPFunctionsSniff.php +++ b/src/Standards/Squiz/Sniffs/PHP/LowercasePHPFunctionsSniff.php @@ -43,7 +43,10 @@ public function __construct() */ public function register() { - return [T_STRING]; + return [ + T_STRING, + T_NAME_FULLY_QUALIFIED, + ]; }//end register() @@ -61,7 +64,11 @@ public function process(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); - $content = $tokens[$stackPtr]['content']; + $content = $tokens[$stackPtr]['content']; + if ($tokens[$stackPtr]['code'] === T_NAME_FULLY_QUALIFIED) { + $content = ltrim($content, '\\'); + } + $contentLc = strtolower($content); if ($content === $contentLc) { return; @@ -93,7 +100,7 @@ public function process(File $phpcsFile, $stackPtr) if ($tokens[$next]['code'] !== T_OPEN_PARENTHESIS) { // Is this a use statement importing a PHP native function ? - if ($tokens[$next]['code'] !== T_NS_SEPARATOR + if ($tokens[$stackPtr]['code'] === T_STRING && $tokens[$prev]['code'] === T_STRING && $tokens[$prev]['content'] === 'function' && $prevPrev !== false @@ -120,19 +127,6 @@ public function process(File $phpcsFile, $stackPtr) return; } - if ($tokens[$prev]['code'] === T_NS_SEPARATOR) { - if ($prevPrev !== false - && ($tokens[$prevPrev]['code'] === T_STRING - || $tokens[$prevPrev]['code'] === T_NAMESPACE - || $tokens[$prevPrev]['code'] === T_NEW) - ) { - // Namespaced class/function, not an inbuilt function. - // Could potentially give false negatives for non-namespaced files - // when namespace\functionName() is encountered. - return; - } - } - if ($tokens[$prev]['code'] === T_NEW) { // Object creation, not an inbuilt function. return; @@ -158,7 +152,7 @@ public function process(File $phpcsFile, $stackPtr) $fix = $phpcsFile->addFixableError($error, $stackPtr, 'CallUppercase', $data); if ($fix === true) { - $phpcsFile->fixer->replaceToken($stackPtr, $contentLc); + $phpcsFile->fixer->replaceToken($stackPtr, strtolower($tokens[$stackPtr]['content'])); } }//end process() diff --git a/src/Standards/Squiz/Tests/Classes/ClassDeclarationUnitTest.inc b/src/Standards/Squiz/Tests/Classes/ClassDeclarationUnitTest.inc index 25d8d30211..0b43f96eeb 100644 --- a/src/Standards/Squiz/Tests/Classes/ClassDeclarationUnitTest.inc +++ b/src/Standards/Squiz/Tests/Classes/ClassDeclarationUnitTest.inc @@ -56,7 +56,7 @@ class ClassOne implements ClassTwo, ClassThree { }//end class -class ClassOne implements ClassFour ,ClassFive, ClassSix +class ClassOne implements ClassFour ,\ClassFive, namespace\ClassSix { }//end class diff --git a/src/Standards/Squiz/Tests/Classes/ClassDeclarationUnitTest.inc.fixed b/src/Standards/Squiz/Tests/Classes/ClassDeclarationUnitTest.inc.fixed index bf042f932d..e0a0dac4f9 100644 --- a/src/Standards/Squiz/Tests/Classes/ClassDeclarationUnitTest.inc.fixed +++ b/src/Standards/Squiz/Tests/Classes/ClassDeclarationUnitTest.inc.fixed @@ -65,7 +65,7 @@ class ClassOne implements ClassTwo, ClassThree { }//end class -class ClassOne implements ClassFour, ClassFive, ClassSix +class ClassOne implements ClassFour, \ClassFive, namespace\ClassSix { }//end class diff --git a/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.inc b/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.inc index 4f17813866..00aa5922e0 100644 --- a/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.inc +++ b/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.inc @@ -159,16 +159,16 @@ class Nested_Anon_Class { namespace Foo\Baz { class BarFoo { public function foo() { - echo Foo\Baz\BarFoo::$prop; + echo \Foo\Baz\BarFoo::$prop; } } } -// Prevent false negative when namespace has whitespace/comments. +// Prevent false negative when namespace has whitespace/comments - no longer supported as of PHPCS 4.x. namespace Foo /*comment*/ \ Bah { class BarFoo { public function foo() { - echo Foo \ /*comment*/ Bah\BarFoo::$prop; + echo \Foo \ /*comment*/ Bah\BarFoo::$prop; } } } diff --git a/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.inc.fixed b/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.inc.fixed index 770429dc82..ad370890b1 100644 --- a/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.inc.fixed +++ b/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.inc.fixed @@ -152,11 +152,11 @@ namespace Foo\Baz { } } -// Prevent false negative when namespace has whitespace/comments. +// Prevent false negative when namespace has whitespace/comments - no longer supported as of PHPCS 4.x. namespace Foo /*comment*/ \ Bah { class BarFoo { public function foo() { - echo /*comment*/ self::$prop; + echo \Foo \ /*comment*/ Bah\BarFoo::$prop; } } } diff --git a/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.php b/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.php index 0f4aa5cf6a..69140bc4f9 100644 --- a/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.php +++ b/src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.php @@ -45,7 +45,6 @@ public function getErrorList() 140 => 1, 143 => 2, 162 => 1, - 171 => 1, 183 => 1, 197 => 1, ]; diff --git a/src/Standards/Squiz/Tests/Commenting/FunctionCommentThrowTagUnitTest.inc b/src/Standards/Squiz/Tests/Commenting/FunctionCommentThrowTagUnitTest.inc index 25da5021da..77946bcdd7 100644 --- a/src/Standards/Squiz/Tests/Commenting/FunctionCommentThrowTagUnitTest.inc +++ b/src/Standards/Squiz/Tests/Commenting/FunctionCommentThrowTagUnitTest.inc @@ -281,7 +281,7 @@ class NamespacedException { public function fooBar2() { throw new \Foo\Bar\FooBarException(); } - + /** * @throws FooBarException */ @@ -531,3 +531,21 @@ $anon = new class { public function ImproveCommentTolerance() { throw /*comment*/ new /*comment*/ Right_Exception('Error'); } + +namespace Namespaced\Names { + class NamespacedNames { + /** + * @throws Namespaced\Names\Exception + */ + public function foo() { + throw new namespace\Exception(); + } + + /** + * @throws Namespaced\Names\Bar\FooBarException + */ + public function fooBar2() { + throw new Bar\FooBarException(); + } + } +} diff --git a/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc b/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc index b849029e6d..170b84fe69 100644 --- a/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc +++ b/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc @@ -453,6 +453,13 @@ class MoreMissingButSupportedTypes * @var SomeClass */ private namespace\SomeClass $variableName; + + public static ?\Folder\ClassName $FQNTypeDecl_noComment = null; + + /** + * @var \Folder\ClassName|null + */ + public ?\Folder\ClassName $FQNTypeDecl_withComment = null; } class DNFTypes diff --git a/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc.fixed b/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc.fixed index 0a57d89e06..0b3c5a4b9e 100644 --- a/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc.fixed +++ b/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.inc.fixed @@ -453,6 +453,13 @@ class MoreMissingButSupportedTypes * @var SomeClass */ private namespace\SomeClass $variableName; + + public static ?\Folder\ClassName $FQNTypeDecl_noComment = null; + + /** + * @var \Folder\ClassName|null + */ + public ?\Folder\ClassName $FQNTypeDecl_withComment = null; } class DNFTypes diff --git a/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.php b/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.php index 4af31197ae..01691216ca 100644 --- a/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.php +++ b/src/Standards/Squiz/Tests/Commenting/VariableCommentUnitTest.php @@ -65,6 +65,7 @@ public function getErrorList() 364 => 1, 399 => 1, 403 => 1, + 457 => 1, ]; }//end getErrorList() diff --git a/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.1.inc b/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.1.inc index 8e62896387..b2dd790f9c 100644 --- a/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.1.inc +++ b/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.1.inc @@ -146,7 +146,7 @@ $cntPages = ceil(count($items) / self::ON_PAGE); error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING); error_reporting(E_ALL & ~E_NOTICE | ~E_WARNING); -$results = $stmt->fetchAll(\PDO::FETCH_ASSOC | \PDO::FETCH_GROUP | \PDO::FETCH_UNIQUE); +$results = $stmt->fetchAll(\PDO::FETCH_ASSOC | Relative\PDO::FETCH_GROUP | namespace\PDO::FETCH_UNIQUE); $di = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS); foo(1 + 2 + 3); @@ -201,3 +201,7 @@ match ($a) { }; $cntPages = ceil(count($items) / parent::ON_PAGE); + +$nsRelative = namespace\doSomething($items + 10); +$partiallyQualified = Partially\qualified($items + 10); +$fullyQualified = \Fully\qualified($items + 10); diff --git a/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.1.inc.fixed b/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.1.inc.fixed index 9fa0216cb6..fe7d4ef3d3 100644 --- a/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.1.inc.fixed +++ b/src/Standards/Squiz/Tests/Formatting/OperatorBracketUnitTest.1.inc.fixed @@ -146,7 +146,7 @@ $cntPages = ceil(count($items) / self::ON_PAGE); error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING); error_reporting(E_ALL & ~E_NOTICE | ~E_WARNING); -$results = $stmt->fetchAll(\PDO::FETCH_ASSOC | \PDO::FETCH_GROUP | \PDO::FETCH_UNIQUE); +$results = $stmt->fetchAll(\PDO::FETCH_ASSOC | Relative\PDO::FETCH_GROUP | namespace\PDO::FETCH_UNIQUE); $di = new \RecursiveDirectoryIterator($path, (\RecursiveDirectoryIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS)); foo(1 + 2 + 3); @@ -201,3 +201,7 @@ match ($a) { }; $cntPages = ceil(count($items) / parent::ON_PAGE); + +$nsRelative = namespace\doSomething($items + 10); +$partiallyQualified = Partially\qualified($items + 10); +$fullyQualified = \Fully\qualified($items + 10); diff --git a/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.inc b/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.inc index 8522438dcf..49084540d7 100644 --- a/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.inc +++ b/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.inc @@ -136,3 +136,13 @@ if (empty($argTags > 0)) { } myFunction($var1 === true ? "" : "foobar"); + +if (unqualified($argTags > 0) === true) {} +if (Partially\qualified($argTags > 0) === false) {} +if (\Fully\qualified($argTags > 0) === true) {} +if (namespace\relative($argTags > 0) === false) {} + +if (unqualified($argTags > 0)) {} +if (Partially\qualified($argTags > 0)) {} +if (\Fully\qualified($argTags > 0)) {} +if (namespace\relative($argTags > 0)) {} diff --git a/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.php b/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.php index 2d6dbd329c..ca5864fdde 100644 --- a/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.php +++ b/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.php @@ -59,6 +59,10 @@ public function getErrorList() 127 => 1, 131 => 1, 135 => 1, + 145 => 1, + 146 => 1, + 147 => 1, + 148 => 1, ]; }//end getErrorList() diff --git a/src/Standards/Squiz/Tests/PHP/DisallowComparisonAssignmentUnitTest.inc b/src/Standards/Squiz/Tests/PHP/DisallowComparisonAssignmentUnitTest.inc index a07047b196..d3e0e5d58d 100644 --- a/src/Standards/Squiz/Tests/PHP/DisallowComparisonAssignmentUnitTest.inc +++ b/src/Standards/Squiz/Tests/PHP/DisallowComparisonAssignmentUnitTest.inc @@ -81,3 +81,7 @@ function issue3616() { $food === 'cake' => 'This food is a cake', }; } + +$var = \Fully\qualified(!$var); +$var = Partially\qualified(!$var); +$var = namespace\relative(!$var); diff --git a/src/Standards/Squiz/Tests/PHP/DisallowSizeFunctionsInLoopsUnitTest.inc b/src/Standards/Squiz/Tests/PHP/DisallowSizeFunctionsInLoopsUnitTest.inc index 56802e37aa..cb3235982a 100644 --- a/src/Standards/Squiz/Tests/PHP/DisallowSizeFunctionsInLoopsUnitTest.inc +++ b/src/Standards/Squiz/Tests/PHP/DisallowSizeFunctionsInLoopsUnitTest.inc @@ -56,3 +56,8 @@ do { for ($i = 0; $i < $a->count; $i++) {} for ($i = 0; $i < $a?->count; $i++) {} + +for ($i = 0; $i < \sizeof($this->children); $i++) {} +while ($i < \count($array)) {} +do { +} while ($i < \strlen($string)); diff --git a/src/Standards/Squiz/Tests/PHP/DisallowSizeFunctionsInLoopsUnitTest.php b/src/Standards/Squiz/Tests/PHP/DisallowSizeFunctionsInLoopsUnitTest.php index d28f279ee3..a9241f35bc 100644 --- a/src/Standards/Squiz/Tests/PHP/DisallowSizeFunctionsInLoopsUnitTest.php +++ b/src/Standards/Squiz/Tests/PHP/DisallowSizeFunctionsInLoopsUnitTest.php @@ -43,6 +43,9 @@ public function getErrorList() 40 => 1, 44 => 1, 46 => 1, + 60 => 1, + 61 => 1, + 63 => 1, ]; }//end getErrorList() diff --git a/src/Standards/Squiz/Tests/PHP/LowercasePHPFunctionsUnitTest.inc b/src/Standards/Squiz/Tests/PHP/LowercasePHPFunctionsUnitTest.inc index 702b13de0a..99b6246ba9 100644 --- a/src/Standards/Squiz/Tests/PHP/LowercasePHPFunctionsUnitTest.inc +++ b/src/Standards/Squiz/Tests/PHP/LowercasePHPFunctionsUnitTest.inc @@ -33,9 +33,9 @@ class ArrayUnique {} $sillyComments = strToLower /*comment*/ ($string); $callToGlobalFunction = \STR_REPEAT($a, 2); -$callToGlobalFunction = \ /*comment*/ str_Repeat($a, 2); +$callToGlobalFunction = \str_Repeat($a, 2); -$callToNamespacedFunction = MyNamespace /* phpcs:ignore Standard */ \STR_REPEAT($a, 2); +$callToNamespacedFunction = MyNamespace\STR_REPEAT($a, 2); $callToNamespacedFunction = namespace\STR_REPEAT($a, 2); // Could potentially be false negative. $filePath = new \File($path); diff --git a/src/Standards/Squiz/Tests/PHP/LowercasePHPFunctionsUnitTest.inc.fixed b/src/Standards/Squiz/Tests/PHP/LowercasePHPFunctionsUnitTest.inc.fixed index 281425c595..67aa475930 100644 --- a/src/Standards/Squiz/Tests/PHP/LowercasePHPFunctionsUnitTest.inc.fixed +++ b/src/Standards/Squiz/Tests/PHP/LowercasePHPFunctionsUnitTest.inc.fixed @@ -33,9 +33,9 @@ class ArrayUnique {} $sillyComments = strtolower /*comment*/ ($string); $callToGlobalFunction = \str_repeat($a, 2); -$callToGlobalFunction = \ /*comment*/ str_repeat($a, 2); +$callToGlobalFunction = \str_repeat($a, 2); -$callToNamespacedFunction = MyNamespace /* phpcs:ignore Standard */ \STR_REPEAT($a, 2); +$callToNamespacedFunction = MyNamespace\STR_REPEAT($a, 2); $callToNamespacedFunction = namespace\STR_REPEAT($a, 2); // Could potentially be false negative. $filePath = new \File($path); diff --git a/src/Tokenizers/PHP.php b/src/Tokenizers/PHP.php index 64a6504a6f..7b61af3f3f 100644 --- a/src/Tokenizers/PHP.php +++ b/src/Tokenizers/PHP.php @@ -16,6 +16,13 @@ class PHP extends Tokenizer { + /** + * Regular expression to check if a given identifier name is valid for use in PHP. + * + * @var string + */ + private const PHP_LABEL_REGEX = '`^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$`'; + /** * A list of tokens that are allowed to open a scope. * @@ -1298,78 +1305,89 @@ protected function tokenize($string) }//end if /* - As of PHP 8.0 fully qualified, partially qualified and namespace relative - identifier names are tokenized differently. - This "undoes" the new tokenization so the tokenization will be the same in - in PHP 5, 7 and 8. + Before PHP 8.0, namespaced names were not tokenized as a single token. + + Note: reserved keywords are allowed within the "single token" names, so + no check is done on the token type following a namespace separator _on purpose_. + As long as it is not an empty token and the token contents complies with the + "name" requirements in PHP, we'll accept it. */ - if (PHP_VERSION_ID >= 80000 + if (PHP_VERSION_ID < 80000 && $tokenIsArray === true - && ($token[0] === T_NAME_QUALIFIED - || $token[0] === T_NAME_FULLY_QUALIFIED - || $token[0] === T_NAME_RELATIVE) + && ($token[0] === T_STRING + || $token[0] === T_NAMESPACE + || ($token[0] === T_NS_SEPARATOR + && isset($tokens[($stackPtr + 1)]) === true + && is_array($tokens[($stackPtr + 1)]) === true + && isset(Tokens::$emptyTokens[$tokens[($stackPtr + 1)][0]]) === false + && preg_match(self::PHP_LABEL_REGEX, $tokens[($stackPtr + 1)][1]) === 1)) ) { - $name = $token[1]; - - if ($token[0] === T_NAME_FULLY_QUALIFIED) { - $newToken = []; - $newToken['code'] = T_NS_SEPARATOR; - $newToken['type'] = 'T_NS_SEPARATOR'; - $newToken['content'] = '\\'; - $finalTokens[$newStackPtr] = $newToken; - ++$newStackPtr; + $nameStart = $stackPtr; + $i = $stackPtr; + $newToken = []; + $newToken['content'] = $token[1]; - $name = ltrim($name, '\\'); - } + switch ($token[0]) { + case T_STRING: + $newToken['code'] = T_NAME_QUALIFIED; + $newToken['type'] = 'T_NAME_QUALIFIED'; + break; + case T_NAMESPACE: + $newToken['code'] = T_NAME_RELATIVE; + $newToken['type'] = 'T_NAME_RELATIVE'; + break; + case T_NS_SEPARATOR: + $newToken['code'] = T_NAME_FULLY_QUALIFIED; + $newToken['type'] = 'T_NAME_FULLY_QUALIFIED'; - if ($token[0] === T_NAME_RELATIVE) { - $newToken = []; - $newToken['code'] = T_NAMESPACE; - $newToken['type'] = 'T_NAMESPACE'; - $newToken['content'] = substr($name, 0, 9); - $finalTokens[$newStackPtr] = $newToken; - ++$newStackPtr; + if (is_array($tokens[($i - 1)]) === true + && isset(Tokens::$emptyTokens[$tokens[($i - 1)][0]]) === false + && preg_match(self::PHP_LABEL_REGEX, $tokens[($i - 1)][1]) === 1 + ) { + // The namespaced name starts with a reserved keyword. Move one token back. + $newToken['code'] = T_NAME_QUALIFIED; + $newToken['type'] = 'T_NAME_QUALIFIED'; + $newToken['content'] = $tokens[($i - 1)][1]; + --$nameStart; + --$i; + break; + } - $newToken = []; - $newToken['code'] = T_NS_SEPARATOR; - $newToken['type'] = 'T_NS_SEPARATOR'; - $newToken['content'] = '\\'; - $finalTokens[$newStackPtr] = $newToken; - ++$newStackPtr; + ++$i; + $newToken['content'] .= $tokens[$i][1]; + break; + }//end switch - $name = substr($name, 10); + while (isset($tokens[($i + 1)], $tokens[($i + 2)]) === true + && is_array($tokens[($i + 1)]) === true && $tokens[($i + 1)][0] === T_NS_SEPARATOR + && is_array($tokens[($i + 2)]) === true + && isset(Tokens::$emptyTokens[$tokens[($i + 2)][0]]) === false + && preg_match(self::PHP_LABEL_REGEX, $tokens[($i + 2)][1]) === 1 + ) { + $newToken['content'] .= $tokens[($i + 1)][1].$tokens[($i + 2)][1]; + $i = ($i + 2); } - $parts = explode('\\', $name); - $partCount = count($parts); - $lastPart = ($partCount - 1); + if ($i !== $nameStart) { + if ($nameStart !== $stackPtr) { + // This must be a qualified name starting with a reserved keyword. + // We need to overwrite the previously set final token. + --$newStackPtr; + } - foreach ($parts as $i => $part) { - $newToken = []; - $newToken['code'] = T_STRING; - $newToken['type'] = 'T_STRING'; - $newToken['content'] = $part; $finalTokens[$newStackPtr] = $newToken; - ++$newStackPtr; + $newStackPtr++; + $stackPtr = $i; - if ($i !== $lastPart) { - $newToken = []; - $newToken['code'] = T_NS_SEPARATOR; - $newToken['type'] = 'T_NS_SEPARATOR'; - $newToken['content'] = '\\'; - $finalTokens[$newStackPtr] = $newToken; - ++$newStackPtr; + if (PHP_CODESNIFFER_VERBOSITY > 1) { + $type = $newToken['type']; + $content = $newToken['content']; + StatusWriter::write("* token $nameStart to $i ($content) retokenized to $type", 2); } - } - - if (PHP_CODESNIFFER_VERBOSITY > 1) { - $type = Tokens::tokenName($token[0]); - $content = Common::prepareForOutput($token[1]); - StatusWriter::write("* token $stackPtr split into individual tokens; was: $type => $content", 2); - } - continue; + continue; + }//end if }//end if /* @@ -2000,10 +2018,7 @@ protected function tokenize($string) continue; } - if ($tokenType === T_STRING - || $tokenType === T_NAME_FULLY_QUALIFIED - || $tokenType === T_NAME_RELATIVE - || $tokenType === T_NAME_QUALIFIED + if (isset(Tokens::$nameTokens[$tokenType]) === true || $tokenType === T_ARRAY || $tokenType === T_NAMESPACE || $tokenType === T_NS_SEPARATOR @@ -2016,10 +2031,7 @@ protected function tokenize($string) && isset($lastRelevantNonEmpty) === false) || ($lastRelevantNonEmpty === T_ARRAY && $tokenType === '(') - || (($lastRelevantNonEmpty === T_STRING - || $lastRelevantNonEmpty === T_NAME_FULLY_QUALIFIED - || $lastRelevantNonEmpty === T_NAME_RELATIVE - || $lastRelevantNonEmpty === T_NAME_QUALIFIED) + || (isset(Tokens::$nameTokens[$lastRelevantNonEmpty]) === true && ($tokenType === T_DOUBLE_COLON || $tokenType === '(' || $tokenType === ':')) @@ -2759,12 +2771,11 @@ protected function processAdditional() && isset($this->tokens[$x]['parenthesis_closer']) === true ) { $ignore = Tokens::$emptyTokens; + $ignore += Tokens::$nameTokens; $ignore += [ T_ARRAY => T_ARRAY, T_CALLABLE => T_CALLABLE, T_COLON => T_COLON, - T_NAMESPACE => T_NAMESPACE, - T_NS_SEPARATOR => T_NS_SEPARATOR, T_NULL => T_NULL, T_TRUE => T_TRUE, T_FALSE => T_FALSE, @@ -2772,7 +2783,6 @@ protected function processAdditional() T_PARENT => T_PARENT, T_SELF => T_SELF, T_STATIC => T_STATIC, - T_STRING => T_STRING, T_TYPE_UNION => T_TYPE_UNION, T_TYPE_INTERSECTION => T_TYPE_INTERSECTION, T_TYPE_OPEN_PARENTHESIS => T_TYPE_OPEN_PARENTHESIS, @@ -2954,14 +2964,14 @@ protected function processAdditional() // Unless there is a variable or a bracket before this token, // it is the start of an array being defined using the short syntax. $isShortArray = false; - $allowed = [ + $allowed = Tokens::$nameTokens; + $allowed += [ T_CLOSE_SQUARE_BRACKET => T_CLOSE_SQUARE_BRACKET, T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, T_CLOSE_PARENTHESIS => T_CLOSE_PARENTHESIS, T_VARIABLE => T_VARIABLE, T_OBJECT_OPERATOR => T_OBJECT_OPERATOR, T_NULLSAFE_OBJECT_OPERATOR => T_NULLSAFE_OBJECT_OPERATOR, - T_STRING => T_STRING, T_CONSTANT_ENCAPSED_STRING => T_CONSTANT_ENCAPSED_STRING, T_DOUBLE_QUOTED_STRING => T_DOUBLE_QUOTED_STRING, ]; @@ -3095,17 +3105,15 @@ protected function processAdditional() All type related tokens will be converted in one go as soon as this section is hit. */ - $allowed = [ - T_STRING => T_STRING, - T_CALLABLE => T_CALLABLE, - T_SELF => T_SELF, - T_PARENT => T_PARENT, - T_STATIC => T_STATIC, - T_FALSE => T_FALSE, - T_TRUE => T_TRUE, - T_NULL => T_NULL, - T_NAMESPACE => T_NAMESPACE, - T_NS_SEPARATOR => T_NS_SEPARATOR, + $allowed = Tokens::$nameTokens; + $allowed += [ + T_CALLABLE => T_CALLABLE, + T_SELF => T_SELF, + T_PARENT => T_PARENT, + T_STATIC => T_STATIC, + T_FALSE => T_FALSE, + T_TRUE => T_TRUE, + T_NULL => T_NULL, ]; $suspectedType = null; diff --git a/src/Tokenizers/Tokenizer.php b/src/Tokenizers/Tokenizer.php index 95bb44a585..eb10765173 100644 --- a/src/Tokenizers/Tokenizer.php +++ b/src/Tokenizers/Tokenizer.php @@ -1073,13 +1073,6 @@ private function recurseScopeMap($stackPtr, $depth=1, &$ignore=0) continue; } - if ($tokenType === T_NAMESPACE) { - // PHP namespace keywords are special because they can be - // used as blocks but also inline as operators. - // So if we find them nested inside another opener, just skip them. - continue; - } - if ($tokenType === T_FUNCTION && $this->tokens[$stackPtr]['code'] !== T_FUNCTION ) { diff --git a/src/Util/Tokens.php b/src/Util/Tokens.php index 420bda4b85..3179acb8cb 100644 --- a/src/Util/Tokens.php +++ b/src/Util/Tokens.php @@ -566,6 +566,18 @@ final class Tokens T_NOWDOC => T_NOWDOC, ]; + /** + * Tokens used for "names", be it namespace, OO, function or constant names. + * + * @var array + */ + public static $nameTokens = [ + T_STRING => T_STRING, + T_NAME_QUALIFIED => T_NAME_QUALIFIED, + T_NAME_FULLY_QUALIFIED => T_NAME_FULLY_QUALIFIED, + T_NAME_RELATIVE => T_NAME_RELATIVE, + ]; + /** * Tokens that represent the names of called functions. * @@ -575,19 +587,22 @@ final class Tokens * @var array */ public static $functionNameTokens = [ - T_STRING => T_STRING, - T_EVAL => T_EVAL, - T_EXIT => T_EXIT, - T_INCLUDE => T_INCLUDE, - T_INCLUDE_ONCE => T_INCLUDE_ONCE, - T_REQUIRE => T_REQUIRE, - T_REQUIRE_ONCE => T_REQUIRE_ONCE, - T_ISSET => T_ISSET, - T_UNSET => T_UNSET, - T_EMPTY => T_EMPTY, - T_SELF => T_SELF, - T_PARENT => T_PARENT, - T_STATIC => T_STATIC, + T_STRING => T_STRING, + T_NAME_QUALIFIED => T_NAME_QUALIFIED, + T_NAME_FULLY_QUALIFIED => T_NAME_FULLY_QUALIFIED, + T_NAME_RELATIVE => T_NAME_RELATIVE, + T_EVAL => T_EVAL, + T_EXIT => T_EXIT, + T_INCLUDE => T_INCLUDE, + T_INCLUDE_ONCE => T_INCLUDE_ONCE, + T_REQUIRE => T_REQUIRE, + T_REQUIRE_ONCE => T_REQUIRE_ONCE, + T_ISSET => T_ISSET, + T_UNSET => T_UNSET, + T_EMPTY => T_EMPTY, + T_SELF => T_SELF, + T_PARENT => T_PARENT, + T_STATIC => T_STATIC, ]; /** diff --git a/tests/Core/File/FindEndOfStatementTest.php b/tests/Core/File/FindEndOfStatementTest.php index 5475287934..3e73fe93aa 100644 --- a/tests/Core/File/FindEndOfStatementTest.php +++ b/tests/Core/File/FindEndOfStatementTest.php @@ -196,7 +196,7 @@ public function testUseGroup() $start = $this->getTargetToken('/* testUseGroup */', T_USE); $found = self::$phpcsFile->findEndOfStatement($start); - $this->assertSame(($start + 23), $found); + $this->assertSame(($start + 21), $found); }//end testUseGroup() diff --git a/tests/Core/File/FindExtendedClassNameTest.inc b/tests/Core/File/FindExtendedClassNameTest.inc index dae2cd9692..afa3ec83d1 100644 --- a/tests/Core/File/FindExtendedClassNameTest.inc +++ b/tests/Core/File/FindExtendedClassNameTest.inc @@ -15,6 +15,9 @@ class testFECNNamespacedClass extends \PHP_CodeSniffer\Tests\Core\File\testFECNC /* testExtendsPartiallyQualifiedClass */ class testFECNQualifiedClass extends Core\File\RelativeClass {} +/* testExtendsNamespaceRelativeClass */ +class testWithNSOperator extends namespace\Bar {} + /* testNonExtendedInterface */ interface testFECNInterface {} diff --git a/tests/Core/File/FindExtendedClassNameTest.php b/tests/Core/File/FindExtendedClassNameTest.php index 273fa2c305..1ae71b8058 100644 --- a/tests/Core/File/FindExtendedClassNameTest.php +++ b/tests/Core/File/FindExtendedClassNameTest.php @@ -93,6 +93,10 @@ public static function dataExtendedClass() 'identifier' => '/* testExtendsPartiallyQualifiedClass */', 'expected' => 'Core\File\RelativeClass', ], + 'class extends namespace relative class' => [ + 'identifier' => '/* testExtendsNamespaceRelativeClass */', + 'expected' => 'namespace\Bar', + ], 'interface does not extend' => [ 'identifier' => '/* testNonExtendedInterface */', 'expected' => false, diff --git a/tests/Core/File/FindImplementedInterfaceNamesTest.inc b/tests/Core/File/FindImplementedInterfaceNamesTest.inc index 3246efa2ce..50d38b88b2 100644 --- a/tests/Core/File/FindImplementedInterfaceNamesTest.inc +++ b/tests/Core/File/FindImplementedInterfaceNamesTest.inc @@ -21,6 +21,9 @@ class testFIINNamespacedClass implements \PHP_CodeSniffer\Tests\Core\File\testFI /* testImplementsPartiallyQualified */ class testFIINQualifiedClass implements Core\File\RelativeInterface {} +/* testImplementsMultipleNamespaceRelativeInterfaces */ +class testMultiImplementedNSOperator implements namespace\testInterfaceA, namespace\testInterfaceB {} + /* testClassThatExtendsAndImplements */ class testFECNClassThatExtendsAndImplements extends testFECNClass implements InterfaceA, \NameSpaced\Cat\InterfaceB {} diff --git a/tests/Core/File/FindImplementedInterfaceNamesTest.php b/tests/Core/File/FindImplementedInterfaceNamesTest.php index 7dfbf4044a..4077efef08 100644 --- a/tests/Core/File/FindImplementedInterfaceNamesTest.php +++ b/tests/Core/File/FindImplementedInterfaceNamesTest.php @@ -109,6 +109,13 @@ public static function dataImplementedInterface() 'Core\File\RelativeInterface', ], ], + 'class implements multiple interfaces, namespace relative' => [ + 'identifier' => '/* testImplementsMultipleNamespaceRelativeInterfaces */', + 'expected' => [ + 'namespace\testInterfaceA', + 'namespace\testInterfaceB', + ], + ], 'class extends and implements' => [ 'identifier' => '/* testClassThatExtendsAndImplements */', 'expected' => [ diff --git a/tests/Core/File/FindStartOfStatementTest.php b/tests/Core/File/FindStartOfStatementTest.php index cde94f2ba2..6da8234f9e 100644 --- a/tests/Core/File/FindStartOfStatementTest.php +++ b/tests/Core/File/FindStartOfStatementTest.php @@ -246,7 +246,7 @@ public function testUseGroup() $start = $this->getTargetToken('/* testUseGroup */', T_SEMICOLON); $found = self::$phpcsFile->findStartOfStatement($start); - $this->assertSame(($start - 23), $found); + $this->assertSame(($start - 21), $found); }//end testUseGroup() diff --git a/tests/Core/File/GetMemberPropertiesTest.php b/tests/Core/File/GetMemberPropertiesTest.php index 31ded2d7df..ff2fd7d462 100644 --- a/tests/Core/File/GetMemberPropertiesTest.php +++ b/tests/Core/File/GetMemberPropertiesTest.php @@ -548,7 +548,7 @@ public static function dataGetMemberProperties() 'is_readonly' => false, 'is_final' => false, 'type' => '\MyNamespace\MyClass', - 'type_token' => -5, + 'type_token' => -2, 'type_end_token' => -2, 'nullable_type' => false, ], @@ -576,7 +576,7 @@ public static function dataGetMemberProperties() 'is_readonly' => false, 'is_final' => false, 'type' => '?Folder\ClassName', - 'type_token' => -4, + 'type_token' => -2, 'type_end_token' => -2, 'nullable_type' => true, ], @@ -590,7 +590,7 @@ public static function dataGetMemberProperties() 'is_readonly' => false, 'is_final' => false, 'type' => '\MyNamespace\MyClass\Foo', - 'type_token' => -18, + 'type_token' => -15, 'type_end_token' => -2, 'nullable_type' => false, ], @@ -688,7 +688,7 @@ public static function dataGetMemberProperties() 'is_readonly' => false, 'is_final' => false, 'type' => '?namespace\Name', - 'type_token' => -4, + 'type_token' => -2, 'type_end_token' => -2, 'nullable_type' => true, ], @@ -716,7 +716,7 @@ public static function dataGetMemberProperties() 'is_readonly' => false, 'is_final' => false, 'type' => 'MyClassA|\Package\MyClassB', - 'type_token' => -7, + 'type_token' => -4, 'type_end_token' => -2, 'nullable_type' => false, ], @@ -941,7 +941,7 @@ public static function dataGetMemberProperties() 'is_readonly' => true, 'is_final' => false, 'type' => '\InterfaceA|\Sub\InterfaceB|false', - 'type_token' => -11, + 'type_token' => -7, 'type_end_token' => -3, 'nullable_type' => false, ], @@ -1096,7 +1096,7 @@ public static function dataGetMemberProperties() 'is_readonly' => false, 'is_final' => false, 'type' => '\Foo&Bar', - 'type_token' => -9, + 'type_token' => -8, 'type_end_token' => -2, 'nullable_type' => false, ], @@ -1167,7 +1167,7 @@ public static function dataGetMemberProperties() 'is_readonly' => false, 'is_final' => false, 'type' => '(Foo&\Bar)|bool', - 'type_token' => -9, + 'type_token' => -8, 'type_end_token' => -2, 'nullable_type' => false, ], @@ -1181,7 +1181,7 @@ public static function dataGetMemberProperties() 'is_readonly' => true, 'is_final' => false, 'type' => 'float|(Partially\Qualified&Traversable)', - 'type_token' => -10, + 'type_token' => -8, 'type_end_token' => -2, 'nullable_type' => false, ], @@ -1195,7 +1195,7 @@ public static function dataGetMemberProperties() 'is_readonly' => true, 'is_final' => false, 'type' => '(namespace\Foo&Bar)|string', - 'type_token' => -10, + 'type_token' => -8, 'type_end_token' => -2, 'nullable_type' => false, ], @@ -1209,7 +1209,7 @@ public static function dataGetMemberProperties() 'is_readonly' => false, 'is_final' => false, 'type' => '?(A&\Pck\B)|bool', - 'type_token' => -11, + 'type_token' => -8, 'type_end_token' => -2, 'nullable_type' => true, ], @@ -1335,7 +1335,7 @@ public static function dataGetMemberProperties() 'is_readonly' => false, 'is_final' => true, 'type' => '(Foo&\Bar)|bool', - 'type_token' => -9, + 'type_token' => -8, 'type_end_token' => -2, 'nullable_type' => false, ], diff --git a/tests/Core/File/GetMethodParametersTest.php b/tests/Core/File/GetMethodParametersTest.php index 9e8d5bc6bc..67703ac4bc 100644 --- a/tests/Core/File/GetMethodParametersTest.php +++ b/tests/Core/File/GetMethodParametersTest.php @@ -434,7 +434,7 @@ public function testNullableTypeHint() ]; $expected[1] = [ - 'token' => 14, + 'token' => 13, 'name' => '$var2', 'content' => '?\bar $var2', 'has_attributes' => false, @@ -444,7 +444,7 @@ public function testNullableTypeHint() 'variadic_token' => false, 'type_hint' => '?\bar', 'type_hint_token' => 11, - 'type_hint_end_token' => 12, + 'type_hint_end_token' => 11, 'nullable_type' => true, 'comma_token' => false, ]; @@ -825,7 +825,7 @@ public function testNameSpacedTypeDeclaration() // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'token' => 12, + 'token' => 7, 'name' => '$a', 'content' => '\Package\Sub\ClassName $a', 'has_attributes' => false, @@ -835,12 +835,12 @@ public function testNameSpacedTypeDeclaration() 'variadic_token' => false, 'type_hint' => '\Package\Sub\ClassName', 'type_hint_token' => 5, - 'type_hint_end_token' => 10, + 'type_hint_end_token' => 5, 'nullable_type' => false, - 'comma_token' => 13, + 'comma_token' => 8, ]; $expected[1] = [ - 'token' => 20, + 'token' => 13, 'name' => '$b', 'content' => '?Sub\AnotherClass $b', 'has_attributes' => false, @@ -849,8 +849,8 @@ public function testNameSpacedTypeDeclaration() 'variable_length' => false, 'variadic_token' => false, 'type_hint' => '?Sub\AnotherClass', - 'type_hint_token' => 16, - 'type_hint_end_token' => 18, + 'type_hint_token' => 11, + 'type_hint_end_token' => 11, 'nullable_type' => true, 'comma_token' => false, ]; @@ -1248,7 +1248,7 @@ public function testMessyDeclaration() // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'token' => 25, + 'token' => 24, 'name' => '$a', 'content' => '// comment ?\MyNS /* comment */ @@ -1261,17 +1261,17 @@ public function testMessyDeclaration() 'variadic_token' => false, 'type_hint' => '?\MyNS\SubCat\MyClass', 'type_hint_token' => 9, - 'type_hint_end_token' => 23, + 'type_hint_end_token' => 22, 'nullable_type' => true, - 'comma_token' => 26, + 'comma_token' => 25, ]; $expected[1] = [ - 'token' => 29, + 'token' => 28, 'name' => '$b', 'content' => "\$b /* comment */ = /* comment */ 'default' /* comment*/", 'default' => "'default' /* comment*/", - 'default_token' => 37, - 'default_equal_token' => 33, + 'default_token' => 36, + 'default_equal_token' => 32, 'has_attributes' => false, 'pass_by_reference' => false, 'reference_token' => false, @@ -1281,10 +1281,10 @@ public function testMessyDeclaration() 'type_hint_token' => false, 'type_hint_end_token' => false, 'nullable_type' => false, - 'comma_token' => 40, + 'comma_token' => 39, ]; $expected[2] = [ - 'token' => 62, + 'token' => 61, 'name' => '$c', 'content' => '// phpcs:ignore Stnd.Cat.Sniff -- For reasons. ? /*comment*/ @@ -1292,12 +1292,12 @@ public function testMessyDeclaration() & /*test*/ ... /* phpcs:ignore */ $c', 'has_attributes' => false, 'pass_by_reference' => true, - 'reference_token' => 54, + 'reference_token' => 53, 'variable_length' => true, - 'variadic_token' => 58, + 'variadic_token' => 57, 'type_hint' => '?bool', - 'type_hint_token' => 50, - 'type_hint_end_token' => 50, + 'type_hint_token' => 49, + 'type_hint_end_token' => 49, 'nullable_type' => true, 'comma_token' => false, ]; @@ -1377,7 +1377,7 @@ public function testNamespaceOperatorTypeHint() // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'token' => 9, + 'token' => 7, 'name' => '$var1', 'content' => '?namespace\Name $var1', 'has_attributes' => false, @@ -1387,7 +1387,7 @@ public function testNamespaceOperatorTypeHint() 'variadic_token' => false, 'type_hint' => '?namespace\Name', 'type_hint_token' => 5, - 'type_hint_end_token' => 7, + 'type_hint_end_token' => 5, 'nullable_type' => true, 'comma_token' => false, ]; @@ -1530,7 +1530,7 @@ public function testPHP8UnionTypesTwoClasses() // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'token' => 11, + 'token' => 8, 'name' => '$var', 'content' => 'MyClassA|\Package\MyClassB $var', 'has_attributes' => false, @@ -1540,7 +1540,7 @@ public function testPHP8UnionTypesTwoClasses() 'variadic_token' => false, 'type_hint' => 'MyClassA|\Package\MyClassB', 'type_hint_token' => 4, - 'type_hint_end_token' => 9, + 'type_hint_end_token' => 6, 'nullable_type' => false, 'comma_token' => false, ]; @@ -2318,7 +2318,7 @@ public function testParameterAttributesInFunctionDeclaration() // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'token' => 17, + 'token' => 14, 'name' => '$constructorPropPromTypedParamSingleAttribute', 'content' => '#[\MyExample\MyAttribute] private string $constructorPropPromTypedParamSingleAttribute', 'has_attributes' => true, @@ -2327,16 +2327,16 @@ public function testParameterAttributesInFunctionDeclaration() 'variable_length' => false, 'variadic_token' => false, 'type_hint' => 'string', - 'type_hint_token' => 15, - 'type_hint_end_token' => 15, + 'type_hint_token' => 12, + 'type_hint_end_token' => 12, 'nullable_type' => false, 'property_visibility' => 'private', - 'visibility_token' => 13, + 'visibility_token' => 10, 'property_readonly' => false, - 'comma_token' => 18, + 'comma_token' => 15, ]; $expected[1] = [ - 'token' => 39, + 'token' => 36, 'name' => '$typedParamSingleAttribute', 'content' => '#[MyAttr([1, 2])] Type|false @@ -2347,13 +2347,13 @@ public function testParameterAttributesInFunctionDeclaration() 'variable_length' => false, 'variadic_token' => false, 'type_hint' => 'Type|false', - 'type_hint_token' => 34, - 'type_hint_end_token' => 36, + 'type_hint_token' => 31, + 'type_hint_end_token' => 33, 'nullable_type' => false, - 'comma_token' => 40, + 'comma_token' => 37, ]; $expected[2] = [ - 'token' => 59, + 'token' => 56, 'name' => '$nullableTypedParamMultiAttribute', 'content' => '#[MyAttribute(1234), MyAttribute(5678)] ?int $nullableTypedParamMultiAttribute', 'has_attributes' => true, @@ -2362,13 +2362,13 @@ public function testParameterAttributesInFunctionDeclaration() 'variable_length' => false, 'variadic_token' => false, 'type_hint' => '?int', - 'type_hint_token' => 57, - 'type_hint_end_token' => 57, + 'type_hint_token' => 54, + 'type_hint_end_token' => 54, 'nullable_type' => true, - 'comma_token' => 60, + 'comma_token' => 57, ]; $expected[3] = [ - 'token' => 74, + 'token' => 71, 'name' => '$nonTypedParamTwoAttributes', 'content' => '#[WithoutArgument] #[SingleArgument(0)] $nonTypedParamTwoAttributes', 'has_attributes' => true, @@ -2380,23 +2380,23 @@ public function testParameterAttributesInFunctionDeclaration() 'type_hint_token' => false, 'type_hint_end_token' => false, 'nullable_type' => false, - 'comma_token' => 75, + 'comma_token' => 72, ]; $expected[4] = [ - 'token' => 95, + 'token' => 92, 'name' => '$otherParam', 'content' => '#[MyAttribute(array("key" => "value"))] &...$otherParam', 'has_attributes' => true, 'pass_by_reference' => true, - 'reference_token' => 93, + 'reference_token' => 90, 'variable_length' => true, - 'variadic_token' => 94, + 'variadic_token' => 91, 'type_hint' => '', 'type_hint_token' => false, 'type_hint_end_token' => false, 'nullable_type' => false, - 'comma_token' => 96, + 'comma_token' => 93, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -2505,7 +2505,7 @@ public function testPHP81MoreIntersectionTypes() // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'token' => 16, + 'token' => 10, 'name' => '$var', 'content' => 'MyClassA&\Package\MyClassB&\Package\MyClassC $var', 'has_attributes' => false, @@ -2515,7 +2515,7 @@ public function testPHP81MoreIntersectionTypes() 'variadic_token' => false, 'type_hint' => 'MyClassA&\Package\MyClassB&\Package\MyClassC', 'type_hint_token' => 4, - 'type_hint_end_token' => 14, + 'type_hint_end_token' => 8, 'nullable_type' => false, 'comma_token' => false, ]; @@ -2679,12 +2679,12 @@ public function testPHP81NewInInitializers() 'comma_token' => 20, ]; $expected[1] = [ - 'token' => 28, + 'token' => 25, 'name' => '$newToo', 'content' => '\Package\TypeB $newToo = new \Package\TypeB(10, \'string\')', 'default' => "new \Package\TypeB(10, 'string')", - 'default_token' => 32, - 'default_equal_token' => 30, + 'default_token' => 29, + 'default_equal_token' => 27, 'has_attributes' => false, 'pass_by_reference' => false, 'reference_token' => false, @@ -2692,9 +2692,9 @@ public function testPHP81NewInInitializers() 'variadic_token' => false, 'type_hint' => '\Package\TypeB', 'type_hint_token' => 23, - 'type_hint_end_token' => 26, + 'type_hint_end_token' => 23, 'nullable_type' => false, - 'comma_token' => 44, + 'comma_token' => 38, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -2728,12 +2728,12 @@ public function testPHP82DNFTypes() 'comma_token' => 22, ]; $expected[1] = [ - 'token' => 41, + 'token' => 37, 'name' => '$obj2', 'content' => '(\Boo&\Pck\Bar)|(Boo&Baz) $obj2 = new Boo()', 'default' => 'new Boo()', - 'default_token' => 45, - 'default_equal_token' => 43, + 'default_token' => 41, + 'default_equal_token' => 39, 'has_attributes' => false, 'pass_by_reference' => false, 'reference_token' => false, @@ -2741,7 +2741,7 @@ public function testPHP82DNFTypes() 'variadic_token' => false, 'type_hint' => '(\Boo&\Pck\Bar)|(Boo&Baz)', 'type_hint_token' => 25, - 'type_hint_end_token' => 39, + 'type_hint_end_token' => 35, 'nullable_type' => false, 'comma_token' => false, ]; @@ -2807,7 +2807,7 @@ public function testPHP82DNFTypesIllegalNullable() // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'token' => 27, + 'token' => 21, 'name' => '$var', 'content' => '? ( MyClassA & /*comment*/ \Package\MyClassB & \Package\MyClassC ) $var', 'has_attributes' => false, @@ -2817,7 +2817,7 @@ public function testPHP82DNFTypesIllegalNullable() 'variadic_token' => false, 'type_hint' => '?(MyClassA&\Package\MyClassB&\Package\MyClassC)', 'type_hint_token' => 5, - 'type_hint_end_token' => 25, + 'type_hint_end_token' => 19, 'nullable_type' => true, 'comma_token' => false, ]; diff --git a/tests/Core/File/GetMethodPropertiesTest.php b/tests/Core/File/GetMethodPropertiesTest.php index 2dd83328ea..29ef00f7c6 100644 --- a/tests/Core/File/GetMethodPropertiesTest.php +++ b/tests/Core/File/GetMethodPropertiesTest.php @@ -342,7 +342,7 @@ public function testReturnNamespace() 'scope_specified' => false, 'return_type' => '\MyNamespace\MyClass', 'return_type_token' => 7, - 'return_type_end_token' => 10, + 'return_type_end_token' => 7, 'nullable_return_type' => false, 'is_abstract' => false, 'is_final' => false, @@ -368,7 +368,7 @@ public function testReturnMultilineNamespace() 'scope_specified' => false, 'return_type' => '\MyNamespace\MyClass\Foo', 'return_type_token' => 7, - 'return_type_end_token' => 23, + 'return_type_end_token' => 20, 'nullable_return_type' => false, 'is_abstract' => false, 'is_final' => false, @@ -420,7 +420,7 @@ public function testReturnPartiallyQualifiedName() 'scope_specified' => false, 'return_type' => 'Sub\Level\MyClass', 'return_type_token' => 7, - 'return_type_end_token' => 11, + 'return_type_end_token' => 7, 'nullable_return_type' => false, 'is_abstract' => false, 'is_final' => false, @@ -652,7 +652,7 @@ public function testNamespaceOperatorTypeHint() 'scope_specified' => false, 'return_type' => '?namespace\Name', 'return_type_token' => 9, - 'return_type_end_token' => 11, + 'return_type_end_token' => 9, 'nullable_return_type' => true, 'is_abstract' => false, 'is_final' => false, @@ -704,7 +704,7 @@ public function testPHP8UnionTypesTwoClasses() 'scope_specified' => false, 'return_type' => 'MyClassA|\Package\MyClassB', 'return_type_token' => 6, - 'return_type_end_token' => 11, + 'return_type_end_token' => 8, 'nullable_return_type' => false, 'is_abstract' => false, 'is_final' => false, @@ -1044,7 +1044,7 @@ public function testPHP81MoreIntersectionTypes() 'scope_specified' => false, 'return_type' => 'MyClassA&\Package\MyClassB&\Package\MyClassC', 'return_type_token' => 7, - 'return_type_end_token' => 17, + 'return_type_end_token' => 11, 'nullable_return_type' => false, 'is_abstract' => false, 'is_final' => false, @@ -1070,7 +1070,7 @@ public function testPHP81IntersectionArrowFunction() 'scope_specified' => false, 'return_type' => 'MyClassA&\Package\MyClassB', 'return_type_token' => 6, - 'return_type_end_token' => 11, + 'return_type_end_token' => 8, 'nullable_return_type' => false, 'is_abstract' => false, 'is_final' => false, @@ -1252,7 +1252,7 @@ public function testPHP82DNFTypeIllegalNullable() 'scope_specified' => false, 'return_type' => '?(A&\Pck\B)|bool', 'return_type_token' => 8, - 'return_type_end_token' => 17, + 'return_type_end_token' => 14, 'nullable_return_type' => true, 'is_abstract' => false, 'is_final' => false, @@ -1278,7 +1278,7 @@ public function testPHP82DNFTypeClosure() 'scope_specified' => false, 'return_type' => 'object|(namespace\Foo&Countable)', 'return_type_token' => 6, - 'return_type_end_token' => 14, + 'return_type_end_token' => 12, 'nullable_return_type' => false, 'is_abstract' => false, 'is_final' => false, @@ -1304,7 +1304,7 @@ public function testPHP82DNFTypeFn() 'scope_specified' => false, 'return_type' => 'null|(Partially\Qualified&Traversable)|void', 'return_type_token' => 6, - 'return_type_end_token' => 16, + 'return_type_end_token' => 14, 'nullable_return_type' => false, 'is_abstract' => false, 'is_final' => false, diff --git a/tests/Core/File/GetTokensAsStringTest.php b/tests/Core/File/GetTokensAsStringTest.php index c14db1a7ef..d993cba1c5 100644 --- a/tests/Core/File/GetTokensAsStringTest.php +++ b/tests/Core/File/GetTokensAsStringTest.php @@ -243,7 +243,7 @@ public static function dataGetTokensAsString() 'namespace' => [ 'testMarker' => '/* testNamespace */', 'startTokenType' => T_NAMESPACE, - 'length' => 8, + 'length' => 4, 'expected' => 'namespace Foo\Bar\Baz;', ], 'use-with-comments' => [ diff --git a/tests/Core/Ruleset/PopulateTokenListenersTest.php b/tests/Core/Ruleset/PopulateTokenListenersTest.php index 68a30e3a4b..2dd88f905e 100644 --- a/tests/Core/Ruleset/PopulateTokenListenersTest.php +++ b/tests/Core/Ruleset/PopulateTokenListenersTest.php @@ -131,7 +131,7 @@ public static function dataSniffListensToTokenss() return [ 'Generic.NamingConventions.UpperCaseConstantName' => [ 'sniffClass' => 'PHP_CodeSniffer\\Standards\\Generic\\Sniffs\\NamingConventions\\UpperCaseConstantNameSniff', - 'expectedCount' => 2, + 'expectedCount' => 3, ], 'PSR1.Files.SideEffects' => [ 'sniffClass' => 'PHP_CodeSniffer\\Standards\\PSR1\\Sniffs\\Files\\SideEffectsSniff', diff --git a/tests/Core/Tokenizers/PHP/AttributesTest.php b/tests/Core/Tokenizers/PHP/AttributesTest.php index 5e2ea0aeda..d95959264b 100644 --- a/tests/Core/Tokenizers/PHP/AttributesTest.php +++ b/tests/Core/Tokenizers/PHP/AttributesTest.php @@ -256,15 +256,10 @@ public static function dataAttribute() 'function attribute; using partially qualified and fully qualified class names' => [ 'testMarker' => '/* testFqcnAttribute */', 'tokenCodes' => [ - T_STRING, - T_NS_SEPARATOR, - T_STRING, + T_NAME_QUALIFIED, T_COMMA, T_WHITESPACE, - T_NS_SEPARATOR, - T_STRING, - T_NS_SEPARATOR, - T_STRING, + T_NAME_FULLY_QUALIFIED, T_OPEN_PARENTHESIS, T_CONSTANT_ENCAPSED_STRING, T_CLOSE_PARENTHESIS, @@ -616,9 +611,7 @@ public function testNestedAttributes() { $tokens = $this->phpcsFile->getTokens(); $tokenCodes = [ - T_STRING, - T_NS_SEPARATOR, - T_STRING, + T_NAME_QUALIFIED, T_OPEN_PARENTHESIS, T_FN, T_WHITESPACE, @@ -642,7 +635,8 @@ public function testNestedAttributes() ]; // Calculate the number of tokens between opener and closer (excluding the opener, including the closer). - $outerAttributeLength = (count($tokenCodes) + 1); + $outerAttributeLength = (count($tokenCodes) + 1); + $nestedAttributeOffset = 6; $attribute = $this->getTargetToken('/* testNestedAttributes */', T_ATTRIBUTE); $this->assertArrayHasKey('attribute_closer', $tokens[$attribute]); @@ -656,8 +650,8 @@ public function testNestedAttributes() $this->assertSame($tokens[$attribute]['attribute_closer'], $tokens[$closer]['attribute_closer']); $this->assertArrayNotHasKey('nested_attributes', $tokens[$attribute]); - $this->assertArrayHasKey('nested_attributes', $tokens[($attribute + 8)]); - $this->assertSame([$attribute => ($attribute + $outerAttributeLength)], $tokens[($attribute + 8)]['nested_attributes']); + $this->assertArrayHasKey('nested_attributes', $tokens[($attribute + $nestedAttributeOffset)]); + $this->assertSame([$attribute => ($attribute + $outerAttributeLength)], $tokens[($attribute + $nestedAttributeOffset)]['nested_attributes']); $test = function (array $tokens, $outerAttributeLength, $nestedMap) use ($attribute) { foreach ($tokens as $token) { @@ -667,23 +661,23 @@ public function testNestedAttributes() } }; - // Length here is 8 (nested attribute offset) + 5 (real length). - $innerAttributeLength = (8 + 5); + // Length here is 6 (nested attribute offset) + 5 (real length). + $innerAttributeLength = ($nestedAttributeOffset + 5); - $test(array_slice($tokens, ($attribute + 1), 7), $outerAttributeLength, [$attribute => $attribute + $outerAttributeLength]); - $test(array_slice($tokens, ($attribute + 8), 1), $innerAttributeLength, [$attribute => $attribute + $outerAttributeLength]); + $test(array_slice($tokens, ($attribute + 1), 5), $outerAttributeLength, [$attribute => $attribute + $outerAttributeLength]); + $test(array_slice($tokens, ($attribute + $nestedAttributeOffset), 1), $innerAttributeLength, [$attribute => $attribute + $outerAttributeLength]); $test( - array_slice($tokens, ($attribute + 9), 4), + array_slice($tokens, ($attribute + 7), 4), $innerAttributeLength, [ - $attribute => $attribute + $outerAttributeLength, - $attribute + 8 => $attribute + 13, + $attribute => $attribute + $outerAttributeLength, + $attribute + $nestedAttributeOffset => $attribute + $innerAttributeLength, ] ); - $test(array_slice($tokens, ($attribute + 13), 1), $innerAttributeLength, [$attribute => $attribute + $outerAttributeLength]); - $test(array_slice($tokens, ($attribute + 14), 10), $outerAttributeLength, [$attribute => $attribute + $outerAttributeLength]); + $test(array_slice($tokens, ($attribute + 11), 1), $innerAttributeLength, [$attribute => $attribute + $outerAttributeLength]); + $test(array_slice($tokens, ($attribute + 12), 10), $outerAttributeLength, [$attribute => $attribute + $outerAttributeLength]); $map = array_map( static function ($token) { diff --git a/tests/Core/Tokenizers/PHP/BackfillEnumTest.php b/tests/Core/Tokenizers/PHP/BackfillEnumTest.php index 9b313ca694..47a4ed3a64 100644 --- a/tests/Core/Tokenizers/PHP/BackfillEnumTest.php +++ b/tests/Core/Tokenizers/PHP/BackfillEnumTest.php @@ -10,6 +10,7 @@ namespace PHP_CodeSniffer\Tests\Core\Tokenizers\PHP; use PHP_CodeSniffer\Tests\Core\Tokenizers\AbstractTokenizerTestCase; +use PHP_CodeSniffer\Util\Tokens; final class BackfillEnumTest extends AbstractTokenizerTestCase { @@ -126,24 +127,29 @@ public static function dataEnums() /** - * Test that "enum" when not used as the keyword is still tokenized as `T_STRING`. + * Test that "enum" when not used as the keyword is still tokenized as identifier names. * - * @param string $testMarker The comment which prefaces the target token in the test file. - * @param string $testContent The token content to look for. + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param string $testContent The token content to look for. + * @param string $expectedType Optional. The token type which is expected (not T_FN). + * Defaults to `T_STRING`. * * @dataProvider dataNotEnums * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize * * @return void */ - public function testNotEnums($testMarker, $testContent) + public function testNotEnums($testMarker, $testContent, $expectedType='T_STRING') { + $targetTypes = Tokens::$nameTokens; + $targetTypes += [T_ENUM => T_ENUM]; + $target = $this->getTargetToken($testMarker, $targetTypes, $testContent); + $tokens = $this->phpcsFile->getTokens(); - $target = $this->getTargetToken($testMarker, [T_ENUM, T_STRING], $testContent); $tokenArray = $tokens[$target]; - $this->assertSame(T_STRING, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (code)'); - $this->assertSame('T_STRING', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (type)'); + $this->assertSame(constant($expectedType), $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not '.$expectedType.' (code)'); + $this->assertSame($expectedType, $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not '.$expectedType.' (type)'); }//end testNotEnums() @@ -187,8 +193,9 @@ public static function dataNotEnums() 'testContent' => 'Enum', ], 'not enum - part of namespace named enum' => [ - 'testMarker' => '/* testEnumUsedAsPartOfNamespaceName */', - 'testContent' => 'Enum', + 'testMarker' => '/* testEnumUsedAsPartOfNamespaceName */', + 'testContent' => 'My\Enum\Collection', + 'expectedType' => 'T_NAME_QUALIFIED', ], 'not enum - class instantiation for class enum' => [ 'testMarker' => '/* testEnumUsedInObjectInitialization */', @@ -199,8 +206,9 @@ public static function dataNotEnums() 'testContent' => 'enum', ], 'not enum - namespace relative function call' => [ - 'testMarker' => '/* testEnumAsFunctionCallWithNamespace */', - 'testContent' => 'enum', + 'testMarker' => '/* testEnumAsFunctionCallWithNamespace */', + 'testContent' => 'namespace\enum', + 'expectedType' => 'T_NAME_RELATIVE', ], 'not enum - class constant fetch with enum as class name' => [ 'testMarker' => '/* testClassConstantFetchWithEnumAsClassName */', diff --git a/tests/Core/Tokenizers/PHP/BackfillFnTokenTest.inc b/tests/Core/Tokenizers/PHP/BackfillFnTokenTest.inc index 8930d4fbfc..e6d17388be 100644 --- a/tests/Core/Tokenizers/PHP/BackfillFnTokenTest.inc +++ b/tests/Core/Tokenizers/PHP/BackfillFnTokenTest.inc @@ -71,6 +71,12 @@ $a = [ /* testYield */ $a = fn($x) => yield 'k' => $x; +/* testReturnTypeNullableFullyQualifiedClassName */ +$fn = fn($x) : ?\My\NS\ClassName => $x; + +/* testReturnTypeNullablePartiallyQualifiedClassName */ +$fn = fn($x) : ?NS\ClassName => $x; + /* testNullableUnqualifiedClassName */ $a = fn(?\DateTime $x) : ?\DateTime => $x; diff --git a/tests/Core/Tokenizers/PHP/BackfillFnTokenTest.php b/tests/Core/Tokenizers/PHP/BackfillFnTokenTest.php index d5cf019b87..2d84ee9ca1 100644 --- a/tests/Core/Tokenizers/PHP/BackfillFnTokenTest.php +++ b/tests/Core/Tokenizers/PHP/BackfillFnTokenTest.php @@ -10,6 +10,7 @@ namespace PHP_CodeSniffer\Tests\Core\Tokenizers\PHP; use PHP_CodeSniffer\Tests\Core\Tokenizers\AbstractTokenizerTestCase; +use PHP_CodeSniffer\Util\Tokens; final class BackfillFnTokenTest extends AbstractTokenizerTestCase { @@ -361,6 +362,38 @@ public function testYield() }//end testYield() + /** + * Test arrow functions that use nullable type with FQN class name. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testReturnTypeNullableFullyQualifiedClassName() + { + $token = $this->getTargetToken('/* testReturnTypeNullableFullyQualifiedClassName */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 10, 13); + + }//end testReturnTypeNullableFullyQualifiedClassName() + + + /** + * Test arrow functions that use nullable type with partially qualified class name. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testReturnTypeNullablePartiallyQualifiedClassName() + { + $token = $this->getTargetToken('/* testReturnTypeNullablePartiallyQualifiedClassName */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 10, 13); + + }//end testReturnTypeNullablePartiallyQualifiedClassName() + + /** * Test arrow functions that use nullable type with unqualified class name. * @@ -372,7 +405,7 @@ public function testNullableUnqualifiedClassName() { $token = $this->getTargetToken('/* testNullableUnqualifiedClassName */', T_FN); $this->backfillHelper($token); - $this->scopePositionTestHelper($token, 15, 18); + $this->scopePositionTestHelper($token, 13, 16); }//end testNullableUnqualifiedClassName() @@ -388,7 +421,7 @@ public function testNamespaceRelativeClassNameInTypes() { $token = $this->getTargetToken('/* testNamespaceRelativeClassNameInTypes */', T_FN); $this->backfillHelper($token); - $this->scopePositionTestHelper($token, 16, 19); + $this->scopePositionTestHelper($token, 12, 15); }//end testNamespaceRelativeClassNameInTypes() @@ -542,7 +575,7 @@ public function testIntersectionReturnType() { $token = $this->getTargetToken('/* testIntersectionReturnType */', T_FN); $this->backfillHelper($token); - $this->scopePositionTestHelper($token, 12, 20); + $this->scopePositionTestHelper($token, 11, 19); }//end testIntersectionReturnType() @@ -574,7 +607,7 @@ public function testDNFReturnType() { $token = $this->getTargetToken('/* testDNFReturnType */', T_FN); $this->backfillHelper($token); - $this->scopePositionTestHelper($token, 16, 29); + $this->scopePositionTestHelper($token, 15, 27); }//end testDNFReturnType() @@ -783,25 +816,30 @@ public function testNestedInMethod() /** - * Verify that "fn" keywords which are not arrow functions get tokenized as T_STRING and don't + * Verify that "fn" keywords which are not arrow functions get tokenized as identifier names and don't * have the extra token array indexes. * - * @param string $testMarker The comment prefacing the target token. - * @param string $testContent The token content to look for. + * @param string $testMarker The comment prefacing the target token. + * @param string $testContent The token content to look for. + * @param string $expectedType Optional. The token type which is expected (not T_FN). + * Defaults to `T_STRING`. * * @dataProvider dataNotAnArrowFunction * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional * * @return void */ - public function testNotAnArrowFunction($testMarker, $testContent='fn') + public function testNotAnArrowFunction($testMarker, $testContent='fn', $expectedType='T_STRING') { - $tokens = $this->phpcsFile->getTokens(); + $targetTypes = Tokens::$nameTokens; + $targetTypes += [T_FN => T_FN]; + $target = $this->getTargetToken($testMarker, $targetTypes, $testContent); - $token = $this->getTargetToken($testMarker, [T_STRING, T_FN], $testContent); - $tokenArray = $tokens[$token]; + $tokens = $this->phpcsFile->getTokens(); + $tokenArray = $tokens[$target]; - $this->assertSame('T_STRING', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING'); + $this->assertSame(constant($expectedType), $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not '.$expectedType.' (code)'); + $this->assertSame($expectedType, $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not '.$expectedType.' (type)'); $this->assertArrayNotHasKey('scope_condition', $tokenArray, 'Scope condition is set'); $this->assertArrayNotHasKey('scope_opener', $tokenArray, 'Scope opener is set'); @@ -863,11 +901,14 @@ public static function dataNotAnArrowFunction() 'testContent' => 'FN', ], 'name of a (namespaced) function, context: partially qualified function call' => [ - 'testMarker' => '/* testNonArrowNamespacedFunctionCall */', - 'testContent' => 'Fn', + 'testMarker' => '/* testNonArrowNamespacedFunctionCall */', + 'testContent' => 'MyNS\Sub\Fn', + 'expectedType' => 'T_NAME_QUALIFIED', ], 'name of a (namespaced) function, context: namespace relative function call' => [ - 'testMarker' => '/* testNonArrowNamespaceOperatorFunctionCall */', + 'testMarker' => '/* testNonArrowNamespaceOperatorFunctionCall */', + 'testContent' => 'namespace\fn', + 'expectedType' => 'T_NAME_RELATIVE', ], 'name of a function, context: declaration with union types for param and return' => [ 'testMarker' => '/* testNonArrowFunctionNameWithUnionTypes */', diff --git a/tests/Core/Tokenizers/PHP/BackfillMatchTokenTest.php b/tests/Core/Tokenizers/PHP/BackfillMatchTokenTest.php index 8214ea1a91..07a335c058 100644 --- a/tests/Core/Tokenizers/PHP/BackfillMatchTokenTest.php +++ b/tests/Core/Tokenizers/PHP/BackfillMatchTokenTest.php @@ -199,11 +199,13 @@ public static function dataMatchExpression() /** - * Verify that "match" keywords which are not match control structures get tokenized as T_STRING + * Verify that "match" keywords which are not match control structures get tokenized as identifier names * and don't have the extra token array indexes. * - * @param string $testMarker The comment prefacing the target token. - * @param string $testContent The token content to look for. + * @param string $testMarker The comment prefacing the target token. + * @param string $testContent The token content to look for. + * @param string $expectedType Optional. The token type which is expected (not T_FN). + * Defaults to `T_STRING`. * * @dataProvider dataNotAMatchStructure * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize @@ -211,15 +213,17 @@ public static function dataMatchExpression() * * @return void */ - public function testNotAMatchStructure($testMarker, $testContent='match') + public function testNotAMatchStructure($testMarker, $testContent='match', $expectedType='T_STRING') { - $tokens = $this->phpcsFile->getTokens(); + $targetTypes = Tokens::$nameTokens; + $targetTypes += [T_MATCH => T_MATCH]; + $target = $this->getTargetToken($testMarker, $targetTypes, $testContent); - $token = $this->getTargetToken($testMarker, [T_STRING, T_MATCH], $testContent); - $tokenArray = $tokens[$token]; + $tokens = $this->phpcsFile->getTokens(); + $tokenArray = $tokens[$target]; - $this->assertSame(T_STRING, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (code)'); - $this->assertSame('T_STRING', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (type)'); + $this->assertSame(constant($expectedType), $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not '.$expectedType.' (code)'); + $this->assertSame($expectedType, $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not '.$expectedType.' (type)'); $this->assertArrayNotHasKey('scope_condition', $tokenArray, 'Scope condition is set'); $this->assertArrayNotHasKey('scope_opener', $tokenArray, 'Scope opener is set'); @@ -228,7 +232,7 @@ public function testNotAMatchStructure($testMarker, $testContent='match') $this->assertArrayNotHasKey('parenthesis_opener', $tokenArray, 'Parenthesis opener is set'); $this->assertArrayNotHasKey('parenthesis_closer', $tokenArray, 'Parenthesis closer is set'); - $next = $this->phpcsFile->findNext(Tokens::$emptyTokens, ($token + 1), null, true); + $next = $this->phpcsFile->findNext(Tokens::$emptyTokens, ($target + 1), null, true); if ($next !== false && $tokens[$next]['code'] === T_OPEN_PARENTHESIS) { $this->assertArrayNotHasKey('parenthesis_owner', $tokenArray, 'Parenthesis owner is set for opener after'); } @@ -268,10 +272,14 @@ public static function dataNotAMatchStructure() 'testMarker' => '/* testNoMatchPropertyAccess */', ], 'namespaced_function_call' => [ - 'testMarker' => '/* testNoMatchNamespacedFunctionCall */', + 'testMarker' => '/* testNoMatchNamespacedFunctionCall */', + 'testContent' => 'MyNS\Sub\match', + 'expectedType' => 'T_NAME_QUALIFIED', ], 'namespace_operator_function_call' => [ - 'testMarker' => '/* testNoMatchNamespaceOperatorFunctionCall */', + 'testMarker' => '/* testNoMatchNamespaceOperatorFunctionCall */', + 'testContent' => 'namespace\match', + 'expectedType' => 'T_NAME_RELATIVE', ], 'interface_method_declaration' => [ 'testMarker' => '/* testNoMatchInterfaceMethodDeclaration */', @@ -325,8 +333,9 @@ public static function dataNotAMatchStructure() 'testContent' => 'Match', ], 'use_statement' => [ - 'testMarker' => '/* testNoMatchInUseStatement */', - 'testContent' => 'Match', + 'testMarker' => '/* testNoMatchInUseStatement */', + 'testContent' => 'Match\me', + 'expectedType' => 'T_NAME_QUALIFIED', ], 'unsupported_inline_control_structure' => [ 'testMarker' => '/* testNoMatchMissingCurlies */', diff --git a/tests/Core/Tokenizers/PHP/BackfillReadonlyTest.php b/tests/Core/Tokenizers/PHP/BackfillReadonlyTest.php index 89dc3e19d1..f1f616cb68 100644 --- a/tests/Core/Tokenizers/PHP/BackfillReadonlyTest.php +++ b/tests/Core/Tokenizers/PHP/BackfillReadonlyTest.php @@ -10,6 +10,7 @@ namespace PHP_CodeSniffer\Tests\Core\Tokenizers\PHP; use PHP_CodeSniffer\Tests\Core\Tokenizers\AbstractTokenizerTestCase; +use PHP_CodeSniffer\Util\Tokens; final class BackfillReadonlyTest extends AbstractTokenizerTestCase { @@ -173,25 +174,30 @@ public static function dataReadonly() /** - * Test that "readonly" when not used as the keyword is still tokenized as `T_STRING`. + * Test that "readonly" when not used as the keyword is still tokenized as identifier names. * - * @param string $testMarker The comment which prefaces the target token in the test file. - * @param string $testContent Optional. The token content to look for. - * Defaults to lowercase "readonly". + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param string $testContent Optional. The token content to look for. + * Defaults to lowercase "readonly". + * @param string $expectedType Optional. The token type which is expected (not T_FN). + * Defaults to `T_STRING`. * * @dataProvider dataNotReadonly * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional * * @return void */ - public function testNotReadonly($testMarker, $testContent='readonly') + public function testNotReadonly($testMarker, $testContent='readonly', $expectedType='T_STRING') { + $targetTypes = Tokens::$nameTokens; + $targetTypes += [T_READONLY => T_READONLY]; + $target = $this->getTargetToken($testMarker, $targetTypes, $testContent); + $tokens = $this->phpcsFile->getTokens(); - $target = $this->getTargetToken($testMarker, [T_READONLY, T_STRING], $testContent); $tokenArray = $tokens[$target]; - $this->assertSame(T_STRING, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (code)'); - $this->assertSame('T_STRING', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (type)'); + $this->assertSame(constant($expectedType), $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not '.$expectedType.' (code)'); + $this->assertSame($expectedType, $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not '.$expectedType.' (type)'); }//end testNotReadonly() @@ -230,18 +236,22 @@ public static function dataNotReadonly() 'testContent' => 'Readonly', ], 'partial name of namespace, context: declaration, mixed case' => [ - 'testMarker' => '/* testReadonlyUsedAsPartOfNamespaceName */', - 'testContent' => 'Readonly', + 'testMarker' => '/* testReadonlyUsedAsPartOfNamespaceName */', + 'testContent' => 'My\Readonly\Collection', + 'expectedType' => 'T_NAME_QUALIFIED', ], 'name of a function, context: call' => [ 'testMarker' => '/* testReadonlyAsFunctionCall */', ], 'name of a namespaced function, context: partially qualified call' => [ - 'testMarker' => '/* testReadonlyAsNamespacedFunctionCall */', + 'testMarker' => '/* testReadonlyAsNamespacedFunctionCall */', + 'testContent' => 'My\NS\readonly', + 'expectedType' => 'T_NAME_QUALIFIED', ], 'name of a function, context: namespace relative call, mixed case' => [ - 'testMarker' => '/* testReadonlyAsNamespaceRelativeFunctionCall */', - 'testContent' => 'ReadOnly', + 'testMarker' => '/* testReadonlyAsNamespaceRelativeFunctionCall */', + 'testContent' => 'namespace\ReadOnly', + 'expectedType' => 'T_NAME_RELATIVE', ], 'name of a method, context: method call on object' => [ 'testMarker' => '/* testReadonlyAsMethodCall */', diff --git a/tests/Core/Tokenizers/PHP/ContextSensitiveKeywordsTest.inc b/tests/Core/Tokenizers/PHP/ContextSensitiveKeywordsTest.inc index 2825f26e52..fabb9b8713 100644 --- a/tests/Core/Tokenizers/PHP/ContextSensitiveKeywordsTest.inc +++ b/tests/Core/Tokenizers/PHP/ContextSensitiveKeywordsTest.inc @@ -222,9 +222,6 @@ $anonymousClass3 = new class /* testImplementsInAnonymousClassIsKeyword */ imple $instantiated = new /* testClassInstantiationStaticIsKeyword */ static($param); -class Foo extends /* testNamespaceInNameIsKeyword */ namespace\Exception -{} - function /* testKeywordAfterFunctionShouldBeString */ eval() {} function /* testKeywordAfterFunctionByRefShouldBeString */ &switch() {} diff --git a/tests/Core/Tokenizers/PHP/ContextSensitiveKeywordsTest.php b/tests/Core/Tokenizers/PHP/ContextSensitiveKeywordsTest.php index a8d746ae9f..35b47385aa 100644 --- a/tests/Core/Tokenizers/PHP/ContextSensitiveKeywordsTest.php +++ b/tests/Core/Tokenizers/PHP/ContextSensitiveKeywordsTest.php @@ -515,10 +515,6 @@ public static function dataKeywords() 'testMarker' => '/* testClassInstantiationStaticIsKeyword */', 'expectedTokenType' => 'T_STATIC', ], - 'namespace: operator' => [ - 'testMarker' => '/* testNamespaceInNameIsKeyword */', - 'expectedTokenType' => 'T_NAMESPACE', - ], 'static: closure declaration' => [ 'testMarker' => '/* testStaticIsKeywordBeforeClosure */', diff --git a/tests/Core/Tokenizers/PHP/DefaultKeywordTest.php b/tests/Core/Tokenizers/PHP/DefaultKeywordTest.php index c398aa6cb2..0b7ffdf31e 100644 --- a/tests/Core/Tokenizers/PHP/DefaultKeywordTest.php +++ b/tests/Core/Tokenizers/PHP/DefaultKeywordTest.php @@ -11,6 +11,7 @@ namespace PHP_CodeSniffer\Tests\Core\Tokenizers\PHP; use PHP_CodeSniffer\Tests\Core\Tokenizers\AbstractTokenizerTestCase; +use PHP_CodeSniffer\Util\Tokens; final class DefaultKeywordTest extends AbstractTokenizerTestCase { @@ -155,26 +156,33 @@ public static function dataSwitchDefault() /** * Verify that the retokenization of `T_DEFAULT` tokens in match constructs, doesn't negatively - * impact the tokenization of `T_STRING` tokens with the contents 'default' which aren't in + * impact the tokenization of identifier name tokens with the contents 'default' which aren't in * actual fact the default keyword. * - * @param string $testMarker The comment prefacing the target token. - * @param string $testContent The token content to look for. + * @param string $testMarker The comment prefacing the target token. + * @param string $testContent The token content to look for. + * @param string $expectedType Optional. The token type which is expected (not T_FN). + * Defaults to `T_STRING`. * * @dataProvider dataNotDefaultKeyword * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional * * @return void */ - public function testNotDefaultKeyword($testMarker, $testContent='DEFAULT') + public function testNotDefaultKeyword($testMarker, $testContent='DEFAULT', $expectedType='T_STRING') { - $tokens = $this->phpcsFile->getTokens(); + $targetTypes = Tokens::$nameTokens; + $targetTypes += [ + T_MATCH_DEFAULT => T_MATCH_DEFAULT, + T_DEFAULT => T_DEFAULT, + ]; + $target = $this->getTargetToken($testMarker, $targetTypes, $testContent); - $token = $this->getTargetToken($testMarker, [T_MATCH_DEFAULT, T_DEFAULT, T_STRING], $testContent); - $tokenArray = $tokens[$token]; + $tokens = $this->phpcsFile->getTokens(); + $tokenArray = $tokens[$target]; - $this->assertSame(T_STRING, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (code)'); - $this->assertSame('T_STRING', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (type)'); + $this->assertSame(constant($expectedType), $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not '.$expectedType.' (code)'); + $this->assertSame($expectedType, $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not '.$expectedType.' (type)'); }//end testNotDefaultKeyword() @@ -196,10 +204,14 @@ public static function dataNotDefaultKeyword() 'testMarker' => '/* testClassPropertyAsShortArrayKey */', ], 'namespaced-constant-as-short-array-key' => [ - 'testMarker' => '/* testNamespacedConstantAsShortArrayKey */', + 'testMarker' => '/* testNamespacedConstantAsShortArrayKey */', + 'testContent' => 'SomeNamespace\DEFAULT', + 'expectedType' => 'T_NAME_QUALIFIED', ], 'fqn-global-constant-as-short-array-key' => [ - 'testMarker' => '/* testFQNGlobalConstantAsShortArrayKey */', + 'testMarker' => '/* testFQNGlobalConstantAsShortArrayKey */', + 'testContent' => '\DEFAULT', + 'expectedType' => 'T_NAME_FULLY_QUALIFIED', ], 'class-constant-as-long-array-key' => [ 'testMarker' => '/* testClassConstantAsLongArrayKey */', @@ -234,10 +246,14 @@ public static function dataNotDefaultKeyword() 'testMarker' => '/* testClassPropertyInSwitchCase */', ], 'namespaced-constant-in-switch-case' => [ - 'testMarker' => '/* testNamespacedConstantInSwitchCase */', + 'testMarker' => '/* testNamespacedConstantInSwitchCase */', + 'testContent' => 'SomeNamespace\DEFAULT', + 'expectedType' => 'T_NAME_QUALIFIED', ], 'namespace-relative-constant-in-switch-case' => [ - 'testMarker' => '/* testNamespaceRelativeConstantInSwitchCase */', + 'testMarker' => '/* testNamespaceRelativeConstantInSwitchCase */', + 'testContent' => 'namespace\DEFAULT', + 'expectedType' => 'T_NAME_RELATIVE', ], 'class-constant-declaration' => [ diff --git a/tests/Core/Tokenizers/PHP/GotoLabelTest.php b/tests/Core/Tokenizers/PHP/GotoLabelTest.php index 643338b145..059b634408 100644 --- a/tests/Core/Tokenizers/PHP/GotoLabelTest.php +++ b/tests/Core/Tokenizers/PHP/GotoLabelTest.php @@ -191,10 +191,8 @@ public static function dataGotoDeclaration() */ public function testNotAGotoDeclaration($testMarker, $testContent, $expectedType='T_STRING') { - $targetTypes = [ - T_STRING => T_STRING, - T_GOTO_LABEL => T_GOTO_LABEL, - ]; + $targetTypes = Tokens::$nameTokens; + $targetTypes += [T_GOTO_LABEL => T_GOTO_LABEL]; $expectedCode = T_STRING; if ($expectedType !== 'T_STRING') { @@ -228,8 +226,9 @@ public static function dataNotAGotoDeclaration() 'testContent' => 'CONSTANT', ], 'not goto label - namespaced constant followed by switch-case colon' => [ - 'testMarker' => '/* testNotGotoDeclarationNamespacedConstant */', - 'testContent' => 'CONSTANT', + 'testMarker' => '/* testNotGotoDeclarationNamespacedConstant */', + 'testContent' => 'MyNS\CONSTANT', + 'expectedType' => 'T_NAME_QUALIFIED', ], 'not goto label - class constant followed by switch-case colon' => [ 'testMarker' => '/* testNotGotoDeclarationClassConstantInCase */', diff --git a/tests/Core/Tokenizers/PHP/NamedFunctionCallArgumentsTest.inc b/tests/Core/Tokenizers/PHP/NamedFunctionCallArgumentsTest.inc index 2f1d20bf08..b6434d3d93 100644 --- a/tests/Core/Tokenizers/PHP/NamedFunctionCallArgumentsTest.inc +++ b/tests/Core/Tokenizers/PHP/NamedFunctionCallArgumentsTest.inc @@ -1,7 +1,7 @@ count, self::VALUE); /* testNamedArgs */ array_fill(start_index: 0, count: 100, value: 50); diff --git a/tests/Core/Tokenizers/PHP/NamedFunctionCallArgumentsTest.php b/tests/Core/Tokenizers/PHP/NamedFunctionCallArgumentsTest.php index 768e41a98b..8c6be5bd4c 100644 --- a/tests/Core/Tokenizers/PHP/NamedFunctionCallArgumentsTest.php +++ b/tests/Core/Tokenizers/PHP/NamedFunctionCallArgumentsTest.php @@ -329,7 +329,7 @@ public static function dataOtherTstringInFunctionCall() ], 'not arg name - fully qualified constant' => [ 'testMarker' => '/* testPositionalArgs */', - 'content' => 'COUNT', + 'content' => 'count', ], 'not arg name - namespace relative constant' => [ 'testMarker' => '/* testPositionalArgs */', diff --git a/tests/Core/Tokenizers/PHP/UndoNamespacedNameSingleTokenTest.inc b/tests/Core/Tokenizers/PHP/NamespacedNameSingleTokenTest.inc similarity index 90% rename from tests/Core/Tokenizers/PHP/UndoNamespacedNameSingleTokenTest.inc rename to tests/Core/Tokenizers/PHP/NamespacedNameSingleTokenTest.inc index 65c551a84f..886b6ed04e 100644 --- a/tests/Core/Tokenizers/PHP/UndoNamespacedNameSingleTokenTest.inc +++ b/tests/Core/Tokenizers/PHP/NamespacedNameSingleTokenTest.inc @@ -6,6 +6,9 @@ namespace Package; /* testNamespaceDeclarationWithLevels */ namespace Vendor\SubLevel\Domain; +/* testNamespaceDeclarationWithReservedKeywords */ +namespace For\Include\Fn; + /* testUseStatement */ use ClassName; @@ -49,8 +52,8 @@ class MyClass \Fully\Qualified, /* testImplementsUnqualified */ Unqualified, - /* testImplementsPartiallyQualified */ - Sub\Level\Name + /* testImplementsPartiallyQualifiedWithReservedKeyword */ + Exit\Level\Name { /* testFunctionName */ public function function_name( @@ -73,8 +76,8 @@ class MyClass /* testFunctionCallRelative */ echo NameSpace\function_name(); - /* testFunctionCallFQN */ - echo \Vendor\Package\function_name(); + /* testFunctionCallFQNWithReservedKeyword */ + echo \Vendor\Foreach\function_name(); /* testFunctionCallUnqualified */ echo function_name(); @@ -118,7 +121,7 @@ class MyClass /* testDoubleColonPartiallyQualified */ $value = Level\ClassName::CONSTANT_NAME['key']; - + /* testInstanceOfRelative */ $is = $obj instanceof namespace\ClassName; @@ -145,3 +148,7 @@ $value = \Fully \Name // comment :: function_name(); + +/* testInvalidDoubleBackslash */ +// Intentional parse error. +$obj = new \\SomeClass(); diff --git a/tests/Core/Tokenizers/PHP/UndoNamespacedNameSingleTokenTest.php b/tests/Core/Tokenizers/PHP/NamespacedNameSingleTokenTest.php similarity index 67% rename from tests/Core/Tokenizers/PHP/UndoNamespacedNameSingleTokenTest.php rename to tests/Core/Tokenizers/PHP/NamespacedNameSingleTokenTest.php index 18a88fe8e9..575da62f00 100644 --- a/tests/Core/Tokenizers/PHP/UndoNamespacedNameSingleTokenTest.php +++ b/tests/Core/Tokenizers/PHP/NamespacedNameSingleTokenTest.php @@ -5,9 +5,9 @@ * As of PHP 8, identifier names are tokenized differently, depending on them being * either fully qualified, partially qualified or relative to the current namespace. * - * This test file safeguards that in PHPCS 3.x this new form of tokenization is "undone" - * and the tokenization of these identifier names is the same in all PHP versions - * based on how these names were tokenized in PHP 5/7. + * This test file safeguards that in PHPCS 4.x this new form of tokenization is correctly + * backfilled and that the tokenization of these identifier names is the same in all + * PHP versions based on how these names are tokenized in PHP 8. * * {@link https://wiki.php.net/rfc/namespaced_names_as_token} * {@link https://github.com/squizlabs/PHP_CodeSniffer/issues/3041} @@ -20,14 +20,13 @@ namespace PHP_CodeSniffer\Tests\Core\Tokenizers\PHP; use PHP_CodeSniffer\Tests\Core\Tokenizers\AbstractTokenizerTestCase; -use PHP_CodeSniffer\Util\Tokens; -final class UndoNamespacedNameSingleTokenTest extends AbstractTokenizerTestCase +final class NamespacedNameSingleTokenTest extends AbstractTokenizerTestCase { /** - * Test that identifier names are tokenized the same across PHP versions, based on the PHP 5/7 tokenization. + * Test that identifier names are tokenized the same across PHP versions, based on the PHP 8 tokenization. * * @param string $testMarker The comment prefacing the test. * @param array> $expectedTokens The tokenization expected. @@ -46,7 +45,7 @@ public function testIdentifierTokenization($testMarker, $expectedTokens) $this->assertSame( constant($tokenInfo['type']), $tokens[$identifier]['code'], - 'Token tokenized as '.Tokens::tokenName($tokens[$identifier]['code']).', not '.$tokenInfo['type'].' (code)' + 'Token tokenized as '.$tokens[$identifier]['type'].', not '.$tokenInfo['type'].' (code)' ); $this->assertSame( $tokenInfo['type'], @@ -74,6 +73,14 @@ public static function dataIdentifierTokenization() 'namespace declaration' => [ 'testMarker' => '/* testNamespaceDeclaration */', 'expectedTokens' => [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], [ 'type' => 'T_STRING', 'content' => 'Package', @@ -88,24 +95,37 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testNamespaceDeclarationWithLevels */', 'expectedTokens' => [ [ - 'type' => 'T_STRING', - 'content' => 'Vendor', + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', ], [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', + 'type' => 'T_WHITESPACE', + 'content' => ' ', ], [ - 'type' => 'T_STRING', - 'content' => 'SubLevel', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'Vendor\SubLevel\Domain', ], [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', + 'type' => 'T_SEMICOLON', + 'content' => ';', ], + ], + ], + 'namespace declaration, uses reserved keywords in name' => [ + 'testMarker' => '/* testNamespaceDeclarationWithReservedKeywords */', + 'expectedTokens' => [ [ - 'type' => 'T_STRING', - 'content' => 'Domain', + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'For\Include\Fn', ], [ 'type' => 'T_SEMICOLON', @@ -130,24 +150,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testUseStatementWithLevels */', 'expectedTokens' => [ [ - 'type' => 'T_STRING', - 'content' => 'Vendor', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Level', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Domain', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'Vendor\Level\Domain', ], [ 'type' => 'T_SEMICOLON', @@ -188,24 +192,8 @@ public static function dataIdentifierTokenization() 'content' => ' ', ], [ - 'type' => 'T_STRING', - 'content' => 'Vendor', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Level', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'function_in_ns', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'Vendor\Level\function_in_ns', ], [ 'type' => 'T_SEMICOLON', @@ -246,24 +234,8 @@ public static function dataIdentifierTokenization() 'content' => ' ', ], [ - 'type' => 'T_STRING', - 'content' => 'Vendor', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Level', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'OTHER_CONSTANT', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'Vendor\Level\OTHER_CONSTANT', ], [ 'type' => 'T_SEMICOLON', @@ -288,16 +260,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testMultiUsePartiallyQualified */', 'expectedTokens' => [ [ - 'type' => 'T_STRING', - 'content' => 'Sublevel', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'PartiallyClassName', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'Sublevel\PartiallyClassName', ], [ 'type' => 'T_SEMICOLON', @@ -309,16 +273,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testGroupUseStatement */', 'expectedTokens' => [ [ - 'type' => 'T_STRING', - 'content' => 'Vendor', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Level', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'Vendor\Level', ], [ 'type' => 'T_NS_SEPARATOR', @@ -405,16 +361,8 @@ public static function dataIdentifierTokenization() 'content' => ' ', ], [ - 'type' => 'T_STRING', - 'content' => 'Sub', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'YetAnotherDomain', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'Sub\YetAnotherDomain', ], [ 'type' => 'T_COMMA', @@ -438,16 +386,8 @@ public static function dataIdentifierTokenization() 'content' => ' ', ], [ - 'type' => 'T_STRING', - 'content' => 'SubLevelA', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'function_grouped_too', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'SubLevelA\function_grouped_too', ], [ 'type' => 'T_COMMA', @@ -471,16 +411,8 @@ public static function dataIdentifierTokenization() 'content' => ' ', ], [ - 'type' => 'T_STRING', - 'content' => 'SubLevelB', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'CONSTANT_GROUPED_TOO', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'SubLevelB\CONSTANT_GROUPED_TOO', ], [ 'type' => 'T_COMMA', @@ -519,28 +451,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testExtendedFQN */', 'expectedTokens' => [ [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Vendor', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Level', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'FQN', + 'type' => 'T_NAME_FULLY_QUALIFIED', + 'content' => '\Vendor\Level\FQN', ], [ 'type' => 'T_WHITESPACE', @@ -553,16 +465,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testImplementsRelative */', 'expectedTokens' => [ [ - 'type' => 'T_NAMESPACE', - 'content' => 'namespace', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Name', + 'type' => 'T_NAME_RELATIVE', + 'content' => 'namespace\Name', ], [ 'type' => 'T_COMMA', @@ -574,20 +478,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testImplementsFQN */', 'expectedTokens' => [ [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Fully', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Qualified', + 'type' => 'T_NAME_FULLY_QUALIFIED', + 'content' => '\Fully\Qualified', ], [ 'type' => 'T_COMMA', @@ -608,28 +500,12 @@ public static function dataIdentifierTokenization() ], ], ], - 'class declaration, implements partially qualified name' => [ - 'testMarker' => '/* testImplementsPartiallyQualified */', + 'class declaration, implements partially qualified name (with reserved keyword)' => [ + 'testMarker' => '/* testImplementsPartiallyQualifiedWithReservedKeyword */', 'expectedTokens' => [ [ - 'type' => 'T_STRING', - 'content' => 'Sub', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Level', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Name', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'Exit\Level\Name', ], [ 'type' => 'T_WHITESPACE', @@ -655,16 +531,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testTypeDeclarationRelative */', 'expectedTokens' => [ [ - 'type' => 'T_NAMESPACE', - 'content' => 'namespace', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Name', + 'type' => 'T_NAME_RELATIVE', + 'content' => 'namespace\Name', ], [ 'type' => 'T_TYPE_UNION', @@ -680,28 +548,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testTypeDeclarationFQN */', 'expectedTokens' => [ [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Fully', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Qualified', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Name', + 'type' => 'T_NAME_FULLY_QUALIFIED', + 'content' => '\Fully\Qualified\Name', ], [ 'type' => 'T_WHITESPACE', @@ -734,16 +582,8 @@ public static function dataIdentifierTokenization() 'content' => '?', ], [ - 'type' => 'T_STRING', - 'content' => 'Sublevel', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Name', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'Sublevel\Name', ], [ 'type' => 'T_WHITESPACE', @@ -759,12 +599,8 @@ public static function dataIdentifierTokenization() 'content' => '?', ], [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Name', + 'type' => 'T_NAME_FULLY_QUALIFIED', + 'content' => '\Name', ], [ 'type' => 'T_WHITESPACE', @@ -776,16 +612,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testFunctionCallRelative */', 'expectedTokens' => [ [ - 'type' => 'T_NAMESPACE', - 'content' => 'NameSpace', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'function_name', + 'type' => 'T_NAME_RELATIVE', + 'content' => 'NameSpace\function_name', ], [ 'type' => 'T_OPEN_PARENTHESIS', @@ -793,32 +621,12 @@ public static function dataIdentifierTokenization() ], ], ], - 'function call, fully qualified name' => [ - 'testMarker' => '/* testFunctionCallFQN */', + 'function call, fully qualified name (with reserved keyword)' => [ + 'testMarker' => '/* testFunctionCallFQNWithReservedKeyword */', 'expectedTokens' => [ [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Vendor', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Package', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'function_name', + 'type' => 'T_NAME_FULLY_QUALIFIED', + 'content' => '\Vendor\Foreach\function_name', ], [ 'type' => 'T_OPEN_PARENTHESIS', @@ -843,16 +651,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testFunctionCallPartiallyQualified */', 'expectedTokens' => [ [ - 'type' => 'T_STRING', - 'content' => 'Level', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'function_name', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'Level\function_name', ], [ 'type' => 'T_OPEN_PARENTHESIS', @@ -864,24 +664,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testCatchRelative */', 'expectedTokens' => [ [ - 'type' => 'T_NAMESPACE', - 'content' => 'namespace', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'SubLevel', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Exception', + 'type' => 'T_NAME_RELATIVE', + 'content' => 'namespace\SubLevel\Exception', ], [ 'type' => 'T_WHITESPACE', @@ -893,12 +677,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testCatchFQN */', 'expectedTokens' => [ [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Exception', + 'type' => 'T_NAME_FULLY_QUALIFIED', + 'content' => '\Exception', ], [ 'type' => 'T_WHITESPACE', @@ -923,16 +703,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testCatchPartiallyQualified */', 'expectedTokens' => [ [ - 'type' => 'T_STRING', - 'content' => 'Level', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Exception', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'Level\Exception', ], [ 'type' => 'T_WHITESPACE', @@ -944,16 +716,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testNewRelative */', 'expectedTokens' => [ [ - 'type' => 'T_NAMESPACE', - 'content' => 'namespace', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'ClassName', + 'type' => 'T_NAME_RELATIVE', + 'content' => 'namespace\ClassName', ], [ 'type' => 'T_OPEN_PARENTHESIS', @@ -965,20 +729,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testNewFQN */', 'expectedTokens' => [ [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Vendor', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'ClassName', + 'type' => 'T_NAME_FULLY_QUALIFIED', + 'content' => '\Vendor\ClassName', ], [ 'type' => 'T_OPEN_PARENTHESIS', @@ -1003,16 +755,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testNewPartiallyQualified */', 'expectedTokens' => [ [ - 'type' => 'T_STRING', - 'content' => 'Level', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'ClassName', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'Level\ClassName', ], [ 'type' => 'T_SEMICOLON', @@ -1024,16 +768,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testDoubleColonRelative */', 'expectedTokens' => [ [ - 'type' => 'T_NAMESPACE', - 'content' => 'namespace', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'ClassName', + 'type' => 'T_NAME_RELATIVE', + 'content' => 'namespace\ClassName', ], [ 'type' => 'T_DOUBLE_COLON', @@ -1045,12 +781,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testDoubleColonFQN */', 'expectedTokens' => [ [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'ClassName', + 'type' => 'T_NAME_FULLY_QUALIFIED', + 'content' => '\ClassName', ], [ 'type' => 'T_DOUBLE_COLON', @@ -1075,16 +807,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testDoubleColonPartiallyQualified */', 'expectedTokens' => [ [ - 'type' => 'T_STRING', - 'content' => 'Level', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'ClassName', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'Level\ClassName', ], [ 'type' => 'T_DOUBLE_COLON', @@ -1096,16 +820,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testInstanceOfRelative */', 'expectedTokens' => [ [ - 'type' => 'T_NAMESPACE', - 'content' => 'namespace', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'ClassName', + 'type' => 'T_NAME_RELATIVE', + 'content' => 'namespace\ClassName', ], [ 'type' => 'T_SEMICOLON', @@ -1117,20 +833,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testInstanceOfFQN */', 'expectedTokens' => [ [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Full', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'ClassName', + 'type' => 'T_NAME_FULLY_QUALIFIED', + 'content' => '\Full\ClassName', ], [ 'type' => 'T_CLOSE_PARENTHESIS', @@ -1155,16 +859,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testInstanceOfPartiallyQualified */', 'expectedTokens' => [ [ - 'type' => 'T_STRING', - 'content' => 'Partially', - ], - [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'ClassName', + 'type' => 'T_NAME_QUALIFIED', + 'content' => 'Partially\ClassName', ], [ 'type' => 'T_SEMICOLON', @@ -1226,12 +922,8 @@ public static function dataIdentifierTokenization() 'testMarker' => '/* testInvalidInPHP8Comments */', 'expectedTokens' => [ [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Fully', + 'type' => 'T_NAME_FULLY_QUALIFIED', + 'content' => '\Fully', ], [ 'type' => 'T_WHITESPACE', @@ -1252,12 +944,8 @@ public static function dataIdentifierTokenization() 'content' => ' ', ], [ - 'type' => 'T_NS_SEPARATOR', - 'content' => '\\', - ], - [ - 'type' => 'T_STRING', - 'content' => 'Qualified', + 'type' => 'T_NAME_FULLY_QUALIFIED', + 'content' => '\Qualified', ], [ 'type' => 'T_WHITESPACE', @@ -1281,18 +969,31 @@ public static function dataIdentifierTokenization() 'type' => 'T_WHITESPACE', 'content' => ' ', ], + [ + 'type' => 'T_NAME_FULLY_QUALIFIED', + 'content' => '\Name', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + 'invalid name, double backslash' => [ + 'testMarker' => '/* testInvalidDoubleBackslash */', + 'expectedTokens' => [ [ 'type' => 'T_NS_SEPARATOR', 'content' => '\\', ], [ - 'type' => 'T_STRING', - 'content' => 'Name', + 'type' => 'T_NAME_FULLY_QUALIFIED', + 'content' => '\SomeClass', ], [ - 'type' => 'T_WHITESPACE', - 'content' => ' -', + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', ], ], ], diff --git a/tests/Core/Tokenizers/PHP/ShortArrayTest.inc b/tests/Core/Tokenizers/PHP/ShortArrayTest.inc index 60b23a51cc..04cc912e60 100644 --- a/tests/Core/Tokenizers/PHP/ShortArrayTest.inc +++ b/tests/Core/Tokenizers/PHP/ShortArrayTest.inc @@ -45,6 +45,15 @@ $var ClassName::CONSTANT_NAME[2]; /* testMagicConstantDereferencing */ $var = __FILE__[0]; +/* testPartiallyQualifiedConstantDereferencing */ +$var = MyNS\MY_CONSTANT[1]; + +/* testFQNConstantDereferencing */ +$var = \MY_CONSTANT[1]; + +/* testNamespaceRelativeConstantDereferencing */ +$var = namespace\MY_CONSTANT[1]; + /* testArrayAccessCurlyBraces */ $var = $array{'key'}['key']; diff --git a/tests/Core/Tokenizers/PHP/ShortArrayTest.php b/tests/Core/Tokenizers/PHP/ShortArrayTest.php index da9c7c1040..741da620d9 100644 --- a/tests/Core/Tokenizers/PHP/ShortArrayTest.php +++ b/tests/Core/Tokenizers/PHP/ShortArrayTest.php @@ -69,6 +69,9 @@ public static function dataSquareBrackets() 'global constant dereferencing' => ['/* testConstantDereferencing */'], 'class constant dereferencing' => ['/* testClassConstantDereferencing */'], 'magic constant dereferencing' => ['/* testMagicConstantDereferencing */'], + 'partially qualified constant dereferencing' => ['/* testPartiallyQualifiedConstantDereferencing */'], + 'fully qualified constant dereferencing' => ['/* testFQNConstantDereferencing */'], + 'namespace relative constant dereferencing' => ['/* testNamespaceRelativeConstantDereferencing */'], 'array access with curly braces' => ['/* testArrayAccessCurlyBraces */'], 'array literal dereferencing' => ['/* testArrayLiteralDereferencing */'], 'short array literal dereferencing' => ['/* testShortArrayLiteralDereferencing */'], diff --git a/tests/Core/Tokenizers/PHP/TypedConstantsTest.php b/tests/Core/Tokenizers/PHP/TypedConstantsTest.php index 6968c4ef8c..d40fa797f1 100644 --- a/tests/Core/Tokenizers/PHP/TypedConstantsTest.php +++ b/tests/Core/Tokenizers/PHP/TypedConstantsTest.php @@ -208,24 +208,19 @@ public static function dataTypedConstant() 'simple type: fully qualified name' => [ 'testMarker' => '/* testClassConstTypedClassFullyQualified */', 'sequence' => [ - T_NS_SEPARATOR, - T_STRING, + T_NAME_FULLY_QUALIFIED, ], ], 'simple type: namespace relative name' => [ 'testMarker' => '/* testClassConstTypedClassNamespaceRelative */', 'sequence' => [ - T_NAMESPACE, - T_NS_SEPARATOR, - T_STRING, + T_NAME_RELATIVE, ], ], 'simple type: partially qualified name' => [ 'testMarker' => '/* testClassConstTypedClassPartiallyQualified */', 'sequence' => [ - T_STRING, - T_NS_SEPARATOR, - T_STRING, + T_NAME_QUALIFIED, ], ], 'simple type: parent' => [ @@ -334,24 +329,19 @@ public static function dataNullableTypedConstant() 'nullable type: fully qualified name' => [ 'testMarker' => '/* testTraitConstTypedNullableClassFullyQualified */', 'sequence' => [ - T_NS_SEPARATOR, - T_STRING, + T_NAME_FULLY_QUALIFIED, ], ], 'nullable type: namespace relative name' => [ 'testMarker' => '/* testTraitConstTypedNullableClassNamespaceRelative */', 'sequence' => [ - T_NAMESPACE, - T_NS_SEPARATOR, - T_STRING, + T_NAME_RELATIVE, ], ], 'nullable type: partially qualified name' => [ 'testMarker' => '/* testTraitConstTypedNullableClassPartiallyQualified */', 'sequence' => [ - T_STRING, - T_NS_SEPARATOR, - T_STRING, + T_NAME_QUALIFIED, ], ], 'nullable type: parent' => [ @@ -440,22 +430,15 @@ public static function dataUnionTypedConstant() 'sequence' => [ T_STRING, T_TYPE_UNION, - T_NAMESPACE, - T_NS_SEPARATOR, - T_STRING, + T_NAME_RELATIVE, ], ], 'union type: FQN|Partial' => [ 'testMarker' => '/* testInterfaceConstTypedUnionFullyQualifiedPartiallyQualified */', 'sequence' => [ - T_NS_SEPARATOR, - T_STRING, - T_NS_SEPARATOR, - T_STRING, + T_NAME_FULLY_QUALIFIED, T_TYPE_UNION, - T_STRING, - T_NS_SEPARATOR, - T_STRING, + T_NAME_QUALIFIED, ], ], ]; @@ -485,22 +468,15 @@ public static function dataIntersectionTypedConstant() 'sequence' => [ T_STRING, T_TYPE_INTERSECTION, - T_NAMESPACE, - T_NS_SEPARATOR, - T_STRING, + T_NAME_RELATIVE, ], ], 'intersection type: FQN&Partial' => [ 'testMarker' => '/* testEnumConstTypedIntersectFullyQualifiedPartiallyQualified */', 'sequence' => [ - T_NS_SEPARATOR, - T_STRING, - T_NS_SEPARATOR, - T_STRING, + T_NAME_FULLY_QUALIFIED, T_TYPE_INTERSECTION, - T_STRING, - T_NS_SEPARATOR, - T_STRING, + T_NAME_QUALIFIED, ], ], ]; @@ -628,17 +604,12 @@ public static function dataDNFTypedConstant() 'testMarker' => '/* testAnonClassConstDNFTypeFQNRelativePartiallyQualified */', 'sequence' => [ T_TYPE_OPEN_PARENTHESIS, - T_NS_SEPARATOR, - T_STRING, + T_NAME_FULLY_QUALIFIED, T_TYPE_INTERSECTION, - T_NAMESPACE, - T_NS_SEPARATOR, - T_STRING, + T_NAME_RELATIVE, T_TYPE_CLOSE_PARENTHESIS, T_TYPE_UNION, - T_STRING, - T_NS_SEPARATOR, - T_STRING, + T_NAME_QUALIFIED, ], ], 'DNF type: invalid self/parent/static' => [ diff --git a/tests/Core/Tokenizers/Tokenizer/RecurseScopeMapDefaultKeywordConditionsTest.php b/tests/Core/Tokenizers/Tokenizer/RecurseScopeMapDefaultKeywordConditionsTest.php index 9b40fe1091..a38a1600ea 100644 --- a/tests/Core/Tokenizers/Tokenizer/RecurseScopeMapDefaultKeywordConditionsTest.php +++ b/tests/Core/Tokenizers/Tokenizer/RecurseScopeMapDefaultKeywordConditionsTest.php @@ -10,6 +10,7 @@ namespace PHP_CodeSniffer\Tests\Core\Tokenizers\Tokenizer; use PHP_CodeSniffer\Tests\Core\Tokenizers\AbstractTokenizerTestCase; +use PHP_CodeSniffer\Util\Tokens; final class RecurseScopeMapDefaultKeywordConditionsTest extends AbstractTokenizerTestCase { @@ -357,16 +358,21 @@ public static function dataSwitchDefault() */ public function testNotDefaultKeyword($testMarker, $testContent='DEFAULT') { - $tokens = $this->phpcsFile->getTokens(); + $targetTypes = Tokens::$nameTokens; + $targetTypes += [ + T_MATCH_DEFAULT => T_MATCH_DEFAULT, + T_DEFAULT => T_DEFAULT, + ]; + $target = $this->getTargetToken($testMarker, $targetTypes, $testContent); - $token = $this->getTargetToken($testMarker, [T_MATCH_DEFAULT, T_DEFAULT, T_STRING], $testContent); - $tokenArray = $tokens[$token]; + $tokens = $this->phpcsFile->getTokens(); + $tokenArray = $tokens[$target]; // Make sure we're looking at the right token. - $this->assertSame( - T_STRING, + $this->assertArrayHasKey( $tokenArray['code'], - sprintf('Token tokenized as %s, not T_STRING (code). Marker: %s.', $tokenArray['type'], $testMarker) + Tokens::$nameTokens, + sprintf('Token tokenized as %s, not identifier name (code). Marker: %s.', $tokenArray['type'], $testMarker) ); $this->assertArrayNotHasKey( @@ -405,10 +411,12 @@ public static function dataNotDefaultKeyword() 'testMarker' => '/* testClassPropertyAsShortArrayKey */', ], 'namespaced-constant-as-short-array-key' => [ - 'testMarker' => '/* testNamespacedConstantAsShortArrayKey */', + 'testMarker' => '/* testNamespacedConstantAsShortArrayKey */', + 'testContent' => 'SomeNamespace\DEFAULT', ], 'fqn-global-constant-as-short-array-key' => [ - 'testMarker' => '/* testFQNGlobalConstantAsShortArrayKey */', + 'testMarker' => '/* testFQNGlobalConstantAsShortArrayKey */', + 'testContent' => '\DEFAULT', ], 'class-constant-as-long-array-key' => [ 'testMarker' => '/* testClassConstantAsLongArrayKey */', @@ -443,10 +451,12 @@ public static function dataNotDefaultKeyword() 'testMarker' => '/* testClassPropertyInSwitchCase */', ], 'namespaced-constant-in-switch-case' => [ - 'testMarker' => '/* testNamespacedConstantInSwitchCase */', + 'testMarker' => '/* testNamespacedConstantInSwitchCase */', + 'testContent' => 'SomeNamespace\DEFAULT', ], 'namespace-relative-constant-in-switch-case' => [ - 'testMarker' => '/* testNamespaceRelativeConstantInSwitchCase */', + 'testMarker' => '/* testNamespaceRelativeConstantInSwitchCase */', + 'testContent' => 'namespace\DEFAULT', ], 'class-constant-declaration' => [