From bbe90b005d67ea37ec6b774df5a5dd55fbf37b32 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Tue, 2 Jan 2024 11:30:58 +0100 Subject: [PATCH 1/2] Tests/GetMethodParametersTest: more thoroughly test existing tests This adds testing of various `*_token` return value indexes to the pre-existing tests, same as was already being tested in PHPCSUtils. :point_right: the diff will be easiest to review while ignoring whitespace differences due to the array alignment changes to comply with CS. --- tests/Core/File/GetMethodParametersTest.php | 1215 +++++++++++++------ 1 file changed, 854 insertions(+), 361 deletions(-) diff --git a/tests/Core/File/GetMethodParametersTest.php b/tests/Core/File/GetMethodParametersTest.php index 6f9b2adf9e..b383fb7568 100644 --- a/tests/Core/File/GetMethodParametersTest.php +++ b/tests/Core/File/GetMethodParametersTest.php @@ -3,7 +3,9 @@ * Tests for the \PHP_CodeSniffer\Files\File::getMethodParameters method. * * @author Greg Sherwood + * @author Juliette Reinders Folmer * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @copyright 2019-2024 PHPCSStandards Contributors * @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence */ @@ -27,15 +29,22 @@ class GetMethodParametersTest extends AbstractMethodUnitTest */ public function testPassByReference() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => '&$var', - 'has_attributes' => false, - 'pass_by_reference' => true, - 'variable_length' => false, - 'type_hint' => '', - 'nullable_type' => false, + 'token' => 5, + 'name' => '$var', + 'content' => '&$var', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'reference_token' => 4, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -50,15 +59,22 @@ public function testPassByReference() */ public function testArrayHint() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => 'array $var', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'array', - 'nullable_type' => false, + 'token' => 6, + 'name' => '$var', + 'content' => 'array $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'array', + 'type_hint_token' => 4, + 'type_hint_end_token' => 4, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -73,15 +89,22 @@ public function testArrayHint() */ public function testVariable() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => '$var', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => '', - 'nullable_type' => false, + 'token' => 4, + 'name' => '$var', + 'content' => '$var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -96,16 +119,25 @@ public function testVariable() */ public function testSingleDefaultValue() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var1', - 'content' => '$var1=self::CONSTANT', - 'default' => 'self::CONSTANT', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => '', - 'nullable_type' => false, + 'token' => 4, + 'name' => '$var1', + 'content' => '$var1=self::CONSTANT', + 'default' => 'self::CONSTANT', + 'default_token' => 6, + 'default_equal_token' => 5, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -120,26 +152,43 @@ public function testSingleDefaultValue() */ public function testDefaultValues() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var1', - 'content' => '$var1=1', - 'default' => '1', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => '', - 'nullable_type' => false, + 'token' => 4, + 'name' => '$var1', + 'content' => '$var1=1', + 'default' => '1', + 'default_token' => 6, + 'default_equal_token' => 5, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => 7, ]; $expected[1] = [ - 'name' => '$var2', - 'content' => "\$var2='value'", - 'default' => "'value'", - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => '', - 'nullable_type' => false, + 'token' => 9, + 'name' => '$var2', + 'content' => "\$var2='value'", + 'default' => "'value'", + 'default_token' => 11, + 'default_equal_token' => 10, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -154,25 +203,38 @@ public function testDefaultValues() */ public function testTypeHint() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var1', - 'content' => 'foo $var1', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'foo', - 'nullable_type' => false, + 'token' => 6, + 'name' => '$var1', + 'content' => 'foo $var1', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'foo', + 'type_hint_token' => 4, + 'type_hint_end_token' => 4, + 'nullable_type' => false, + 'comma_token' => 7, ]; $expected[1] = [ - 'name' => '$var2', - 'content' => 'bar $var2', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'bar', - 'nullable_type' => false, + 'token' => 11, + 'name' => '$var2', + 'content' => 'bar $var2', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'bar', + 'type_hint_token' => 9, + 'type_hint_end_token' => 9, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -187,15 +249,22 @@ public function testTypeHint() */ public function testSelfTypeHint() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => 'self $var', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'self', - 'nullable_type' => false, + 'token' => 6, + 'name' => '$var', + 'content' => 'self $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'self', + 'type_hint_token' => 4, + 'type_hint_end_token' => 4, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -210,25 +279,38 @@ public function testSelfTypeHint() */ public function testNullableTypeHint() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var1', - 'content' => '?int $var1', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => '?int', - 'nullable_type' => true, + 'token' => 7, + 'name' => '$var1', + 'content' => '?int $var1', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?int', + 'type_hint_token' => 5, + 'type_hint_end_token' => 5, + 'nullable_type' => true, + 'comma_token' => 8, ]; $expected[1] = [ - 'name' => '$var2', - 'content' => '?\bar $var2', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => '?\bar', - 'nullable_type' => true, + 'token' => 14, + 'name' => '$var2', + 'content' => '?\bar $var2', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?\bar', + 'type_hint_token' => 11, + 'type_hint_end_token' => 12, + 'nullable_type' => true, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -243,16 +325,25 @@ public function testNullableTypeHint() */ public function testBitwiseAndConstantExpressionDefaultValue() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$a', - 'content' => '$a = 10 & 20', - 'default' => '10 & 20', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => '', - 'nullable_type' => false, + 'token' => 4, + 'name' => '$a', + 'content' => '$a = 10 & 20', + 'default' => '10 & 20', + 'default_token' => 8, + 'default_equal_token' => 6, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -267,25 +358,38 @@ public function testBitwiseAndConstantExpressionDefaultValue() */ public function testArrowFunction() { + // Offsets are relative to the T_FN token. $expected = []; $expected[0] = [ - 'name' => '$a', - 'content' => 'int $a', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'int', - 'nullable_type' => false, + 'token' => 4, + 'name' => '$a', + 'content' => 'int $a', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'int', + 'type_hint_token' => 2, + 'type_hint_end_token' => 2, + 'nullable_type' => false, + 'comma_token' => 5, ]; $expected[1] = [ - 'name' => '$b', - 'content' => '...$b', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => true, - 'type_hint' => '', - 'nullable_type' => false, + 'token' => 8, + 'name' => '$b', + 'content' => '...$b', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => true, + 'variadic_token' => 7, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -300,15 +404,22 @@ public function testArrowFunction() */ public function testPHP8MixedTypeHint() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var1', - 'content' => 'mixed &...$var1', - 'has_attributes' => false, - 'pass_by_reference' => true, - 'variable_length' => true, - 'type_hint' => 'mixed', - 'nullable_type' => false, + 'token' => 8, + 'name' => '$var1', + 'content' => 'mixed &...$var1', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'reference_token' => 6, + 'variable_length' => true, + 'variadic_token' => 7, + 'type_hint' => 'mixed', + 'type_hint_token' => 4, + 'type_hint_end_token' => 4, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -323,15 +434,22 @@ public function testPHP8MixedTypeHint() */ public function testPHP8MixedTypeHintNullable() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var1', - 'content' => '?Mixed $var1', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => '?Mixed', - 'nullable_type' => true, + 'token' => 7, + 'name' => '$var1', + 'content' => '?Mixed $var1', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?Mixed', + 'type_hint_token' => 5, + 'type_hint_end_token' => 5, + 'nullable_type' => true, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -346,15 +464,22 @@ public function testPHP8MixedTypeHintNullable() */ public function testNamespaceOperatorTypeHint() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var1', - 'content' => '?namespace\Name $var1', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => '?namespace\Name', - 'nullable_type' => true, + 'token' => 9, + 'name' => '$var1', + 'content' => '?namespace\Name $var1', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?namespace\Name', + 'type_hint_token' => 5, + 'type_hint_end_token' => 7, + 'nullable_type' => true, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -369,24 +494,37 @@ public function testNamespaceOperatorTypeHint() */ public function testPHP8UnionTypesSimple() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$number', - 'content' => 'int|float $number', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'int|float', - 'nullable_type' => false, + 'token' => 8, + 'name' => '$number', + 'content' => 'int|float $number', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'int|float', + 'type_hint_token' => 4, + 'type_hint_end_token' => 6, + 'nullable_type' => false, + 'comma_token' => 9, ]; $expected[1] = [ - 'name' => '$obj', - 'content' => 'self|parent &...$obj', - 'has_attributes' => false, - 'pass_by_reference' => true, - 'variable_length' => true, - 'type_hint' => 'self|parent', - 'nullable_type' => false, + 'token' => 17, + 'name' => '$obj', + 'content' => 'self|parent &...$obj', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'reference_token' => 15, + 'variable_length' => true, + 'variadic_token' => 16, + 'type_hint' => 'self|parent', + 'type_hint_token' => 11, + 'type_hint_end_token' => 13, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -401,24 +539,37 @@ public function testPHP8UnionTypesSimple() */ public function testPHP8UnionTypesWithSpreadOperatorAndReference() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$paramA', - 'content' => 'float|null &$paramA', - 'has_attributes' => false, - 'pass_by_reference' => true, - 'variable_length' => false, - 'type_hint' => 'float|null', - 'nullable_type' => false, + 'token' => 9, + 'name' => '$paramA', + 'content' => 'float|null &$paramA', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'reference_token' => 8, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'float|null', + 'type_hint_token' => 4, + 'type_hint_end_token' => 6, + 'nullable_type' => false, + 'comma_token' => 10, ]; $expected[1] = [ - 'name' => '$paramB', - 'content' => 'string|int ...$paramB', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => true, - 'type_hint' => 'string|int', - 'nullable_type' => false, + 'token' => 17, + 'name' => '$paramB', + 'content' => 'string|int ...$paramB', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => true, + 'variadic_token' => 16, + 'type_hint' => 'string|int', + 'type_hint_token' => 12, + 'type_hint_end_token' => 14, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -433,16 +584,25 @@ public function testPHP8UnionTypesWithSpreadOperatorAndReference() */ public function testPHP8UnionTypesSimpleWithBitwiseOrInDefault() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => 'int|float $var = CONSTANT_A | CONSTANT_B', - 'default' => 'CONSTANT_A | CONSTANT_B', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'int|float', - 'nullable_type' => false, + 'token' => 6, + 'name' => '$var', + 'content' => 'int|float $var = CONSTANT_A | CONSTANT_B', + 'default' => 'CONSTANT_A | CONSTANT_B', + 'default_token' => 10, + 'default_equal_token' => 8, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'int|float', + 'type_hint_token' => 2, + 'type_hint_end_token' => 4, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -457,15 +617,22 @@ public function testPHP8UnionTypesSimpleWithBitwiseOrInDefault() */ public function testPHP8UnionTypesTwoClasses() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => 'MyClassA|\Package\MyClassB $var', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'MyClassA|\Package\MyClassB', - 'nullable_type' => false, + 'token' => 11, + 'name' => '$var', + 'content' => 'MyClassA|\Package\MyClassB $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'MyClassA|\Package\MyClassB', + 'type_hint_token' => 4, + 'type_hint_end_token' => 9, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -480,15 +647,22 @@ public function testPHP8UnionTypesTwoClasses() */ public function testPHP8UnionTypesAllBaseTypes() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => 'array|bool|callable|int|float|null|object|string $var', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'array|bool|callable|int|float|null|object|string', - 'nullable_type' => false, + 'token' => 20, + 'name' => '$var', + 'content' => 'array|bool|callable|int|float|null|object|string $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'array|bool|callable|int|float|null|object|string', + 'type_hint_token' => 4, + 'type_hint_end_token' => 18, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -499,19 +673,28 @@ 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 = []; $expected[0] = [ - 'name' => '$var', - 'content' => 'false|mixed|self|parent|iterable|Resource $var', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'false|mixed|self|parent|iterable|Resource', - 'nullable_type' => false, + 'token' => 16, + 'name' => '$var', + 'content' => 'false|mixed|self|parent|iterable|Resource $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'false|mixed|self|parent|iterable|Resource', + 'type_hint_token' => 4, + 'type_hint_end_token' => 14, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -526,15 +709,22 @@ public function testPHP8UnionTypesAllPseudoTypes() */ public function testPHP8UnionTypesNullable() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$number', - 'content' => '?int|float $number', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => '?int|float', - 'nullable_type' => true, + 'token' => 8, + 'name' => '$number', + 'content' => '?int|float $number', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?int|float', + 'type_hint_token' => 4, + 'type_hint_end_token' => 6, + 'nullable_type' => true, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -549,16 +739,25 @@ public function testPHP8UnionTypesNullable() */ public function testPHP8PseudoTypeNull() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => 'null $var = null', - 'default' => 'null', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'null', - 'nullable_type' => false, + 'token' => 6, + 'name' => '$var', + 'content' => 'null $var = null', + 'default' => 'null', + 'default_token' => 10, + 'default_equal_token' => 8, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'null', + 'type_hint_token' => 4, + 'type_hint_end_token' => 4, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -573,16 +772,25 @@ public function testPHP8PseudoTypeNull() */ public function testPHP8PseudoTypeFalse() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => 'false $var = false', - 'default' => 'false', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'false', - 'nullable_type' => false, + 'token' => 6, + 'name' => '$var', + 'content' => 'false $var = false', + 'default' => 'false', + 'default_token' => 10, + 'default_equal_token' => 8, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'false', + 'type_hint_token' => 4, + 'type_hint_end_token' => 4, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -597,16 +805,25 @@ public function testPHP8PseudoTypeFalse() */ public function testPHP8PseudoTypeFalseAndBool() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => 'bool|false $var = false', - 'default' => 'false', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'bool|false', - 'nullable_type' => false, + 'token' => 8, + 'name' => '$var', + 'content' => 'bool|false $var = false', + 'default' => 'false', + 'default_token' => 12, + 'default_equal_token' => 10, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'bool|false', + 'type_hint_token' => 4, + 'type_hint_end_token' => 6, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -621,15 +838,22 @@ public function testPHP8PseudoTypeFalseAndBool() */ public function testPHP8ObjectAndClass() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => 'object|ClassName $var', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'object|ClassName', - 'nullable_type' => false, + 'token' => 8, + 'name' => '$var', + 'content' => 'object|ClassName $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'object|ClassName', + 'type_hint_token' => 4, + 'type_hint_end_token' => 6, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -644,15 +868,22 @@ public function testPHP8ObjectAndClass() */ public function testPHP8PseudoTypeIterableAndArray() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => 'iterable|array|Traversable $var', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'iterable|array|Traversable', - 'nullable_type' => false, + 'token' => 10, + 'name' => '$var', + 'content' => 'iterable|array|Traversable $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'iterable|array|Traversable', + 'type_hint_token' => 4, + 'type_hint_end_token' => 8, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -667,15 +898,22 @@ public function testPHP8PseudoTypeIterableAndArray() */ public function testPHP8DuplicateTypeInUnionWhitespaceAndComment() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => 'int | string /*comment*/ | INT $var', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'int|string|INT', - 'nullable_type' => false, + 'token' => 17, + 'name' => '$var', + 'content' => 'int | string /*comment*/ | INT $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'int|string|INT', + 'type_hint_token' => 5, + 'type_hint_end_token' => 15, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -690,42 +928,70 @@ public function testPHP8DuplicateTypeInUnionWhitespaceAndComment() */ public function testPHP8ConstructorPropertyPromotionNoTypes() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ + 'token' => 8, 'name' => '$x', 'content' => 'public $x = 0.0', 'default' => '0.0', + 'default_token' => 12, + 'default_equal_token' => 10, 'has_attributes' => false, 'pass_by_reference' => false, + 'reference_token' => false, 'variable_length' => false, + 'variadic_token' => false, 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, 'nullable_type' => false, 'property_visibility' => 'public', + 'visibility_token' => 6, 'property_readonly' => false, + 'comma_token' => 13, ]; $expected[1] = [ + 'token' => 18, 'name' => '$y', 'content' => 'protected $y = \'\'', 'default' => "''", + 'default_token' => 22, + 'default_equal_token' => 20, 'has_attributes' => false, 'pass_by_reference' => false, + 'reference_token' => false, 'variable_length' => false, + 'variadic_token' => false, 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, 'nullable_type' => false, 'property_visibility' => 'protected', + 'visibility_token' => 16, 'property_readonly' => false, + 'comma_token' => 23, ]; $expected[2] = [ + 'token' => 28, 'name' => '$z', 'content' => 'private $z = null', 'default' => 'null', + 'default_token' => 32, + 'default_equal_token' => 30, 'has_attributes' => false, 'pass_by_reference' => false, + 'reference_token' => false, 'variable_length' => false, + 'variadic_token' => false, 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, 'nullable_type' => false, 'property_visibility' => 'private', + 'visibility_token' => 26, 'property_readonly' => false, + 'comma_token' => 33, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -740,40 +1006,64 @@ public function testPHP8ConstructorPropertyPromotionNoTypes() */ public function testPHP8ConstructorPropertyPromotionWithTypes() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ + 'token' => 10, 'name' => '$x', 'content' => 'protected float|int $x', 'has_attributes' => false, 'pass_by_reference' => false, + 'reference_token' => false, 'variable_length' => false, + 'variadic_token' => false, 'type_hint' => 'float|int', + 'type_hint_token' => 6, + 'type_hint_end_token' => 8, 'nullable_type' => false, 'property_visibility' => 'protected', + 'visibility_token' => 4, 'property_readonly' => false, + 'comma_token' => 11, ]; $expected[1] = [ + 'token' => 19, 'name' => '$y', 'content' => 'public ?string &$y = \'test\'', 'default' => "'test'", + 'default_token' => 23, + 'default_equal_token' => 21, 'has_attributes' => false, 'pass_by_reference' => true, + 'reference_token' => 18, 'variable_length' => false, + 'variadic_token' => false, 'type_hint' => '?string', + 'type_hint_token' => 16, + 'type_hint_end_token' => 16, 'nullable_type' => true, 'property_visibility' => 'public', + 'visibility_token' => 13, 'property_readonly' => false, + 'comma_token' => 24, ]; $expected[2] = [ + 'token' => 30, 'name' => '$z', 'content' => 'private mixed $z', 'has_attributes' => false, 'pass_by_reference' => false, + 'reference_token' => false, 'variable_length' => false, + 'variadic_token' => false, 'type_hint' => 'mixed', + 'type_hint_token' => 28, + 'type_hint_end_token' => 28, 'nullable_type' => false, 'property_visibility' => 'private', + 'visibility_token' => 26, 'property_readonly' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -788,26 +1078,40 @@ public function testPHP8ConstructorPropertyPromotionWithTypes() */ public function testPHP8ConstructorPropertyPromotionAndNormalParam() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ + 'token' => 8, 'name' => '$promotedProp', 'content' => 'public int $promotedProp', 'has_attributes' => false, 'pass_by_reference' => false, + 'reference_token' => false, 'variable_length' => false, + 'variadic_token' => false, 'type_hint' => 'int', + 'type_hint_token' => 6, + 'type_hint_end_token' => 6, 'nullable_type' => false, 'property_visibility' => 'public', + 'visibility_token' => 4, 'property_readonly' => false, + 'comma_token' => 9, ]; $expected[1] = [ - 'name' => '$normalArg', - 'content' => '?int $normalArg', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => '?int', - 'nullable_type' => true, + 'token' => 14, + 'name' => '$normalArg', + 'content' => '?int $normalArg', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?int', + 'type_hint_token' => 12, + 'type_hint_end_token' => 12, + 'nullable_type' => true, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -822,28 +1126,45 @@ public function testPHP8ConstructorPropertyPromotionAndNormalParam() */ public function testPHP81ConstructorPropertyPromotionWithReadOnly() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ + 'token' => 11, 'name' => '$promotedProp', 'content' => 'public readonly ?int $promotedProp', 'has_attributes' => false, 'pass_by_reference' => false, + 'reference_token' => false, 'variable_length' => false, + 'variadic_token' => false, 'type_hint' => '?int', + 'type_hint_token' => 9, + 'type_hint_end_token' => 9, 'nullable_type' => true, 'property_visibility' => 'public', + 'visibility_token' => 4, 'property_readonly' => true, + 'readonly_token' => 6, + 'comma_token' => 12, ]; $expected[1] = [ + 'token' => 23, 'name' => '$promotedToo', 'content' => 'readonly private string|bool &$promotedToo', 'has_attributes' => false, 'pass_by_reference' => true, + 'reference_token' => 22, 'variable_length' => false, + 'variadic_token' => false, 'type_hint' => 'string|bool', + 'type_hint_token' => 18, + 'type_hint_end_token' => 20, 'nullable_type' => false, 'property_visibility' => 'private', + 'visibility_token' => 16, 'property_readonly' => true, + 'readonly_token' => 14, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -859,28 +1180,45 @@ public function testPHP81ConstructorPropertyPromotionWithReadOnly() */ public function testPHP81ConstructorPropertyPromotionWithOnlyReadOnly() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ + 'token' => 10, 'name' => '$promotedProp', 'content' => 'readonly Foo&Bar $promotedProp', 'has_attributes' => false, 'pass_by_reference' => false, + 'reference_token' => false, 'variable_length' => false, + 'variadic_token' => false, 'type_hint' => 'Foo&Bar', + 'type_hint_token' => 6, + 'type_hint_end_token' => 8, 'nullable_type' => false, 'property_visibility' => 'public', + 'visibility_token' => false, 'property_readonly' => true, + 'readonly_token' => 4, + 'comma_token' => 11, ]; $expected[1] = [ + 'token' => 18, 'name' => '$promotedToo', 'content' => 'readonly ?bool $promotedToo', 'has_attributes' => false, 'pass_by_reference' => false, + 'reference_token' => false, 'variable_length' => false, + 'variadic_token' => false, 'type_hint' => '?bool', + 'type_hint_token' => 16, + 'type_hint_end_token' => 16, 'nullable_type' => true, 'property_visibility' => 'public', + 'visibility_token' => false, 'property_readonly' => true, + 'readonly_token' => 13, + 'comma_token' => 19, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -895,17 +1233,25 @@ public function testPHP81ConstructorPropertyPromotionWithOnlyReadOnly() */ public function testPHP8ConstructorPropertyPromotionGlobalFunction() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ + 'token' => 6, 'name' => '$x', 'content' => 'private $x', 'has_attributes' => false, 'pass_by_reference' => false, + 'reference_token' => false, 'variable_length' => false, + 'variadic_token' => false, 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, 'nullable_type' => false, 'property_visibility' => 'private', + 'visibility_token' => 4, 'property_readonly' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -920,28 +1266,43 @@ public function testPHP8ConstructorPropertyPromotionGlobalFunction() */ public function testPHP8ConstructorPropertyPromotionAbstractMethod() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ + 'token' => 8, 'name' => '$y', 'content' => 'public callable $y', 'has_attributes' => false, 'pass_by_reference' => false, + 'reference_token' => false, 'variable_length' => false, + 'variadic_token' => false, 'type_hint' => 'callable', + 'type_hint_token' => 6, + 'type_hint_end_token' => 6, 'nullable_type' => false, 'property_visibility' => 'public', + 'visibility_token' => 4, 'property_readonly' => false, + 'comma_token' => 9, ]; $expected[1] = [ + 'token' => 14, 'name' => '$x', 'content' => 'private ...$x', 'has_attributes' => false, 'pass_by_reference' => false, + 'reference_token' => false, 'variable_length' => true, + 'variadic_token' => 13, 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, 'nullable_type' => false, 'property_visibility' => 'private', + 'visibility_token' => 11, 'property_readonly' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -956,17 +1317,26 @@ public function testPHP8ConstructorPropertyPromotionAbstractMethod() */ public function testCommentsInParameter() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$param', - 'content' => '// Leading comment. + 'token' => 19, + 'name' => '$param', + 'content' => '// Leading comment. ?MyClass /*-*/ & /*-*/.../*-*/ $param /*-*/ = /*-*/ \'default value\' . /*-*/ \'second part\' // Trailing comment.', - 'default' => '\'default value\' . /*-*/ \'second part\' // Trailing comment.', - 'has_attributes' => false, - 'pass_by_reference' => true, - 'variable_length' => true, - 'type_hint' => '?MyClass', - 'nullable_type' => true, + 'default' => '\'default value\' . /*-*/ \'second part\' // Trailing comment.', + 'default_token' => 27, + 'default_equal_token' => 23, + 'has_attributes' => false, + 'pass_by_reference' => true, + 'reference_token' => 13, + 'variable_length' => true, + 'variadic_token' => 16, + 'type_hint' => '?MyClass', + 'type_hint_token' => 9, + 'type_hint_end_token' => 9, + 'nullable_type' => true, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -981,56 +1351,88 @@ public function testCommentsInParameter() */ public function testParameterAttributesInFunctionDeclaration() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ + 'token' => 17, 'name' => '$constructorPropPromTypedParamSingleAttribute', 'content' => '#[\MyExample\MyAttribute] private string $constructorPropPromTypedParamSingleAttribute', 'has_attributes' => true, 'pass_by_reference' => false, + 'reference_token' => false, 'variable_length' => false, + 'variadic_token' => false, 'type_hint' => 'string', + 'type_hint_token' => 15, + 'type_hint_end_token' => 15, 'nullable_type' => false, 'property_visibility' => 'private', + 'visibility_token' => 13, 'property_readonly' => false, + 'comma_token' => 18, ]; $expected[1] = [ - 'name' => '$typedParamSingleAttribute', - 'content' => '#[MyAttr([1, 2])] + 'token' => 39, + 'name' => '$typedParamSingleAttribute', + 'content' => '#[MyAttr([1, 2])] Type|false $typedParamSingleAttribute', - 'has_attributes' => true, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'Type|false', - 'nullable_type' => false, + 'has_attributes' => true, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'Type|false', + 'type_hint_token' => 34, + 'type_hint_end_token' => 36, + 'nullable_type' => false, + 'comma_token' => 40, ]; $expected[2] = [ - 'name' => '$nullableTypedParamMultiAttribute', - 'content' => '#[MyAttribute(1234), MyAttribute(5678)] ?int $nullableTypedParamMultiAttribute', - 'has_attributes' => true, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => '?int', - 'nullable_type' => true, + 'token' => 59, + 'name' => '$nullableTypedParamMultiAttribute', + 'content' => '#[MyAttribute(1234), MyAttribute(5678)] ?int $nullableTypedParamMultiAttribute', + 'has_attributes' => true, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?int', + 'type_hint_token' => 57, + 'type_hint_end_token' => 57, + 'nullable_type' => true, + 'comma_token' => 60, ]; $expected[3] = [ - 'name' => '$nonTypedParamTwoAttributes', - 'content' => '#[WithoutArgument] #[SingleArgument(0)] $nonTypedParamTwoAttributes', - 'has_attributes' => true, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => '', - 'nullable_type' => false, + 'token' => 74, + 'name' => '$nonTypedParamTwoAttributes', + 'content' => '#[WithoutArgument] #[SingleArgument(0)] $nonTypedParamTwoAttributes', + 'has_attributes' => true, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => 75, ]; $expected[4] = [ - 'name' => '$otherParam', - 'content' => '#[MyAttribute(array("key" => "value"))] + 'token' => 95, + 'name' => '$otherParam', + 'content' => '#[MyAttribute(array("key" => "value"))] &...$otherParam', - 'has_attributes' => true, - 'pass_by_reference' => true, - 'variable_length' => true, - 'type_hint' => '', - 'nullable_type' => false, + 'has_attributes' => true, + 'pass_by_reference' => true, + 'reference_token' => 93, + 'variable_length' => true, + 'variadic_token' => 94, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => 96, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -1045,24 +1447,37 @@ public function testParameterAttributesInFunctionDeclaration() */ public function testPHP8IntersectionTypes() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$obj1', - 'content' => 'Foo&Bar $obj1', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'Foo&Bar', - 'nullable_type' => false, + 'token' => 8, + 'name' => '$obj1', + 'content' => 'Foo&Bar $obj1', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'Foo&Bar', + 'type_hint_token' => 4, + 'type_hint_end_token' => 6, + 'nullable_type' => false, + 'comma_token' => 9, ]; $expected[1] = [ - 'name' => '$obj2', - 'content' => 'Boo&Bar $obj2', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'Boo&Bar', - 'nullable_type' => false, + 'token' => 15, + 'name' => '$obj2', + 'content' => 'Boo&Bar $obj2', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'Boo&Bar', + 'type_hint_token' => 11, + 'type_hint_end_token' => 13, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -1071,30 +1486,44 @@ public function testPHP8IntersectionTypes() /** - * Verify recognition of PHP8 intersection type declaration when the variable has either a spread operator or a reference. + * Verify recognition of PHP8 intersection type declaration when the variable + * has either a spread operator or a reference. * * @return void */ public function testPHP81IntersectionTypesWithSpreadOperatorAndReference() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$paramA', - 'content' => 'Boo&Bar &$paramA', - 'has_attributes' => false, - 'pass_by_reference' => true, - 'variable_length' => false, - 'type_hint' => 'Boo&Bar', - 'nullable_type' => false, + 'token' => 9, + 'name' => '$paramA', + 'content' => 'Boo&Bar &$paramA', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'reference_token' => 8, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'Boo&Bar', + 'type_hint_token' => 4, + 'type_hint_end_token' => 6, + 'nullable_type' => false, + 'comma_token' => 10, ]; $expected[1] = [ - 'name' => '$paramB', - 'content' => 'Foo&Bar ...$paramB', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => true, - 'type_hint' => 'Foo&Bar', - 'nullable_type' => false, + 'token' => 17, + 'name' => '$paramB', + 'content' => 'Foo&Bar ...$paramB', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => true, + 'variadic_token' => 16, + 'type_hint' => 'Foo&Bar', + 'type_hint_token' => 12, + 'type_hint_end_token' => 14, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -1109,15 +1538,22 @@ public function testPHP81IntersectionTypesWithSpreadOperatorAndReference() */ public function testPHP81MoreIntersectionTypes() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => 'MyClassA&\Package\MyClassB&\Package\MyClassC $var', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'MyClassA&\Package\MyClassB&\Package\MyClassC', - 'nullable_type' => false, + 'token' => 16, + 'name' => '$var', + 'content' => 'MyClassA&\Package\MyClassB&\Package\MyClassC $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'MyClassA&\Package\MyClassB&\Package\MyClassC', + 'type_hint_token' => 4, + 'type_hint_end_token' => 14, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -1132,15 +1568,22 @@ public function testPHP81MoreIntersectionTypes() */ public function testPHP81IllegalIntersectionTypes() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$numeric_string', - 'content' => 'string&int $numeric_string', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'string&int', - 'nullable_type' => false, + 'token' => 7, + 'name' => '$numeric_string', + 'content' => 'string&int $numeric_string', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'string&int', + 'type_hint_token' => 3, + 'type_hint_end_token' => 5, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -1155,15 +1598,22 @@ public function testPHP81IllegalIntersectionTypes() */ public function testPHP81NullableIntersectionTypes() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$object', - 'content' => '?Foo&Bar $object', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => '?Foo&Bar', - 'nullable_type' => true, + 'token' => 8, + 'name' => '$object', + 'content' => '?Foo&Bar $object', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?Foo&Bar', + 'type_hint_token' => 4, + 'type_hint_end_token' => 6, + 'nullable_type' => true, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -1178,16 +1628,25 @@ public function testPHP81NullableIntersectionTypes() */ public function testPHP82PseudoTypeTrue() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => '?true $var = true', - 'default' => 'true', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => '?true', - 'nullable_type' => true, + 'token' => 7, + 'name' => '$var', + 'content' => '?true $var = true', + 'default' => 'true', + 'default_token' => 11, + 'default_equal_token' => 9, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?true', + 'type_hint_token' => 5, + 'type_hint_end_token' => 5, + 'nullable_type' => true, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -1202,16 +1661,25 @@ public function testPHP82PseudoTypeTrue() */ public function testPHP82PseudoTypeFalseAndTrue() { + // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'name' => '$var', - 'content' => 'true|false $var = true', - 'default' => 'true', - 'has_attributes' => false, - 'pass_by_reference' => false, - 'variable_length' => false, - 'type_hint' => 'true|false', - 'nullable_type' => false, + 'token' => 8, + 'name' => '$var', + 'content' => 'true|false $var = true', + 'default' => 'true', + 'default_token' => 12, + 'default_equal_token' => 10, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'true|false', + 'type_hint_token' => 4, + 'type_hint_end_token' => 6, + 'nullable_type' => false, + 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); @@ -1222,31 +1690,56 @@ public function testPHP82PseudoTypeFalseAndTrue() /** * 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 */ private function getMethodParametersTestHelper($commentString, $expected) { - $function = $this->getTargetToken($commentString, [T_FUNCTION, T_CLOSURE, T_FN]); - $found = self::$phpcsFile->getMethodParameters($function); - - // Unset those indexes which are not being tested. - foreach ($found as $i => $param) { - unset( - $found[$i]['token'], - $found[$i]['reference_token'], - $found[$i]['variadic_token'], - $found[$i]['type_hint_token'], - $found[$i]['type_hint_end_token'], - $found[$i]['comma_token'], - $found[$i]['default_token'], - $found[$i]['default_equal_token'], - $found[$i]['visibility_token'], - $found[$i]['readonly_token'] - ); - } + $target = $this->getTargetToken($commentString, [T_FUNCTION, T_CLOSURE, T_FN]); + $found = self::$phpcsFile->getMethodParameters($target); + + // Convert offsets to absolute positions in the token stream. + foreach ($expected as $key => $param) { + $expected[$key]['token'] += $target; + + if (is_int($param['reference_token']) === true) { + $expected[$key]['reference_token'] += $target; + } + + if (is_int($param['variadic_token']) === true) { + $expected[$key]['variadic_token'] += $target; + } + + if (is_int($param['type_hint_token']) === true) { + $expected[$key]['type_hint_token'] += $target; + } + + if (is_int($param['type_hint_end_token']) === true) { + $expected[$key]['type_hint_end_token'] += $target; + } + + if (is_int($param['comma_token']) === true) { + $expected[$key]['comma_token'] += $target; + } + + if (isset($param['default_token']) === true) { + $expected[$key]['default_token'] += $target; + } + + if (isset($param['default_equal_token']) === true) { + $expected[$key]['default_equal_token'] += $target; + } + + if (isset($param['visibility_token']) === true && is_int($param['visibility_token']) === true) { + $expected[$key]['visibility_token'] += $target; + } + + if (isset($param['readonly_token']) === true) { + $expected[$key]['readonly_token'] += $target; + } + }//end foreach $this->assertSame($expected, $found); From 5901a6dc4153a0ea6a251603be78d611a9036963 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Tue, 2 Jan 2024 12:04:08 +0100 Subject: [PATCH 2/2] Tests/GetMethodParametersTest: add extra tests This adds some extra tests which were already in use in PHPCSUtils. --- .../GetMethodParametersParseError1Test.inc | 4 + .../GetMethodParametersParseError1Test.php | 38 + .../GetMethodParametersParseError2Test.inc | 4 + .../GetMethodParametersParseError2Test.php | 38 + tests/Core/File/GetMethodParametersTest.inc | 149 +- tests/Core/File/GetMethodParametersTest.php | 1498 +++++++++++++++-- 6 files changed, 1617 insertions(+), 114 deletions(-) create mode 100644 tests/Core/File/GetMethodParametersParseError1Test.inc create mode 100644 tests/Core/File/GetMethodParametersParseError1Test.php create mode 100644 tests/Core/File/GetMethodParametersParseError2Test.inc create mode 100644 tests/Core/File/GetMethodParametersParseError2Test.php diff --git a/tests/Core/File/GetMethodParametersParseError1Test.inc b/tests/Core/File/GetMethodParametersParseError1Test.inc new file mode 100644 index 0000000000..465679eb36 --- /dev/null +++ b/tests/Core/File/GetMethodParametersParseError1Test.inc @@ -0,0 +1,4 @@ + + * @copyright 2019-2024 PHPCSStandards Contributors + * @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\File; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +/** + * Tests for the \PHP_CodeSniffer\Files\File::getMethodParameters method. + * + * @covers \PHP_CodeSniffer\Files\File::getMethodParameters + */ +class GetMethodParametersParseError1Test extends AbstractMethodUnitTest +{ + + + /** + * Test receiving an empty array when encountering a specific parse error. + * + * @return void + */ + public function testParseError() + { + $target = $this->getTargetToken('/* testParseError */', [T_FUNCTION, T_CLOSURE, T_FN]); + $result = self::$phpcsFile->getMethodParameters($target); + + $this->assertSame([], $result); + + }//end testParseError() + + +}//end class diff --git a/tests/Core/File/GetMethodParametersParseError2Test.inc b/tests/Core/File/GetMethodParametersParseError2Test.inc new file mode 100644 index 0000000000..667cb5ed2f --- /dev/null +++ b/tests/Core/File/GetMethodParametersParseError2Test.inc @@ -0,0 +1,4 @@ + + * @copyright 2019-2024 PHPCSStandards Contributors + * @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\File; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +/** + * Tests for the \PHP_CodeSniffer\Files\File::getMethodParameters method. + * + * @covers \PHP_CodeSniffer\Files\File::getMethodParameters + */ +class GetMethodParametersParseError2Test extends AbstractMethodUnitTest +{ + + + /** + * Test receiving an empty array when encountering a specific parse error. + * + * @return void + */ + public function testParseError() + { + $target = $this->getTargetToken('/* testParseError */', [T_FUNCTION, T_CLOSURE, T_FN]); + $result = self::$phpcsFile->getMethodParameters($target); + + $this->assertSame([], $result); + + }//end testParseError() + + +}//end class diff --git a/tests/Core/File/GetMethodParametersTest.inc b/tests/Core/File/GetMethodParametersTest.inc index 2635e092c0..f6f3cd9ebf 100644 --- a/tests/Core/File/GetMethodParametersTest.inc +++ b/tests/Core/File/GetMethodParametersTest.inc @@ -1,5 +1,29 @@ $b; +/* testArrowFunctionReturnByRef */ +fn&(?string $a) => $b; + +/* testArrayDefaultValues */ +function arrayDefaultValues($var1 = [], $var2 = array(1, 2, 3) ) {} + +/* testConstantDefaultValueSecondParam */ +function constantDefaultValueSecondParam($var1, $var2 = M_PI) {} + +/* testScalarTernaryExpressionInDefault */ +function ternayInDefault( $a = FOO ? 'bar' : 10, ? bool $b ) {} + +/* testVariadicFunction */ +function variadicFunction( int ... $a ) {} + +/* testVariadicByRefFunction */ +function variadicByRefFunction( &...$a ) {} + +/* testVariadicFunctionClassType */ +function variableLengthArgument($unit, DateInterval ...$intervals) {} + +/* testNameSpacedTypeDeclaration */ +function namespacedClassType( \Package\Sub\ClassName $a, ?Sub\AnotherClass $b ) {} + +/* testWithAllTypes */ +class testAllTypes { + function allTypes( + ?ClassName $a, + self $b, + parent $c, + object $d, + ?int $e, + string &$f, + iterable $g, + bool $h = true, + callable $i = 'is_null', + float $j = 1.1, + array ...$k + ) {} +} + +/* testArrowFunctionWithAllTypes */ +$fn = fn( + ?ClassName $a, + self $b, + parent $c, + object $d, + ?int $e, + string &$f, + iterable $g, + bool $h = true, + callable $i = 'is_null', + float $j = 1.1, + array ...$k +) => $something; + +/* testMessyDeclaration */ +function messyDeclaration( + // comment + ?\MyNS /* comment */ + \ SubCat // phpcs:ignore Standard.Cat.Sniff -- for reasons. + \ MyClass $a, + $b /* test */ = /* test */ 'default' /* test*/, + // phpcs:ignore Stnd.Cat.Sniff -- For reasons. + ? /*comment*/ + bool // phpcs:disable Stnd.Cat.Sniff -- For reasons. + & /*test*/ ... /* phpcs:ignore */ $c +) {} + /* testPHP8MixedTypeHint */ function mixedTypeHint(mixed &...$var1) {} @@ -46,7 +139,7 @@ function namespaceOperatorTypeHint(?namespace\Name $var1) {} function unionTypeSimple(int|float $number, self|parent &...$obj) {} /* testPHP8UnionTypesWithSpreadOperatorAndReference */ -function globalFunctionWithSpreadAndReference(float|null &$paramA, string|int ...$paramB) {} +function globalFunctionWithSpreadAndReference(float|null &$paramA, string|int ...$paramB ) {} /* testPHP8UnionTypesSimpleWithBitwiseOrInDefault */ $fn = fn(int|float $var = CONSTANT_A | CONSTANT_B) => $var; @@ -110,7 +203,13 @@ class ConstructorPropertyPromotionAndNormalParams { class ConstructorPropertyPromotionWithReadOnly { /* testPHP81ConstructorPropertyPromotionWithReadOnly */ - public function __construct(public readonly ?int $promotedProp, readonly private string|bool &$promotedToo) {} + public function __construct(public readonly ?int $promotedProp, ReadOnly private string|bool &$promotedToo) {} +} + +class ConstructorPropertyPromotionWithReadOnlyNoTypeDeclaration { + /* testPHP81ConstructorPropertyPromotionWithReadOnlyNoTypeDeclaration */ + // Intentional fatal error. Readonly properties MUST be typed. + public function __construct(public readonly $promotedProp, ReadOnly private &$promotedToo) {} } class ConstructorPropertyPromotionWithOnlyReadOnly { @@ -174,3 +273,49 @@ function pseudoTypeTrue(?true $var = 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 $var = true) {} + +/* testPHP81NewInInitializers */ +function newInInitializers( + TypeA $new = new TypeA(self::CONST_VALUE), + \Package\TypeB $newToo = new \Package\TypeB(10, 'string'), +) {} + +/* testFunctionCallFnPHPCS353-354 */ +$value = $obj->fn(true); + +/* testClosureNoParams */ +function() {}; + +/* testClosure */ +function( $a = 'test' ) {}; + +/* testClosureUseNoParams */ +function() use() {}; + +/* testClosureUse */ +function() use( $foo, $bar ) {}; + +/* testFunctionParamListWithTrailingComma */ +function trailingComma( + ?string $foo /*comment*/ , + $bar = 0, +) {} + +/* testClosureParamListWithTrailingComma */ +function( + $foo, + $bar, +) {}; + +/* testArrowFunctionParamListWithTrailingComma */ +$fn = fn( ?int $a , ...$b, ) => $b; + +/* testClosureUseWithTrailingComma */ +function() use( + $foo /*comment*/ , + $bar, +) {}; + +/* testArrowFunctionLiveCoding */ +// Intentional parse error. This has to be the last test in the file. +$fn = fn diff --git a/tests/Core/File/GetMethodParametersTest.php b/tests/Core/File/GetMethodParametersTest.php index b383fb7568..d0b75a94b6 100644 --- a/tests/Core/File/GetMethodParametersTest.php +++ b/tests/Core/File/GetMethodParametersTest.php @@ -22,6 +22,138 @@ class GetMethodParametersTest extends AbstractMethodUnitTest { + /** + * Test receiving an expected exception when a non function/use token is passed. + * + * @param string $commentString The comment which preceeds the test. + * @param int|string|array $targetTokenType The token type to search for after $commentString. + * + * @dataProvider dataUnexpectedTokenException + * + * @return void + */ + public function testUnexpectedTokenException($commentString, $targetTokenType) + { + $this->expectRunTimeException('$stackPtr must be of type T_FUNCTION or T_CLOSURE or T_USE or T_FN'); + + $target = $this->getTargetToken($commentString, $targetTokenType); + self::$phpcsFile->getMethodParameters($target); + + }//end testUnexpectedTokenException() + + + /** + * Data Provider. + * + * @see testUnexpectedTokenException() For the array format. + * + * @return array>> + */ + public static function dataUnexpectedTokenException() + { + return [ + 'interface' => [ + 'commentString' => '/* testNotAFunction */', + 'targetTokenType' => T_INTERFACE, + ], + '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 dataUnexpectedTokenException() + + + /** + * Test receiving an expected exception when a non-closure use token is passed. + * + * @param string $identifier The comment which preceeds the test. + * + * @dataProvider dataInvalidUse + * + * @return void + */ + public function testInvalidUse($identifier) + { + $this->expectRunTimeException('$stackPtr was not a valid T_USE'); + + $use = $this->getTargetToken($identifier, [T_USE]); + self::$phpcsFile->getMethodParameters($use); + + }//end testInvalidUse() + + + /** + * Data Provider. + * + * @see testInvalidUse() For the array format. + * + * @return array> + */ + public static function dataInvalidUse() + { + return [ + 'ImportUse' => ['/* testImportUse */'], + 'ImportGroupUse' => ['/* testImportGroupUse */'], + 'TraitUse' => ['/* testTraitUse */'], + ]; + + }//end dataInvalidUse() + + + /** + * Test receiving an empty array when there are no parameters. + * + * @param string $commentString The comment which preceeds the test. + * @param int|string|array $targetTokenType Optional. The token type to search for after $commentString. + * Defaults to the function/closure/arrow tokens. + * + * @dataProvider dataNoParams + * + * @return void + */ + public function testNoParams($commentString, $targetTokenType=[T_FUNCTION, T_CLOSURE, T_FN]) + { + $target = $this->getTargetToken($commentString, $targetTokenType); + $result = self::$phpcsFile->getMethodParameters($target); + + $this->assertSame([], $result); + + }//end testNoParams() + + + /** + * Data Provider. + * + * @see testNoParams() For the array format. + * + * @return array>> + */ + public static function dataNoParams() + { + return [ + 'FunctionNoParams' => ['/* testFunctionNoParams */'], + 'ClosureNoParams' => ['/* testClosureNoParams */'], + 'ClosureUseNoParams' => [ + '/* testClosureUseNoParams */', + T_USE, + ], + ]; + + }//end dataNoParams() + + /** * Verify pass-by-reference parsing. * @@ -398,261 +530,1035 @@ public function testArrowFunction() /** - * Verify recognition of PHP8 mixed type declaration. + * Verify that arrow functions are supported. * * @return void */ - public function testPHP8MixedTypeHint() + public function testArrowFunctionReturnByRef() { - // Offsets are relative to the T_FUNCTION token. + // Offsets are relative to the T_FN token. $expected = []; $expected[0] = [ - 'token' => 8, - 'name' => '$var1', - 'content' => 'mixed &...$var1', + 'token' => 6, + 'name' => '$a', + 'content' => '?string $a', 'has_attributes' => false, - 'pass_by_reference' => true, - 'reference_token' => 6, - 'variable_length' => true, - 'variadic_token' => 7, - 'type_hint' => 'mixed', + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?string', 'type_hint_token' => 4, 'type_hint_end_token' => 4, - 'nullable_type' => false, + 'nullable_type' => true, 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); - }//end testPHP8MixedTypeHint() + }//end testArrowFunctionReturnByRef() /** - * Verify recognition of PHP8 mixed type declaration with nullability. + * Verify default value parsing with array values. * * @return void */ - public function testPHP8MixedTypeHintNullable() + public function testArrayDefaultValues() { // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'token' => 7, + 'token' => 4, 'name' => '$var1', - 'content' => '?Mixed $var1', + 'content' => '$var1 = []', + 'default' => '[]', + 'default_token' => 8, + 'default_equal_token' => 6, 'has_attributes' => false, 'pass_by_reference' => false, 'reference_token' => false, 'variable_length' => false, 'variadic_token' => false, - 'type_hint' => '?Mixed', - 'type_hint_token' => 5, - 'type_hint_end_token' => 5, - 'nullable_type' => true, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => 10, + ]; + $expected[1] = [ + 'token' => 12, + 'name' => '$var2', + 'content' => '$var2 = array(1, 2, 3)', + 'default' => 'array(1, 2, 3)', + 'default_token' => 16, + 'default_equal_token' => 14, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); - }//end testPHP8MixedTypeHintNullable() + }//end testArrayDefaultValues() /** - * Verify recognition of type declarations using the namespace operator. + * Verify having a T_STRING constant as a default value for the second parameter. * * @return void */ - public function testNamespaceOperatorTypeHint() + public function testConstantDefaultValueSecondParam() { // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'token' => 9, + 'token' => 4, 'name' => '$var1', - 'content' => '?namespace\Name $var1', + 'content' => '$var1', 'has_attributes' => false, 'pass_by_reference' => false, 'reference_token' => false, 'variable_length' => false, 'variadic_token' => false, - 'type_hint' => '?namespace\Name', - 'type_hint_token' => 5, - 'type_hint_end_token' => 7, - 'nullable_type' => true, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => 5, + ]; + $expected[1] = [ + 'token' => 7, + 'name' => '$var2', + 'content' => '$var2 = M_PI', + 'default' => 'M_PI', + 'default_token' => 11, + 'default_equal_token' => 9, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); - }//end testNamespaceOperatorTypeHint() + }//end testConstantDefaultValueSecondParam() /** - * Verify recognition of PHP8 union type declaration. + * Verify distinquishing between a nullable type and a ternary within a default expression. * * @return void */ - public function testPHP8UnionTypesSimple() + public function testScalarTernaryExpressionInDefault() { // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'token' => 8, - 'name' => '$number', - 'content' => 'int|float $number', + 'token' => 5, + 'name' => '$a', + 'content' => '$a = FOO ? \'bar\' : 10', + 'default' => 'FOO ? \'bar\' : 10', + 'default_token' => 9, + 'default_equal_token' => 7, 'has_attributes' => false, 'pass_by_reference' => false, 'reference_token' => false, 'variable_length' => false, 'variadic_token' => false, - 'type_hint' => 'int|float', - 'type_hint_token' => 4, - 'type_hint_end_token' => 6, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, 'nullable_type' => false, - 'comma_token' => 9, + 'comma_token' => 18, ]; $expected[1] = [ - 'token' => 17, - 'name' => '$obj', - 'content' => 'self|parent &...$obj', + 'token' => 24, + 'name' => '$b', + 'content' => '? bool $b', 'has_attributes' => false, - 'pass_by_reference' => true, - 'reference_token' => 15, - 'variable_length' => true, - 'variadic_token' => 16, - 'type_hint' => 'self|parent', - 'type_hint_token' => 11, - 'type_hint_end_token' => 13, - 'nullable_type' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?bool', + 'type_hint_token' => 22, + 'type_hint_end_token' => 22, + 'nullable_type' => true, 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); - }//end testPHP8UnionTypesSimple() + }//end testScalarTernaryExpressionInDefault() /** - * Verify recognition of PHP8 union type declaration when the variable has either a spread operator or a reference. + * Verify a variadic parameter being recognized correctly. * * @return void */ - public function testPHP8UnionTypesWithSpreadOperatorAndReference() + public function testVariadicFunction() { // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ 'token' => 9, - 'name' => '$paramA', - 'content' => 'float|null &$paramA', - 'has_attributes' => false, - 'pass_by_reference' => true, - 'reference_token' => 8, - 'variable_length' => false, - 'variadic_token' => false, - 'type_hint' => 'float|null', - 'type_hint_token' => 4, - 'type_hint_end_token' => 6, - 'nullable_type' => false, - 'comma_token' => 10, - ]; - $expected[1] = [ - 'token' => 17, - 'name' => '$paramB', - 'content' => 'string|int ...$paramB', + 'name' => '$a', + 'content' => 'int ... $a', 'has_attributes' => false, 'pass_by_reference' => false, 'reference_token' => false, 'variable_length' => true, - 'variadic_token' => 16, - 'type_hint' => 'string|int', - 'type_hint_token' => 12, - 'type_hint_end_token' => 14, + 'variadic_token' => 7, + 'type_hint' => 'int', + 'type_hint_token' => 5, + 'type_hint_end_token' => 5, 'nullable_type' => false, 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); - }//end testPHP8UnionTypesWithSpreadOperatorAndReference() + }//end testVariadicFunction() /** - * Verify recognition of PHP8 union type declaration with a bitwise or in the default value. + * Verify a variadic parameter passed by reference being recognized correctly. * * @return void */ - public function testPHP8UnionTypesSimpleWithBitwiseOrInDefault() + public function testVariadicByRefFunction() { // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'token' => 6, - 'name' => '$var', - 'content' => 'int|float $var = CONSTANT_A | CONSTANT_B', - 'default' => 'CONSTANT_A | CONSTANT_B', - 'default_token' => 10, - 'default_equal_token' => 8, + 'token' => 7, + 'name' => '$a', + 'content' => '&...$a', 'has_attributes' => false, - 'pass_by_reference' => false, - 'reference_token' => false, - 'variable_length' => false, - 'variadic_token' => false, - 'type_hint' => 'int|float', - 'type_hint_token' => 2, - 'type_hint_end_token' => 4, + 'pass_by_reference' => true, + 'reference_token' => 5, + 'variable_length' => true, + 'variadic_token' => 6, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, 'nullable_type' => false, 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); - }//end testPHP8UnionTypesSimpleWithBitwiseOrInDefault() + }//end testVariadicByRefFunction() /** - * Verify recognition of PHP8 union type declaration with two classes. + * Verify handling of a variadic parameter with a class based type declaration. * * @return void */ - public function testPHP8UnionTypesTwoClasses() + public function testVariadicFunctionClassType() { // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'token' => 11, - 'name' => '$var', - 'content' => 'MyClassA|\Package\MyClassB $var', + 'token' => 4, + 'name' => '$unit', + 'content' => '$unit', 'has_attributes' => false, 'pass_by_reference' => false, 'reference_token' => false, 'variable_length' => false, 'variadic_token' => false, - 'type_hint' => 'MyClassA|\Package\MyClassB', - 'type_hint_token' => 4, - 'type_hint_end_token' => 9, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => 5, + ]; + $expected[1] = [ + 'token' => 10, + 'name' => '$intervals', + 'content' => 'DateInterval ...$intervals', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => true, + 'variadic_token' => 9, + 'type_hint' => 'DateInterval', + 'type_hint_token' => 7, + 'type_hint_end_token' => 7, 'nullable_type' => false, 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); - }//end testPHP8UnionTypesTwoClasses() + }//end testVariadicFunctionClassType() /** - * Verify recognition of PHP8 union type declaration with all base types. + * Verify distinquishing between a nullable type and a ternary within a default expression. * * @return void */ - public function testPHP8UnionTypesAllBaseTypes() + public function testNameSpacedTypeDeclaration() { // Offsets are relative to the T_FUNCTION token. $expected = []; $expected[0] = [ - 'token' => 20, - 'name' => '$var', - 'content' => 'array|bool|callable|int|float|null|object|string $var', + 'token' => 12, + 'name' => '$a', + 'content' => '\Package\Sub\ClassName $a', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '\Package\Sub\ClassName', + 'type_hint_token' => 5, + 'type_hint_end_token' => 10, + 'nullable_type' => false, + 'comma_token' => 13, + ]; + $expected[1] = [ + 'token' => 20, + 'name' => '$b', + 'content' => '?Sub\AnotherClass $b', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?Sub\AnotherClass', + 'type_hint_token' => 16, + 'type_hint_end_token' => 18, + 'nullable_type' => true, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testNameSpacedTypeDeclaration() + + + /** + * Verify correctly recognizing all type declarations supported by PHP. + * + * @return void + */ + public function testWithAllTypes() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 9, + 'name' => '$a', + 'content' => '?ClassName $a', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?ClassName', + 'type_hint_token' => 7, + 'type_hint_end_token' => 7, + 'nullable_type' => true, + 'comma_token' => 10, + ]; + $expected[1] = [ + 'token' => 15, + 'name' => '$b', + 'content' => 'self $b', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'self', + 'type_hint_token' => 13, + 'type_hint_end_token' => 13, + 'nullable_type' => false, + 'comma_token' => 16, + ]; + $expected[2] = [ + 'token' => 21, + 'name' => '$c', + 'content' => 'parent $c', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'parent', + 'type_hint_token' => 19, + 'type_hint_end_token' => 19, + 'nullable_type' => false, + 'comma_token' => 22, + ]; + $expected[3] = [ + 'token' => 27, + 'name' => '$d', + 'content' => 'object $d', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'object', + 'type_hint_token' => 25, + 'type_hint_end_token' => 25, + 'nullable_type' => false, + 'comma_token' => 28, + ]; + $expected[4] = [ + 'token' => 34, + 'name' => '$e', + 'content' => '?int $e', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?int', + 'type_hint_token' => 32, + 'type_hint_end_token' => 32, + 'nullable_type' => true, + 'comma_token' => 35, + ]; + $expected[5] = [ + 'token' => 41, + 'name' => '$f', + 'content' => 'string &$f', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'reference_token' => 40, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'string', + 'type_hint_token' => 38, + 'type_hint_end_token' => 38, + 'nullable_type' => false, + 'comma_token' => 42, + ]; + $expected[6] = [ + 'token' => 47, + 'name' => '$g', + 'content' => 'iterable $g', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'iterable', + 'type_hint_token' => 45, + 'type_hint_end_token' => 45, + 'nullable_type' => false, + 'comma_token' => 48, + ]; + $expected[7] = [ + 'token' => 53, + 'name' => '$h', + 'content' => 'bool $h = true', + 'default' => 'true', + 'default_token' => 57, + 'default_equal_token' => 55, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'bool', + 'type_hint_token' => 51, + 'type_hint_end_token' => 51, + 'nullable_type' => false, + 'comma_token' => 58, + ]; + $expected[8] = [ + 'token' => 63, + 'name' => '$i', + 'content' => 'callable $i = \'is_null\'', + 'default' => "'is_null'", + 'default_token' => 67, + 'default_equal_token' => 65, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'callable', + 'type_hint_token' => 61, + 'type_hint_end_token' => 61, + 'nullable_type' => false, + 'comma_token' => 68, + ]; + $expected[9] = [ + 'token' => 73, + 'name' => '$j', + 'content' => 'float $j = 1.1', + 'default' => '1.1', + 'default_token' => 77, + 'default_equal_token' => 75, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'float', + 'type_hint_token' => 71, + 'type_hint_end_token' => 71, + 'nullable_type' => false, + 'comma_token' => 78, + ]; + $expected[10] = [ + 'token' => 84, + 'name' => '$k', + 'content' => 'array ...$k', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => true, + 'variadic_token' => 83, + 'type_hint' => 'array', + 'type_hint_token' => 81, + 'type_hint_end_token' => 81, + 'nullable_type' => false, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testWithAllTypes() + + + /** + * Verify correctly recognizing all type declarations supported by PHP when used with an arrow function. + * + * @return void + */ + public function testArrowFunctionWithAllTypes() + { + // Offsets are relative to the T_FN token. + $expected = []; + $expected[0] = [ + 'token' => 7, + 'name' => '$a', + 'content' => '?ClassName $a', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?ClassName', + 'type_hint_token' => 5, + 'type_hint_end_token' => 5, + 'nullable_type' => true, + 'comma_token' => 8, + ]; + $expected[1] = [ + 'token' => 13, + 'name' => '$b', + 'content' => 'self $b', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'self', + 'type_hint_token' => 11, + 'type_hint_end_token' => 11, + 'nullable_type' => false, + 'comma_token' => 14, + ]; + $expected[2] = [ + 'token' => 19, + 'name' => '$c', + 'content' => 'parent $c', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'parent', + 'type_hint_token' => 17, + 'type_hint_end_token' => 17, + 'nullable_type' => false, + 'comma_token' => 20, + ]; + $expected[3] = [ + 'token' => 25, + 'name' => '$d', + 'content' => 'object $d', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'object', + 'type_hint_token' => 23, + 'type_hint_end_token' => 23, + 'nullable_type' => false, + 'comma_token' => 26, + ]; + $expected[4] = [ + 'token' => 32, + 'name' => '$e', + 'content' => '?int $e', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?int', + 'type_hint_token' => 30, + 'type_hint_end_token' => 30, + 'nullable_type' => true, + 'comma_token' => 33, + ]; + $expected[5] = [ + 'token' => 39, + 'name' => '$f', + 'content' => 'string &$f', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'reference_token' => 38, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'string', + 'type_hint_token' => 36, + 'type_hint_end_token' => 36, + 'nullable_type' => false, + 'comma_token' => 40, + ]; + $expected[6] = [ + 'token' => 45, + 'name' => '$g', + 'content' => 'iterable $g', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'iterable', + 'type_hint_token' => 43, + 'type_hint_end_token' => 43, + 'nullable_type' => false, + 'comma_token' => 46, + ]; + $expected[7] = [ + 'token' => 51, + 'name' => '$h', + 'content' => 'bool $h = true', + 'default' => 'true', + 'default_token' => 55, + 'default_equal_token' => 53, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'bool', + 'type_hint_token' => 49, + 'type_hint_end_token' => 49, + 'nullable_type' => false, + 'comma_token' => 56, + ]; + $expected[8] = [ + 'token' => 61, + 'name' => '$i', + 'content' => 'callable $i = \'is_null\'', + 'default' => "'is_null'", + 'default_token' => 65, + 'default_equal_token' => 63, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'callable', + 'type_hint_token' => 59, + 'type_hint_end_token' => 59, + 'nullable_type' => false, + 'comma_token' => 66, + ]; + $expected[9] = [ + 'token' => 71, + 'name' => '$j', + 'content' => 'float $j = 1.1', + 'default' => '1.1', + 'default_token' => 75, + 'default_equal_token' => 73, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'float', + 'type_hint_token' => 69, + 'type_hint_end_token' => 69, + 'nullable_type' => false, + 'comma_token' => 76, + ]; + $expected[10] = [ + 'token' => 82, + 'name' => '$k', + 'content' => 'array ...$k', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => true, + 'variadic_token' => 81, + 'type_hint' => 'array', + 'type_hint_token' => 79, + 'type_hint_end_token' => 79, + 'nullable_type' => false, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testArrowFunctionWithAllTypes() + + + /** + * Verify handling of a declaration interlaced with whitespace and comments. + * + * @return void + */ + public function testMessyDeclaration() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 25, + 'name' => '$a', + 'content' => '// comment + ?\MyNS /* comment */ + \ SubCat // phpcs:ignore Standard.Cat.Sniff -- for reasons. + \ MyClass $a', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?\MyNS\SubCat\MyClass', + 'type_hint_token' => 9, + 'type_hint_end_token' => 23, + 'nullable_type' => true, + 'comma_token' => 26, + ]; + $expected[1] = [ + 'token' => 29, + 'name' => '$b', + 'content' => "\$b /* test */ = /* test */ 'default' /* test*/", + 'default' => "'default' /* test*/", + 'default_token' => 37, + 'default_equal_token' => 33, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => 40, + ]; + $expected[2] = [ + 'token' => 62, + 'name' => '$c', + 'content' => '// phpcs:ignore Stnd.Cat.Sniff -- For reasons. + ? /*comment*/ + bool // phpcs:disable Stnd.Cat.Sniff -- For reasons. + & /*test*/ ... /* phpcs:ignore */ $c', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'reference_token' => 54, + 'variable_length' => true, + 'variadic_token' => 58, + 'type_hint' => '?bool', + 'type_hint_token' => 50, + 'type_hint_end_token' => 50, + 'nullable_type' => true, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testMessyDeclaration() + + + /** + * Verify recognition of PHP8 mixed type declaration. + * + * @return void + */ + public function testPHP8MixedTypeHint() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 8, + 'name' => '$var1', + 'content' => 'mixed &...$var1', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'reference_token' => 6, + 'variable_length' => true, + 'variadic_token' => 7, + 'type_hint' => 'mixed', + 'type_hint_token' => 4, + 'type_hint_end_token' => 4, + 'nullable_type' => false, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8MixedTypeHint() + + + /** + * Verify recognition of PHP8 mixed type declaration with nullability. + * + * @return void + */ + public function testPHP8MixedTypeHintNullable() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 7, + 'name' => '$var1', + 'content' => '?Mixed $var1', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?Mixed', + 'type_hint_token' => 5, + 'type_hint_end_token' => 5, + 'nullable_type' => true, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8MixedTypeHintNullable() + + + /** + * Verify recognition of type declarations using the namespace operator. + * + * @return void + */ + public function testNamespaceOperatorTypeHint() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 9, + 'name' => '$var1', + 'content' => '?namespace\Name $var1', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?namespace\Name', + 'type_hint_token' => 5, + 'type_hint_end_token' => 7, + 'nullable_type' => true, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testNamespaceOperatorTypeHint() + + + /** + * Verify recognition of PHP8 union type declaration. + * + * @return void + */ + public function testPHP8UnionTypesSimple() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 8, + 'name' => '$number', + 'content' => 'int|float $number', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'int|float', + 'type_hint_token' => 4, + 'type_hint_end_token' => 6, + 'nullable_type' => false, + 'comma_token' => 9, + ]; + $expected[1] = [ + 'token' => 17, + 'name' => '$obj', + 'content' => 'self|parent &...$obj', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'reference_token' => 15, + 'variable_length' => true, + 'variadic_token' => 16, + 'type_hint' => 'self|parent', + 'type_hint_token' => 11, + 'type_hint_end_token' => 13, + 'nullable_type' => false, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesSimple() + + + /** + * Verify recognition of PHP8 union type declaration when the variable has either a spread operator or a reference. + * + * @return void + */ + public function testPHP8UnionTypesWithSpreadOperatorAndReference() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 9, + 'name' => '$paramA', + 'content' => 'float|null &$paramA', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'reference_token' => 8, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'float|null', + 'type_hint_token' => 4, + 'type_hint_end_token' => 6, + 'nullable_type' => false, + 'comma_token' => 10, + ]; + $expected[1] = [ + 'token' => 17, + 'name' => '$paramB', + 'content' => 'string|int ...$paramB', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => true, + 'variadic_token' => 16, + 'type_hint' => 'string|int', + 'type_hint_token' => 12, + 'type_hint_end_token' => 14, + 'nullable_type' => false, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesWithSpreadOperatorAndReference() + + + /** + * Verify recognition of PHP8 union type declaration with a bitwise or in the default value. + * + * @return void + */ + public function testPHP8UnionTypesSimpleWithBitwiseOrInDefault() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 6, + 'name' => '$var', + 'content' => 'int|float $var = CONSTANT_A | CONSTANT_B', + 'default' => 'CONSTANT_A | CONSTANT_B', + 'default_token' => 10, + 'default_equal_token' => 8, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'int|float', + 'type_hint_token' => 2, + 'type_hint_end_token' => 4, + 'nullable_type' => false, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesSimpleWithBitwiseOrInDefault() + + + /** + * Verify recognition of PHP8 union type declaration with two classes. + * + * @return void + */ + public function testPHP8UnionTypesTwoClasses() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 11, + 'name' => '$var', + 'content' => 'MyClassA|\Package\MyClassB $var', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'MyClassA|\Package\MyClassB', + 'type_hint_token' => 4, + 'type_hint_end_token' => 9, + 'nullable_type' => false, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP8UnionTypesTwoClasses() + + + /** + * Verify recognition of PHP8 union type declaration with all base types. + * + * @return void + */ + public function testPHP8UnionTypesAllBaseTypes() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 20, + 'name' => '$var', + 'content' => 'array|bool|callable|int|float|null|object|string $var', 'has_attributes' => false, 'pass_by_reference' => false, 'reference_token' => false, @@ -1150,7 +2056,7 @@ public function testPHP81ConstructorPropertyPromotionWithReadOnly() $expected[1] = [ 'token' => 23, 'name' => '$promotedToo', - 'content' => 'readonly private string|bool &$promotedToo', + 'content' => 'ReadOnly private string|bool &$promotedToo', 'has_attributes' => false, 'pass_by_reference' => true, 'reference_token' => 22, @@ -1161,15 +2067,69 @@ public function testPHP81ConstructorPropertyPromotionWithReadOnly() 'type_hint_end_token' => 20, 'nullable_type' => false, 'property_visibility' => 'private', - 'visibility_token' => 16, + 'visibility_token' => 16, + 'property_readonly' => true, + 'readonly_token' => 14, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP81ConstructorPropertyPromotionWithReadOnly() + + + /** + * Verify recognition of PHP8 constructor with property promotion using PHP 8.1 readonly keyword + * without a property type. + * + * @return void + */ + public function testPHP81ConstructorPropertyPromotionWithReadOnlyNoTypeDeclaration() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 8, + 'name' => '$promotedProp', + 'content' => 'public readonly $promotedProp', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'property_visibility' => 'public', + 'visibility_token' => 4, + 'property_readonly' => true, + 'readonly_token' => 6, + 'comma_token' => 9, + ]; + $expected[1] = [ + 'token' => 16, + 'name' => '$promotedToo', + 'content' => 'ReadOnly private &$promotedToo', + 'has_attributes' => false, + 'pass_by_reference' => true, + 'reference_token' => 15, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'property_visibility' => 'private', + 'visibility_token' => 13, 'property_readonly' => true, - 'readonly_token' => 14, + 'readonly_token' => 11, 'comma_token' => false, ]; $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); - }//end testPHP81ConstructorPropertyPromotionWithReadOnly() + }//end testPHP81ConstructorPropertyPromotionWithReadOnlyNoTypeDeclaration() /** @@ -1687,17 +2647,331 @@ public function testPHP82PseudoTypeFalseAndTrue() }//end testPHP82PseudoTypeFalseAndTrue() + /** + * Verify behaviour when the default value uses the "new" keyword, as is allowed per PHP 8.1. + * + * @return void + */ + public function testPHP81NewInInitializers() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 8, + 'name' => '$new', + 'content' => 'TypeA $new = new TypeA(self::CONST_VALUE)', + 'default' => 'new TypeA(self::CONST_VALUE)', + 'default_token' => 12, + 'default_equal_token' => 10, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'TypeA', + 'type_hint_token' => 6, + 'type_hint_end_token' => 6, + 'nullable_type' => false, + 'comma_token' => 20, + ]; + $expected[1] = [ + 'token' => 28, + 'name' => '$newToo', + 'content' => '\Package\TypeB $newToo = new \Package\TypeB(10, \'string\')', + 'default' => "new \Package\TypeB(10, 'string')", + 'default_token' => 32, + 'default_equal_token' => 30, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '\Package\TypeB', + 'type_hint_token' => 23, + 'type_hint_end_token' => 26, + 'nullable_type' => false, + 'comma_token' => 44, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testPHP81NewInInitializers() + + + /** + * Verify handling of a closure. + * + * @return void + */ + public function testClosure() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 3, + 'name' => '$a', + 'content' => '$a = \'test\'', + 'default' => "'test'", + 'default_token' => 7, + 'default_equal_token' => 5, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testClosure() + + + /** + * Verify handling of a closure T_USE token correctly. + * + * @return void + */ + public function testClosureUse() + { + // Offsets are relative to the T_USE token. + $expected = []; + $expected[0] = [ + 'token' => 3, + 'name' => '$foo', + 'content' => '$foo', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => 4, + ]; + $expected[1] = [ + 'token' => 6, + 'name' => '$bar', + 'content' => '$bar', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => false, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected, [T_USE]); + + }//end testClosureUse() + + + /** + * Verify function declarations with trailing commas are handled correctly. + * + * @return void + */ + public function testFunctionParamListWithTrailingComma() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 9, + 'name' => '$foo', + 'content' => '?string $foo /*comment*/', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?string', + 'type_hint_token' => 7, + 'type_hint_end_token' => 7, + 'nullable_type' => true, + 'comma_token' => 13, + ]; + $expected[1] = [ + 'token' => 16, + 'name' => '$bar', + 'content' => '$bar = 0', + 'default' => '0', + 'default_token' => 20, + 'default_equal_token' => 18, + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => 21, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testFunctionParamListWithTrailingComma() + + + /** + * Verify closure declarations with trailing commas are handled correctly. + * + * @return void + */ + public function testClosureParamListWithTrailingComma() + { + // Offsets are relative to the T_FUNCTION token. + $expected = []; + $expected[0] = [ + 'token' => 4, + 'name' => '$foo', + 'content' => '$foo', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => 5, + ]; + $expected[1] = [ + 'token' => 8, + 'name' => '$bar', + 'content' => '$bar', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => 9, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testClosureParamListWithTrailingComma() + + + /** + * Verify arrow function declarations with trailing commas are handled correctly. + * + * @return void + */ + public function testArrowFunctionParamListWithTrailingComma() + { + // Offsets are relative to the T_FN token. + $expected = []; + $expected[0] = [ + 'token' => 6, + 'name' => '$a', + 'content' => '?int $a', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '?int', + 'type_hint_token' => 4, + 'type_hint_end_token' => 4, + 'nullable_type' => true, + 'comma_token' => 8, + ]; + $expected[1] = [ + 'token' => 11, + 'name' => '$b', + 'content' => '...$b', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => true, + 'variadic_token' => 10, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => 12, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testArrowFunctionParamListWithTrailingComma() + + + /** + * Verify closure T_USE statements with trailing commas are handled correctly. + * + * @return void + */ + public function testClosureUseWithTrailingComma() + { + // Offsets are relative to the T_USE token. + $expected = []; + $expected[0] = [ + 'token' => 4, + 'name' => '$foo', + 'content' => '$foo /*comment*/', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => 8, + ]; + $expected[1] = [ + 'token' => 11, + 'name' => '$bar', + 'content' => '$bar', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '', + 'type_hint_token' => false, + 'type_hint_end_token' => false, + 'nullable_type' => false, + 'comma_token' => 12, + ]; + + $this->getMethodParametersTestHelper('/* '.__FUNCTION__.' */', $expected, [T_USE]); + + }//end testClosureUseWithTrailingComma() + + /** * Test helper. * * @param string $commentString The comment which preceeds the test. * @param array> $expected The expected function output. + * @param int|string|array $targetType Optional. The token type to search for after $marker. + * Defaults to the function/closure/arrow tokens. * * @return void */ - private function getMethodParametersTestHelper($commentString, $expected) + private function getMethodParametersTestHelper($commentString, $expected, $targetType=[T_FUNCTION, T_CLOSURE, T_FN]) { - $target = $this->getTargetToken($commentString, [T_FUNCTION, T_CLOSURE, T_FN]); + $target = $this->getTargetToken($commentString, $targetType); $found = self::$phpcsFile->getMethodParameters($target); // Convert offsets to absolute positions in the token stream.