diff --git a/PHPCSUtils/Utils/FunctionDeclarations.php b/PHPCSUtils/Utils/FunctionDeclarations.php index e24bcf27..a10c2785 100644 --- a/PHPCSUtils/Utils/FunctionDeclarations.php +++ b/PHPCSUtils/Utils/FunctionDeclarations.php @@ -447,6 +447,12 @@ public static function getParameters(File $phpcsFile, $stackPtr) * still be a valid assumption. */ || $tokens[$i]['code'] === \T_STATIC + /* + * Disjunctive Normal Form uses a single pipe operator. + * This is not always tokenized as a union type. + * That bug is likely to be solved as part of https://github.com/PHPCSStandards/PHP_CodeSniffer/issues/387 + */ + || $tokens[$i]['code'] === \T_BITWISE_OR ) { if ($typeHintToken === false) { $typeHintToken = $i; @@ -495,6 +501,19 @@ public static function getParameters(File $phpcsFile, $stackPtr) $readonlyToken = $i; break; + case \T_OPEN_PARENTHESIS: + // Disjunctive Normal Form + if ($typeHintToken === false) { + $typeHintToken = $i; + } + + $dnfCloser = $tokens[$i]['parenthesis_closer']; + for ($j = $i; $j <= $dnfCloser; $j++) { + $typeHint .= $tokens[$j]['content']; + } + $typeHintEndToken = $i = $dnfCloser; + break; + case \T_CLOSE_PARENTHESIS: case \T_COMMA: // If it's null, then there must be no parameters for this diff --git a/Tests/BackCompat/BCFile/GetMethodParametersTest.inc b/Tests/BackCompat/BCFile/GetMethodParametersTest.inc index 17c8417e..55ff1b87 100644 --- a/Tests/BackCompat/BCFile/GetMethodParametersTest.inc +++ b/Tests/BackCompat/BCFile/GetMethodParametersTest.inc @@ -319,6 +319,15 @@ function() use( /* testInvalidUse */ function() use {}; // Intentional parse error. +/* testDisjunctiveNormalForm */ +function disjunctiveNormalForm( + A|(B&C) $one, + (A&B)|C $two, + A|(B&C)|(D&E&F)|G|null $three, + A&B $four, + (A&B) $five +) {} + /* testArrowFunctionLiveCoding */ // Intentional parse error. This has to be the last test in the file. $fn = fn diff --git a/Tests/Utils/FunctionDeclarations/GetParametersTest.php b/Tests/Utils/FunctionDeclarations/GetParametersTest.php index d5bfe22d..540f0671 100644 --- a/Tests/Utils/FunctionDeclarations/GetParametersTest.php +++ b/Tests/Utils/FunctionDeclarations/GetParametersTest.php @@ -103,6 +103,95 @@ public function testNoParams($commentString, $targetTokenType = [\T_FUNCTION, \T $this->assertSame([], $result); } + /** + * Verify Disjunctive Normal Form parameter types work as expected. + * + * @return void + */ + public function testDisjunctiveNormalForm() + { + // Offsets are relative to the T_FUNCTION token. + $expected = [ + [ + 'token' => 14, + 'name' => '$one', + 'content' => 'A|(B&C) $one', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'A|(B&C)', + 'type_hint_token' => 6, + 'type_hint_end_token' => 12, + 'nullable_type' => false, + 'comma_token' => 15, + ], + [ + 'token' => 26, + 'name' => '$two', + 'content' => '(A&B)|C $two', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '(A&B)|C', + 'type_hint_token' => 18, + 'type_hint_end_token' => 24, + 'nullable_type' => false, + 'comma_token' => 27, + ], + [ + 'token' => 50, + 'name' => '$three', + 'content' => 'A|(B&C)|(D&E&F)|G|null $three', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'A|(B&C)|(D&E&F)|G|null', + 'type_hint_token' => 30, + 'type_hint_end_token' => 48, + 'nullable_type' => false, + 'comma_token' => 51, + ], + [ + 'token' => 58, + 'name' => '$four', + 'content' => 'A&B $four', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => 'A&B', + 'type_hint_token' => 54, + 'type_hint_end_token' => 56, + 'nullable_type' => false, + 'comma_token' => 59, + ], + [ + 'token' => 68, + 'name' => '$five', + 'content' => '(A&B) $five', + 'has_attributes' => false, + 'pass_by_reference' => false, + 'reference_token' => false, + 'variable_length' => false, + 'variadic_token' => false, + 'type_hint' => '(A&B)', + 'type_hint_token' => 62, + 'type_hint_end_token' => 66, + 'nullable_type' => false, + 'comma_token' => false, + ], + ]; + + $this->getMethodParametersTestHelper('/* ' . __FUNCTION__ . ' */', $expected); + } + /** * Test helper. *