diff --git a/src/Files/File.php b/src/Files/File.php index ab3cc290b8..4d6414251b 100644 --- a/src/Files/File.php +++ b/src/Files/File.php @@ -1757,6 +1757,20 @@ public function getMethodProperties($stackPtr) break; } + if ($this->tokens[$i]['code'] === T_USE) { + // Skip over closure use statements. + for ($j = ($i + 1); $j < $this->numTokens && isset(Tokens::$emptyTokens[$this->tokens[$j]['code']]) === true; $j++); + if ($this->tokens[$j]['code'] === T_OPEN_PARENTHESIS) { + if (isset($this->tokens[$j]['parenthesis_closer']) === false) { + // Live coding/parse error, stop parsing. + break; + } + + $i = $this->tokens[$j]['parenthesis_closer']; + continue; + } + } + if ($this->tokens[$i]['code'] === T_NULLABLE) { $nullableReturnType = true; } diff --git a/tests/Core/File/GetMethodPropertiesTest.inc b/tests/Core/File/GetMethodPropertiesTest.inc index 24d8cc692e..1bc2c4bd8d 100644 --- a/tests/Core/File/GetMethodPropertiesTest.inc +++ b/tests/Core/File/GetMethodPropertiesTest.inc @@ -190,6 +190,18 @@ $value = $obj->fn(true); /* testFunctionDeclarationNestedInTernaryPHPCS2975 */ return (!$a ? [ new class { public function b(): c {} } ] : []); +/* testClosureWithUseNoReturnType */ +$closure = function () use($a) /*comment*/ {}; + +/* testClosureWithUseNoReturnTypeIllegalUseProp */ +$closure = function () use ($this->prop){}; + +/* testClosureWithUseWithReturnType */ +$closure = function () use /*comment*/ ($a): Type {}; + +/* testClosureWithUseMultiParamWithReturnType */ +$closure = function () use ($a, &$b, $c, $d, $e, $f, $g): ?array {}; + /* testArrowFunctionLiveCoding */ // Intentional parse error. This has to be the last test in the file. $fn = fn diff --git a/tests/Core/File/GetMethodPropertiesTest.php b/tests/Core/File/GetMethodPropertiesTest.php index 85a36bb2b4..42649bc14e 100644 --- a/tests/Core/File/GetMethodPropertiesTest.php +++ b/tests/Core/File/GetMethodPropertiesTest.php @@ -1297,6 +1297,111 @@ public function testFunctionDeclarationNestedInTernaryPHPCS2975() }//end testFunctionDeclarationNestedInTernaryPHPCS2975() + /** + * Test handling of closure declarations with a use variable import without a return type declaration. + * + * @return void + */ + public function testClosureWithUseNoReturnType() + { + // Offsets are relative to the T_CLOSURE token. + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '', + 'return_type_token' => false, + 'return_type_end_token' => false, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testClosureWithUseNoReturnType() + + + /** + * Test handling of closure declarations with an illegal use variable for a property import (not allowed in PHP) + * without a return type declaration. + * + * @return void + */ + public function testClosureWithUseNoReturnTypeIllegalUseProp() + { + // Offsets are relative to the T_CLOSURE token. + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '', + 'return_type_token' => false, + 'return_type_end_token' => false, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testClosureWithUseNoReturnTypeIllegalUseProp() + + + /** + * Test handling of closure declarations with a use variable import with a return type declaration. + * + * @return void + */ + public function testClosureWithUseWithReturnType() + { + // Offsets are relative to the T_CLOSURE token. + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => 'Type', + 'return_type_token' => 14, + 'return_type_end_token' => 14, + 'nullable_return_type' => false, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testClosureWithUseWithReturnType() + + + /** + * Test handling of closure declarations with a use variable import with a return type declaration. + * + * @return void + */ + public function testClosureWithUseMultiParamWithReturnType() + { + // Offsets are relative to the T_CLOSURE token. + $expected = [ + 'scope' => 'public', + 'scope_specified' => false, + 'return_type' => '?array', + 'return_type_token' => 32, + 'return_type_end_token' => 32, + 'nullable_return_type' => true, + 'is_abstract' => false, + 'is_final' => false, + 'is_static' => false, + 'has_body' => true, + ]; + + $this->getMethodPropertiesTestHelper('/* '.__FUNCTION__.' */', $expected); + + }//end testClosureWithUseMultiParamWithReturnType() + + /** * Test helper. *