From c08946968bda74869e696982b0af40a9a8784c84 Mon Sep 17 00:00:00 2001 From: Richard van Velzen Date: Mon, 28 Mar 2022 09:06:49 +0200 Subject: [PATCH] Support multiline callable syntax Closes #46. Tests are basically copied and modified from #44 --- src/Parser/TypeParser.php | 7 ++ tests/PHPStan/Parser/PhpDocParserTest.php | 83 ++++++++++++++++++++ tests/PHPStan/Parser/TypeParserTest.php | 95 +++++++++++++++++++++++ 3 files changed, 185 insertions(+) diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php index dcbc71d..8b7f1ea 100644 --- a/src/Parser/TypeParser.php +++ b/src/Parser/TypeParser.php @@ -240,12 +240,19 @@ class TypeParser private function parseCallable(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $identifier): Ast\Type\TypeNode { $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); $parameters = []; if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { $parameters[] = $this->parseCallableParameter($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) { + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PARENTHESES)) { + break; + } $parameters[] = $this->parseCallableParameter($tokens); + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); } } diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php index 16af55c..a01beae 100644 --- a/tests/PHPStan/Parser/PhpDocParserTest.php +++ b/tests/PHPStan/Parser/PhpDocParserTest.php @@ -3492,6 +3492,89 @@ Finder::findFiles('*.php') ), ]), ]; + + yield [ + 'multiline callable types', + '/**' . PHP_EOL . + ' * @param callable(' . PHP_EOL . + ' * A, B' . PHP_EOL . + ' * ): void $foo' . PHP_EOL . + ' */', + new PhpDocNode([ + new PhpDocTagNode( + '@param', + new ParamTagValueNode( + new CallableTypeNode( + new IdentifierTypeNode('callable'), + [ + new CallableTypeParameterNode(new IdentifierTypeNode('A'), false, false, '', false), + new CallableTypeParameterNode(new IdentifierTypeNode('B'), false, false, '', false), + ], + new IdentifierTypeNode('void') + ), + false, + '$foo', + '' + ) + ), + ]), + ]; + + yield [ + 'multiline callable types - leading comma', + '/**' . PHP_EOL . + ' * @param callable(' . PHP_EOL . + ' * A' . PHP_EOL . + ' * , B' . PHP_EOL . + ' * ): void $foo' . PHP_EOL . + ' */', + new PhpDocNode([ + new PhpDocTagNode( + '@param', + new ParamTagValueNode( + new CallableTypeNode( + new IdentifierTypeNode('callable'), + [ + new CallableTypeParameterNode(new IdentifierTypeNode('A'), false, false, '', false), + new CallableTypeParameterNode(new IdentifierTypeNode('B'), false, false, '', false), + ], + new IdentifierTypeNode('void') + ), + false, + '$foo', + '' + ) + ), + ]), + ]; + + yield [ + 'multiline callable types - traling comma', + '/**' . PHP_EOL . + ' * @param callable(' . PHP_EOL . + ' * A,' . PHP_EOL . + ' * B,' . PHP_EOL . + ' * ): void $foo' . PHP_EOL . + ' */', + new PhpDocNode([ + new PhpDocTagNode( + '@param', + new ParamTagValueNode( + new CallableTypeNode( + new IdentifierTypeNode('callable'), + [ + new CallableTypeParameterNode(new IdentifierTypeNode('A'), false, false, '', false), + new CallableTypeParameterNode(new IdentifierTypeNode('B'), false, false, '', false), + ], + new IdentifierTypeNode('void') + ), + false, + '$foo', + '' + ) + ), + ]), + ]; } public function provideDescriptionWithOrWithoutHtml(): Iterator diff --git a/tests/PHPStan/Parser/TypeParserTest.php b/tests/PHPStan/Parser/TypeParserTest.php index 8eb0f76..1ce4077 100644 --- a/tests/PHPStan/Parser/TypeParserTest.php +++ b/tests/PHPStan/Parser/TypeParserTest.php @@ -1004,6 +1004,101 @@ class TypeParserTest extends TestCase 'int|array{}', new UnionTypeNode([new IdentifierTypeNode('int'), new ArrayShapeNode([])]), ], + [ + 'callable(' . PHP_EOL . + ' Foo' . PHP_EOL . + '): void', + new CallableTypeNode( + new IdentifierTypeNode('callable'), + [ + new CallableTypeParameterNode(new IdentifierTypeNode('Foo'), false, false, '', false), + ], + new IdentifierTypeNode('void') + ), + ], + [ + 'callable(' . PHP_EOL . + ' Foo,' . PHP_EOL . + ' Bar' . PHP_EOL . + '): void', + new CallableTypeNode( + new IdentifierTypeNode('callable'), + [ + new CallableTypeParameterNode(new IdentifierTypeNode('Foo'), false, false, '', false), + new CallableTypeParameterNode(new IdentifierTypeNode('Bar'), false, false, '', false), + ], + new IdentifierTypeNode('void') + ), + ], + [ + 'callable(' . PHP_EOL . + ' Foo, Bar' . PHP_EOL . + '): void', + new CallableTypeNode( + new IdentifierTypeNode('callable'), + [ + new CallableTypeParameterNode(new IdentifierTypeNode('Foo'), false, false, '', false), + new CallableTypeParameterNode(new IdentifierTypeNode('Bar'), false, false, '', false), + ], + new IdentifierTypeNode('void') + ), + ], + [ + 'callable(' . PHP_EOL . + ' Foo,' . PHP_EOL . + ' callable(' . PHP_EOL . + ' Bar' . PHP_EOL . + ' ): void' . PHP_EOL . + '): void', + new CallableTypeNode( + new IdentifierTypeNode('callable'), + [ + new CallableTypeParameterNode(new IdentifierTypeNode('Foo'), false, false, '', false), + new CallableTypeParameterNode( + new CallableTypeNode( + new IdentifierTypeNode('callable'), + [ + new CallableTypeParameterNode(new IdentifierTypeNode('Bar'), false, false, '', false), + ], + new IdentifierTypeNode('void') + ), + false, + false, + '', + false + ), + ], + new IdentifierTypeNode('void') + ), + ], + [ + 'callable(' . PHP_EOL . + ' Foo,' . PHP_EOL . + ' callable(' . PHP_EOL . + ' Bar,' . PHP_EOL . + ' ): void' . PHP_EOL . + '): void', + new CallableTypeNode( + new IdentifierTypeNode('callable'), + [ + new CallableTypeParameterNode(new IdentifierTypeNode('Foo'), false, false, '', false), + new CallableTypeParameterNode( + new CallableTypeNode( + new IdentifierTypeNode('callable'), + [ + new CallableTypeParameterNode(new IdentifierTypeNode('Bar'), false, false, '', false), + ], + new IdentifierTypeNode('void') + ), + false, + false, + '', + false + ), + ], + new IdentifierTypeNode('void') + ), + ], ]; }