diff --git a/PHPCSUtils/Tokens/Collections.php b/PHPCSUtils/Tokens/Collections.php index b969c8d7..ff90058e 100644 --- a/PHPCSUtils/Tokens/Collections.php +++ b/PHPCSUtils/Tokens/Collections.php @@ -907,24 +907,26 @@ public static function propertyTypeTokensBC() * * @since 1.0.0-alpha4 This method replaces the {@see Collections::$returnTypeTokens} property. * @since 1.0.0-alpha4 Added support for PHP 8.0 union types. + * @since 1.0.0-alpha4 Added support for PHP 8.0 identifier name tokens. * * @return array => */ public static function returnTypeTokens() { - return [ - \T_STRING => \T_STRING, - \T_CALLABLE => \T_CALLABLE, - \T_SELF => \T_SELF, - \T_PARENT => \T_PARENT, - \T_STATIC => \T_STATIC, - \T_FALSE => \T_FALSE, // Union types only. - \T_NULL => \T_NULL, // Union types only. - \T_ARRAY => \T_ARRAY, // Arrow functions PHPCS < 3.5.4 + union types. - \T_NAMESPACE => \T_NAMESPACE, - \T_NS_SEPARATOR => \T_NS_SEPARATOR, - \T_BITWISE_OR => \T_BITWISE_OR, // Union types. + $tokens = [ + \T_CALLABLE => \T_CALLABLE, + \T_SELF => \T_SELF, + \T_PARENT => \T_PARENT, + \T_STATIC => \T_STATIC, + \T_FALSE => \T_FALSE, // Union types only. + \T_NULL => \T_NULL, // Union types only. + \T_ARRAY => \T_ARRAY, // Arrow functions PHPCS < 3.5.4 + union types. + \T_BITWISE_OR => \T_BITWISE_OR, // Union types. ]; + + $tokens += self::namespacedNameTokens(); + + return $tokens; } /** @@ -946,6 +948,7 @@ public static function returnTypeTokens() * * @since 1.0.0-alpha3 * @since 1.0.0-alpha4 Added support for PHP 8.0 union types. + * @since 1.0.0-alpha4 Added support for PHP 8.0 identifier name tokens. * * @return array => */ diff --git a/PHPCSUtils/Utils/FunctionDeclarations.php b/PHPCSUtils/Utils/FunctionDeclarations.php index dc0e7b33..28cd4f46 100644 --- a/PHPCSUtils/Utils/FunctionDeclarations.php +++ b/PHPCSUtils/Utils/FunctionDeclarations.php @@ -172,6 +172,7 @@ public static function getName(File $phpcsFile, $stackPtr) * `T_STRING` tokens and examine them to check if these are arrow functions. * - Support for PHP 8.0 union types. * - Support for namespace operator in type declarations. + * - Support for PHP 8.0 identifier name tokens in return types, cross-version PHP & PHPCS. * * @see \PHP_CodeSniffer\Files\File::getMethodProperties() Original source. * @see \PHPCSUtils\BackCompat\BCFile::getMethodProperties() Cross-version compatible version of the original. @@ -700,6 +701,7 @@ public static function isArrowFunction(File $phpcsFile, $stackPtr) * @see \PHPCSUtils\Utils\FunctionDeclarations::isArrowFunction() Related function. * * @since 1.0.0 + * @since 1.0.0-alpha4 Handling of PHP 8.0 identifier name tokens in return types, cross-version PHP & PHPCS. * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. * @param int $stackPtr The token to retrieve the openers/closers for. diff --git a/Tests/BackCompat/BCFile/GetMethodPropertiesTest.inc b/Tests/BackCompat/BCFile/GetMethodPropertiesTest.inc index b15d56fa..d77f490b 100644 --- a/Tests/BackCompat/BCFile/GetMethodPropertiesTest.inc +++ b/Tests/BackCompat/BCFile/GetMethodPropertiesTest.inc @@ -44,6 +44,12 @@ class MyClass { function myFunction(): \MyNamespace /** comment *\/ comment */ \MyClass /* comment */ \Foo {} + + /* testReturnUnqualifiedName */ + private function myFunction(): ?MyClass {} + + /* testReturnPartiallyQualifiedName */ + function myFunction(): Sub\Level\MyClass {} } abstract class MyClass diff --git a/Tests/BackCompat/BCFile/GetMethodPropertiesTest.php b/Tests/BackCompat/BCFile/GetMethodPropertiesTest.php index 3adc6e91..4282439d 100644 --- a/Tests/BackCompat/BCFile/GetMethodPropertiesTest.php +++ b/Tests/BackCompat/BCFile/GetMethodPropertiesTest.php @@ -343,6 +343,50 @@ public function testReturnMultilineNamespace() $this->getMethodPropertiesTestHelper('/* ' . __FUNCTION__ . ' */', $expected); } + /** + * Test a method with an unqualified named return type. + * + * @return void + */ + public function testReturnUnqualifiedName() + { + $expected = [ + 'scope' => 'private', + 'scope_specified' => true, + 'return_type' => '?MyClass', + 'return_type_token' => 8, // Offset from the T_FUNCTION token. + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* ' . __FUNCTION__ . ' */', $expected); + } + + /** + * Test a method with a partially qualified namespaced return type. + * + * @return void + */ + public function testReturnPartiallyQualifiedName() + { + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'Sub\Level\MyClass', + 'return_type_token' => 7, // Offset from the T_FUNCTION token. + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* ' . __FUNCTION__ . ' */', $expected); + } + /** * Test a basic abstract method. * diff --git a/Tests/Tokens/Collections/ReturnTypeTokensTest.php b/Tests/Tokens/Collections/ReturnTypeTokensTest.php index dbc63704..f9423344 100644 --- a/Tests/Tokens/Collections/ReturnTypeTokensTest.php +++ b/Tests/Tokens/Collections/ReturnTypeTokensTest.php @@ -33,7 +33,6 @@ class ReturnTypeTokensTest extends TestCase public function testReturnTypeTokens() { $expected = [ - \T_STRING => \T_STRING, \T_CALLABLE => \T_CALLABLE, \T_SELF => \T_SELF, \T_PARENT => \T_PARENT, @@ -41,11 +40,18 @@ public function testReturnTypeTokens() \T_FALSE => \T_FALSE, \T_NULL => \T_NULL, \T_ARRAY => \T_ARRAY, - \T_NAMESPACE => \T_NAMESPACE, - \T_NS_SEPARATOR => \T_NS_SEPARATOR, \T_BITWISE_OR => \T_BITWISE_OR, + \T_NS_SEPARATOR => \T_NS_SEPARATOR, + \T_NAMESPACE => \T_NAMESPACE, + \T_STRING => \T_STRING, ]; + if (\version_compare(\PHP_VERSION_ID, '80000', '>=') === true) { + $expected[\T_NAME_QUALIFIED] = \T_NAME_QUALIFIED; + $expected[\T_NAME_FULLY_QUALIFIED] = \T_NAME_FULLY_QUALIFIED; + $expected[\T_NAME_RELATIVE] = \T_NAME_RELATIVE; + } + $this->assertSame($expected, Collections::returnTypeTokens()); } } diff --git a/Tests/Utils/FunctionDeclarations/IsArrowFunctionTest.inc b/Tests/Utils/FunctionDeclarations/IsArrowFunctionTest.inc index c7b3fbc4..733cb236 100644 --- a/Tests/Utils/FunctionDeclarations/IsArrowFunctionTest.inc +++ b/Tests/Utils/FunctionDeclarations/IsArrowFunctionTest.inc @@ -61,6 +61,9 @@ $a = fn($x) => yield 'k' => $x; /* testReturnTypeNamespacedClass */ $fn = fn($x) : ?\My\NS\ClassName => $x; +/* testReturnTypePartiallyQualifiedClass */ +$fn = fn($x) : ?NS\ClassName => $x; + /* testReturnTypeNullableFQNClass */ $a = fn(?\DateTime $x) : ?\DateTime => $x; diff --git a/Tests/Utils/FunctionDeclarations/IsArrowFunctionTest.php b/Tests/Utils/FunctionDeclarations/IsArrowFunctionTest.php index 6d73fd12..8667f397 100644 --- a/Tests/Utils/FunctionDeclarations/IsArrowFunctionTest.php +++ b/Tests/Utils/FunctionDeclarations/IsArrowFunctionTest.php @@ -352,6 +352,18 @@ public function dataArrowFunction() ], ], ], + 'arrow-function-with-return-type-nullable-partially-qualified-class' => [ + '/* testReturnTypePartiallyQualifiedClass */', + [ + 'is' => true, + 'get' => [ + 'parenthesis_opener' => 1, + 'parenthesis_closer' => 3, + 'scope_opener' => ($php8Names === true) ? 10 : 12, + 'scope_closer' => ($php8Names === true) ? 13 : 15, + ], + ], + ], 'arrow-function-with-fqn-class' => [ '/* testReturnTypeNullableFQNClass */', [ diff --git a/Tests/Utils/Operators/IsTypeUnionTest.inc b/Tests/Utils/Operators/IsTypeUnionTest.inc index 8d210bac..b843875d 100644 --- a/Tests/Utils/Operators/IsTypeUnionTest.inc +++ b/Tests/Utils/Operators/IsTypeUnionTest.inc @@ -27,6 +27,15 @@ class TypeUnion /* testTypeUnionPropertyMulti3 */ | null $arrayOrFalse; + /* testTypeUnionPropertyNamespaceRelative */ + public namespace\Sub\NameA|namespace\Sub\NameB $namespaceRelative; + + /* testTypeUnionPropertyPartiallyQualified */ + public Partially\Qualified\NameA|Partially\Qualified\NameB $partiallyQual; + + /* testTypeUnionPropertyFullyQualified */ + public \Fully\Qualified\NameA|\Fully\Qualified\NameB $fullyQual; + public function paramTypes( /* testTypeUnionParam1 */ int|float $paramA /* testBitwiseOrParamDefaultValue */ = CONSTANT_A | CONSTANT_B, @@ -38,6 +47,15 @@ class TypeUnion return (($a1 ^ $b1) |($a2 ^ $b2)) + $c; } + public function identifierNames( + /* testTypeUnionParamNamespaceRelative */ + namespace\Sub\NameA|namespace\Sub\NameB $paramA, + /* testTypeUnionParamPartiallyQualified */ + Partially\Qualified\NameA|Partially\Qualified\NameB $paramB, + /* testTypeUnionParamFullyQualified */ + \Fully\Qualified\NameA|\Fully\Qualified\NameB $paramC, + ) {} + /* testTypeUnionReturnType */ public function returnType() : int|false {} @@ -49,6 +67,12 @@ class TypeUnion /* testTypeUnionReturnTypeNamespaceRelative */ public function identifierNamesReturnRelative() : namespace\Sub\NameA|namespace\Sub\NameB {} + + /* testTypeUnionReturnPartiallyQualified */ + public function identifierNamesReturnPQ() : Partially\Qualified\NameA|Partially\Qualified\NameB {} + + /* testTypeUnionReturnFullyQualified */ + public function identifierNamesReturnFQ() : \Fully\Qualified\NameA|\Fully\Qualified\NameB {} } /* testTypeUnionClosureParamIllegalNullable */ diff --git a/Tests/Utils/Operators/IsTypeUnionTest.php b/Tests/Utils/Operators/IsTypeUnionTest.php index 51233227..fd892b51 100644 --- a/Tests/Utils/Operators/IsTypeUnionTest.php +++ b/Tests/Utils/Operators/IsTypeUnionTest.php @@ -77,14 +77,22 @@ public function dataIsTypeUnion() 'property-multi-type-1' => ['/* testTypeUnionPropertyMulti1 */'], 'property-multi-type-2' => ['/* testTypeUnionPropertyMulti2 */'], 'property-multi-type-3' => ['/* testTypeUnionPropertyMulti3 */'], + 'property-namespace-operator' => ['/* testTypeUnionPropertyNamespaceRelative */'], + 'property-partially-qualified' => ['/* testTypeUnionPropertyPartiallyQualified */'], + 'property-fully-qualified' => ['/* testTypeUnionPropertyFullyQualified */'], 'parameter-multi-type-1' => ['/* testTypeUnionParam1 */'], 'parameter-multi-type-2' => ['/* testTypeUnionParam2 */'], 'parameter-multi-type-3' => ['/* testTypeUnionParam3 */'], + 'parameter-namespace-operator' => ['/* testTypeUnionParamNamespaceRelative */'], + 'parameter-partially-qualified' => ['/* testTypeUnionParamPartiallyQualified */'], + 'parameter-fully-qualified' => ['/* testTypeUnionParamFullyQualified */'], 'return' => ['/* testTypeUnionReturnType */'], 'constructor-property-promotion' => ['/* testTypeUnionConstructorPropertyPromotion */'], 'return-abstract-method-1' => ['/* testTypeUnionAbstractMethodReturnType1 */'], 'return-abstract-method-2' => ['/* testTypeUnionAbstractMethodReturnType2 */'], 'return-namespace-operator' => ['/* testTypeUnionReturnTypeNamespaceRelative */'], + 'return-partially-qualified' => ['/* testTypeUnionReturnPartiallyQualified */'], + 'return-fully-qualified' => ['/* testTypeUnionReturnFullyQualified */'], 'parameter-closure-with-nullable' => ['/* testTypeUnionClosureParamIllegalNullable */'], 'return-closure' => ['/* testTypeUnionClosureReturn */'], 'parameter-arrow' => ['/* testTypeUnionArrowParam */'],