diff --git a/tests/Core/File/GetMethodPropertiesTest.inc b/tests/Core/File/GetMethodPropertiesTest.inc index 5ebee67947..1bc570595e 100644 --- a/tests/Core/File/GetMethodPropertiesTest.inc +++ b/tests/Core/File/GetMethodPropertiesTest.inc @@ -34,16 +34,23 @@ class MyClass { /* testMessyNullableReturnMethod */ public function myFunction() /* comment */ : - /* comment */ ? //comment + /* comment */ ? // phpcs:ignore Stnd.Cat.Sniff -- For reasons. array {} /* testReturnNamespace */ function myFunction(): \MyNamespace\MyClass {} /* testReturnMultilineNamespace */ + // Parse error in PHP 8.0. function myFunction(): \MyNamespace /** comment *\/ comment */ \MyClass /* comment */ \Foo {} + + /* testReturnUnqualifiedName */ + private function myFunction(): ?MyClass {} + + /* testReturnPartiallyQualifiedName */ + function myFunction(): Sub\Level\MyClass {} } abstract class MyClass @@ -157,3 +164,27 @@ function pseudoTypeTrue(): ?true {} /* testPHP82PseudoTypeFalseAndTrue */ // Intentional fatal error - Type contains both true and false, bool should be used instead, but that's not the concern of the method. function pseudoTypeFalseAndTrue(): true|false {} + +/* testNotAFunction */ +return true; + +/* testPhpcsIssue1264 */ +function foo() : array { + echo $foo; +} + +/* testArrowFunctionArrayReturnValue */ +$fn = fn(): array => [a($a, $b)]; + +/* testArrowFunctionReturnByRef */ +fn&(?string $a) : ?string => $b; + +/* testFunctionCallFnPHPCS353-354 */ +$value = $obj->fn(true); + +/* testFunctionDeclarationNestedInTernaryPHPCS2975 */ +return (!$a ? [ new class { public function b(): c {} } ] : []); + +/* testArrowFunctionLiveCoding */ +// Intentional parse error. This has to be the last test in the file. +$fn = fn diff --git a/tests/Core/File/GetMethodPropertiesTest.php b/tests/Core/File/GetMethodPropertiesTest.php index e971611ea4..b7c8bc928c 100644 --- a/tests/Core/File/GetMethodPropertiesTest.php +++ b/tests/Core/File/GetMethodPropertiesTest.php @@ -20,6 +20,59 @@ class GetMethodPropertiesTest extends AbstractMethodUnitTest { + /** + * Test receiving an expected exception when a non function token is passed. + * + * @param string $commentString The comment which preceeds the test. + * @param string|int|array $targetTokenType The token type to search for after $commentString. + * + * @dataProvider dataNotAFunctionException + * + * @return void + */ + public function testNotAFunctionException($commentString, $targetTokenType) + { + $this->expectRunTimeException('$stackPtr must be of type T_FUNCTION or T_CLOSURE or T_FN'); + + $next = $this->getTargetToken($commentString, $targetTokenType); + self::$phpcsFile->getMethodProperties($next); + + }//end testNotAFunctionException() + + + /** + * Data Provider. + * + * @see testNotAFunctionException() For the array format. + * + * @return array>> + */ + public static function dataNotAFunctionException() + { + return [ + 'return' => [ + 'commentString' => '/* testNotAFunction */', + 'targetTokenType' => T_RETURN, + ], + 'function-call-fn-phpcs-3.5.3-3.5.4' => [ + 'commentString' => '/* testFunctionCallFnPHPCS353-354 */', + 'targetTokenType' => [ + T_FN, + T_STRING, + ], + ], + 'fn-live-coding' => [ + 'commentString' => '/* testArrowFunctionLiveCoding */', + 'targetTokenType' => [ + T_FN, + T_STRING, + ], + ], + ]; + + }//end dataNotAFunctionException() + + /** * Test a basic function. * @@ -28,14 +81,16 @@ class GetMethodPropertiesTest extends AbstractMethodUnitTest public function testBasicFunction() { $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => '', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '', + 'return_type_token' => false, + 'return_type_end_token' => false, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -50,15 +105,18 @@ public function testBasicFunction() */ public function testReturnFunction() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'array', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'array', + 'return_type_token' => 11, + 'return_type_end_token' => 11, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -73,15 +131,18 @@ public function testReturnFunction() */ public function testNestedClosure() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'int', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'int', + 'return_type_token' => 8, + 'return_type_end_token' => 8, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -97,14 +158,16 @@ public function testNestedClosure() public function testBasicMethod() { $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => '', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '', + 'return_type_token' => false, + 'return_type_end_token' => false, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -120,14 +183,16 @@ public function testBasicMethod() public function testPrivateStaticMethod() { $expected = [ - 'scope' => 'private', - 'scope_specified' => true, - 'return_type' => '', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => true, - 'has_body' => true, + 'scope' => 'private', + 'scope_specified' => true, + 'return_type' => '', + 'return_type_token' => false, + 'return_type_end_token' => false, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => true, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -143,14 +208,16 @@ public function testPrivateStaticMethod() public function testFinalMethod() { $expected = [ - 'scope' => 'public', - 'scope_specified' => true, - 'return_type' => '', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => true, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => true, + 'return_type' => '', + 'return_type_token' => false, + 'return_type_end_token' => false, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => true, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -165,15 +232,18 @@ public function testFinalMethod() */ public function testProtectedReturnMethod() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'protected', - 'scope_specified' => true, - 'return_type' => 'int', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'protected', + 'scope_specified' => true, + 'return_type' => 'int', + 'return_type_token' => 8, + 'return_type_end_token' => 8, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -188,15 +258,18 @@ public function testProtectedReturnMethod() */ public function testPublicReturnMethod() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => true, - 'return_type' => 'array', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => true, + 'return_type' => 'array', + 'return_type_token' => 7, + 'return_type_end_token' => 7, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -211,15 +284,18 @@ public function testPublicReturnMethod() */ public function testNullableReturnMethod() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => true, - 'return_type' => '?array', - 'nullable_return_type' => true, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => true, + 'return_type' => '?array', + 'return_type_token' => 8, + 'return_type_end_token' => 8, + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -234,15 +310,18 @@ public function testNullableReturnMethod() */ public function testMessyNullableReturnMethod() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => true, - 'return_type' => '?array', - 'nullable_return_type' => true, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => true, + 'return_type' => '?array', + 'return_type_token' => 18, + 'return_type_end_token' => 18, + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -257,15 +336,18 @@ public function testMessyNullableReturnMethod() */ public function testReturnNamespace() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => '\MyNamespace\MyClass', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '\MyNamespace\MyClass', + 'return_type_token' => 7, + 'return_type_end_token' => 10, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -280,15 +362,18 @@ public function testReturnNamespace() */ public function testReturnMultilineNamespace() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => '\MyNamespace\MyClass\Foo', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '\MyNamespace\MyClass\Foo', + 'return_type_token' => 7, + 'return_type_end_token' => 23, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -296,6 +381,58 @@ public function testReturnMultilineNamespace() }//end testReturnMultilineNamespace() + /** + * Test a method with an unqualified named return type. + * + * @return void + */ + public function testReturnUnqualifiedName() + { + // Offsets are relative to the T_FUNCTION token. + $expected = [ + 'scope' => 'private', + 'scope_specified' => true, + 'return_type' => '?MyClass', + 'return_type_token' => 8, + 'return_type_end_token' => 8, + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testReturnUnqualifiedName() + + + /** + * Test a method with a partially qualified namespaced return type. + * + * @return void + */ + public function testReturnPartiallyQualifiedName() + { + // Offsets are relative to the T_FUNCTION token. + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'Sub\Level\MyClass', + 'return_type_token' => 7, + 'return_type_end_token' => 11, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testReturnPartiallyQualifiedName() + + /** * Test a basic abstract method. * @@ -304,14 +441,16 @@ public function testReturnMultilineNamespace() public function testAbstractMethod() { $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => '', - 'nullable_return_type' => false, - 'is_abstract' => true, - 'is_final' => false, - 'is_static' => false, - 'has_body' => false, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '', + 'return_type_token' => false, + 'return_type_end_token' => false, + 'nullable_return_type' => false, + 'is_abstract' => true, + 'is_final' => false, + 'is_static' => false, + 'has_body' => false, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -326,15 +465,18 @@ public function testAbstractMethod() */ public function testAbstractReturnMethod() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'protected', - 'scope_specified' => true, - 'return_type' => 'bool', - 'nullable_return_type' => false, - 'is_abstract' => true, - 'is_final' => false, - 'is_static' => false, - 'has_body' => false, + 'scope' => 'protected', + 'scope_specified' => true, + 'return_type' => 'bool', + 'return_type_token' => 7, + 'return_type_end_token' => 7, + 'nullable_return_type' => false, + 'is_abstract' => true, + 'is_final' => false, + 'is_static' => false, + 'has_body' => false, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -350,14 +492,16 @@ public function testAbstractReturnMethod() public function testInterfaceMethod() { $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => '', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => false, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '', + 'return_type_token' => false, + 'return_type_end_token' => false, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => false, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -372,15 +516,18 @@ public function testInterfaceMethod() */ public function testArrowFunction() { + // Offsets are relative to the T_FN token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'int', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => true, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'int', + 'return_type_token' => 9, + 'return_type_end_token' => 9, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => true, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -395,15 +542,18 @@ public function testArrowFunction() */ public function testReturnTypeStatic() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'private', - 'scope_specified' => true, - 'return_type' => 'static', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'private', + 'scope_specified' => true, + 'return_type' => 'static', + 'return_type_token' => 7, + 'return_type_end_token' => 7, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -418,15 +568,18 @@ public function testReturnTypeStatic() */ public function testPHP8MixedTypeHint() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'mixed', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'mixed', + 'return_type_token' => 7, + 'return_type_end_token' => 7, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -441,15 +594,18 @@ public function testPHP8MixedTypeHint() */ public function testPHP8MixedTypeHintNullable() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => '?mixed', - 'nullable_return_type' => true, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '?mixed', + 'return_type_token' => 8, + 'return_type_end_token' => 8, + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -464,15 +620,18 @@ public function testPHP8MixedTypeHintNullable() */ public function testNamespaceOperatorTypeHint() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => '?namespace\Name', - 'nullable_return_type' => true, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '?namespace\Name', + 'return_type_token' => 9, + 'return_type_end_token' => 11, + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -487,15 +646,18 @@ public function testNamespaceOperatorTypeHint() */ public function testPHP8UnionTypesSimple() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'int|float', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'int|float', + 'return_type_token' => 9, + 'return_type_end_token' => 11, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -510,15 +672,18 @@ public function testPHP8UnionTypesSimple() */ public function testPHP8UnionTypesTwoClasses() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'MyClassA|\Package\MyClassB', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'MyClassA|\Package\MyClassB', + 'return_type_token' => 6, + 'return_type_end_token' => 11, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -533,15 +698,18 @@ public function testPHP8UnionTypesTwoClasses() */ public function testPHP8UnionTypesAllBaseTypes() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'array|bool|callable|int|float|null|Object|string', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'array|bool|callable|int|float|null|Object|string', + 'return_type_token' => 8, + 'return_type_end_token' => 22, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -552,19 +720,24 @@ public function testPHP8UnionTypesAllBaseTypes() /** * Verify recognition of PHP8 union type declaration with all pseudo types. * + * Note: "Resource" is not a type, but seen as a class name. + * * @return void */ public function testPHP8UnionTypesAllPseudoTypes() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'false|MIXED|self|parent|static|iterable|Resource|void', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'false|MIXED|self|parent|static|iterable|Resource|void', + 'return_type_token' => 9, + 'return_type_end_token' => 23, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -579,15 +752,18 @@ public function testPHP8UnionTypesAllPseudoTypes() */ public function testPHP8UnionTypesNullable() { + // Offsets are relative to the T_CLOSURE token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => '?int|float', - 'nullable_return_type' => true, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '?int|float', + 'return_type_token' => 12, + 'return_type_end_token' => 14, + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -602,15 +778,18 @@ public function testPHP8UnionTypesNullable() */ public function testPHP8PseudoTypeNull() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'null', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'null', + 'return_type_token' => 7, + 'return_type_end_token' => 7, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -625,15 +804,18 @@ public function testPHP8PseudoTypeNull() */ public function testPHP8PseudoTypeFalse() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'false', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'false', + 'return_type_token' => 7, + 'return_type_end_token' => 7, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -648,15 +830,18 @@ public function testPHP8PseudoTypeFalse() */ public function testPHP8PseudoTypeFalseAndBool() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'bool|false', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'bool|false', + 'return_type_token' => 7, + 'return_type_end_token' => 9, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -671,15 +856,18 @@ public function testPHP8PseudoTypeFalseAndBool() */ public function testPHP8ObjectAndClass() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'object|ClassName', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'object|ClassName', + 'return_type_token' => 7, + 'return_type_end_token' => 9, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -694,15 +882,18 @@ public function testPHP8ObjectAndClass() */ public function testPHP8PseudoTypeIterableAndArray() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => true, - 'return_type' => 'iterable|array|Traversable', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => false, + 'scope' => 'public', + 'scope_specified' => true, + 'return_type' => 'iterable|array|Traversable', + 'return_type_token' => 7, + 'return_type_end_token' => 11, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => false, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -717,15 +908,18 @@ public function testPHP8PseudoTypeIterableAndArray() */ public function testPHP8DuplicateTypeInUnionWhitespaceAndComment() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'int|string|INT', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'int|string|INT', + 'return_type_token' => 7, + 'return_type_end_token' => 17, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -740,15 +934,18 @@ public function testPHP8DuplicateTypeInUnionWhitespaceAndComment() */ public function testPHP81NeverType() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'never', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'never', + 'return_type_token' => 7, + 'return_type_end_token' => 7, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -763,15 +960,18 @@ public function testPHP81NeverType() */ public function testPHP81NullableNeverType() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => '?never', - 'nullable_return_type' => true, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '?never', + 'return_type_token' => 8, + 'return_type_end_token' => 8, + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -786,15 +986,18 @@ public function testPHP81NullableNeverType() */ public function testPHP8IntersectionTypes() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'Foo&Bar', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'Foo&Bar', + 'return_type_token' => 7, + 'return_type_end_token' => 9, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -809,15 +1012,18 @@ public function testPHP8IntersectionTypes() */ public function testPHP81MoreIntersectionTypes() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'MyClassA&\Package\MyClassB&\Package\MyClassC', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'MyClassA&\Package\MyClassB&\Package\MyClassC', + 'return_type_token' => 7, + 'return_type_end_token' => 17, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -832,15 +1038,18 @@ public function testPHP81MoreIntersectionTypes() */ public function testPHP81IntersectionArrowFunction() { + // Offsets are relative to the T_FN token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'MyClassA&\Package\MyClassB', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'MyClassA&\Package\MyClassB', + 'return_type_token' => 6, + 'return_type_end_token' => 11, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -855,15 +1064,18 @@ public function testPHP81IntersectionArrowFunction() */ public function testPHP81IllegalIntersectionTypes() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'string&int', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'string&int', + 'return_type_token' => 6, + 'return_type_end_token' => 8, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -878,15 +1090,18 @@ public function testPHP81IllegalIntersectionTypes() */ public function testPHP81NullableIntersectionTypes() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => '?Foo&Bar', - 'nullable_return_type' => true, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '?Foo&Bar', + 'return_type_token' => 7, + 'return_type_end_token' => 9, + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -901,15 +1116,18 @@ public function testPHP81NullableIntersectionTypes() */ public function testPHP82PseudoTypeTrue() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => '?true', - 'nullable_return_type' => true, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '?true', + 'return_type_token' => 8, + 'return_type_end_token' => 8, + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -924,15 +1142,18 @@ public function testPHP82PseudoTypeTrue() */ public function testPHP82PseudoTypeFalseAndTrue() { + // Offsets are relative to the T_FUNCTION token. $expected = [ - 'scope' => 'public', - 'scope_specified' => false, - 'return_type' => 'true|false', - 'nullable_return_type' => false, - 'is_abstract' => false, - 'is_final' => false, - 'is_static' => false, - 'has_body' => true, + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'true|false', + 'return_type_token' => 7, + 'return_type_end_token' => 9, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, ]; $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -940,11 +1161,121 @@ public function testPHP82PseudoTypeFalseAndTrue() }//end testPHP82PseudoTypeFalseAndTrue() + /** + * Test for incorrect tokenization of array return type declarations in PHPCS < 2.8.0. + * + * @link https://github.com/squizlabs/PHP_CodeSniffer/pull/1264 + * + * @return void + */ + public function testPhpcsIssue1264() + { + // Offsets are relative to the T_FUNCTION token. + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'array', + 'return_type_token' => 8, + 'return_type_end_token' => 8, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPhpcsIssue1264() + + + /** + * Test handling of incorrect tokenization of array return type declarations for arrow functions + * in a very specific code sample in PHPCS < 3.5.4. + * + * @link https://github.com/squizlabs/PHP_CodeSniffer/issues/2773 + * + * @return void + */ + public function testArrowFunctionArrayReturnValue() + { + // Offsets are relative to the T_FN token. + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'array', + 'return_type_token' => 5, + 'return_type_end_token' => 5, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testArrowFunctionArrayReturnValue() + + + /** + * Test handling of an arrow function returning by reference. + * + * @return void + */ + public function testArrowFunctionReturnByRef() + { + // Offsets are relative to the T_FN token. + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '?string', + 'return_type_token' => 12, + 'return_type_end_token' => 12, + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testArrowFunctionReturnByRef() + + + /** + * Test handling of function declaration nested in a ternary, where the colon for the + * return type was incorrectly tokenized as T_INLINE_ELSE prior to PHPCS 3.5.7. + * + * @return void + */ + public function testFunctionDeclarationNestedInTernaryPHPCS2975() + { + // Offsets are relative to the T_FN token. + $expected = [ + 'scope' => 'public', + 'scope_specified' => true, + 'return_type' => 'c', + 'return_type_token' => 7, + 'return_type_end_token' => 7, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testFunctionDeclarationNestedInTernaryPHPCS2975() + + /** * Test helper. * - * @param string $commentString The comment which preceeds the test. - * @param array $expected The expected function output. + * @param string $commentString The comment which preceeds the test. + * @param array $expected The expected function output. * * @return void */ @@ -953,8 +1284,14 @@ private function getMethodPropertiesTestHelper($commentString, $expected) $function = $this->getTargetToken($commentString, [T_FUNCTION, T_CLOSURE, T_FN]); $found = self::$phpcsFile->getMethodProperties($function); - // Unset those indexes which are not being tested. - unset($found['return_type_token'], $found['return_type_end_token']); + // Convert offsets to absolute positions in the token stream. + if (is_int($expected['return_type_token']) === true) { + $expected['return_type_token'] += $function; + } + + if (is_int($expected['return_type_end_token']) === true) { + $expected['return_type_end_token'] += $function; + } $this->assertSame($expected, $found);