diff --git a/src/Parser/PhpDocParser.php b/src/Parser/PhpDocParser.php index fd034d7a..d0286d55 100644 --- a/src/Parser/PhpDocParser.php +++ b/src/Parser/PhpDocParser.php @@ -474,6 +474,10 @@ private function parseOptionalDescription(TokenIterator $tokens, bool $limitStar $tokens->consumeTokenType(Lexer::TOKEN_OTHER); // will throw exception } + + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL) && !$tokens->isPrecededByHorizontalWhitespace()) { + $tokens->consumeTokenType(Lexer::TOKEN_HORIZONTAL_WS); // will throw exception + } } return $this->parseText($tokens)->text; diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php index 20451b7c..31bb0247 100644 --- a/tests/PHPStan/Parser/PhpDocParserTest.php +++ b/tests/PHPStan/Parser/PhpDocParserTest.php @@ -1310,6 +1310,58 @@ public function provideReturnTagsData(): Iterator ), ]), ]; + + yield [ + 'OK variadic callable', + '/** @return \Closure(int ...$u, string): string */', + new PhpDocNode([ + new PhpDocTagNode( + '@return', + new ReturnTagValueNode( + new CallableTypeNode( + new IdentifierTypeNode('\Closure'), + [ + new CallableTypeParameterNode( + new IdentifierTypeNode('int'), + false, + true, + '$u', + false + ), + new CallableTypeParameterNode( + new IdentifierTypeNode('string'), + false, + false, + '', + false + ), + ], + new IdentifierTypeNode('string') + ), + '' + ) + ), + ]), + ]; + + yield [ + 'invalid variadic callable', + '/** @return \Closure(...int, string): string */', + new PhpDocNode([ + new PhpDocTagNode( + '@return', + new InvalidTagValueNode( + '\Closure(...int, string): string', + new ParserException( + '(', + Lexer::TOKEN_OPEN_PARENTHESES, + 20, + Lexer::TOKEN_HORIZONTAL_WS + ) + ) + ), + ]), + ]; } @@ -2104,10 +2156,14 @@ public function provideSingleLinePhpDocData(): Iterator new PhpDocNode([ new PhpDocTagNode( '@var', - new VarTagValueNode( - new IdentifierTypeNode('callable'), - '', - '(int)' + new InvalidTagValueNode( + 'callable(int)', + new ParserException( + '(', + Lexer::TOKEN_OPEN_PARENTHESES, + 17, + Lexer::TOKEN_HORIZONTAL_WS + ) ) ), ]), @@ -4076,14 +4132,14 @@ public function provideDescriptionWithOrWithoutHtml(): Iterator new PhpDocNode([ new PhpDocTagNode( '@return', - new ReturnTagValueNode( - new GenericTypeNode( - new IdentifierTypeNode('Foo'), - [ - new IdentifierTypeNode('strong'), - ] - ), - 'Important description' + new InvalidTagValueNode( + 'Foo Important description', + new ParserException( + 'Important', + Lexer::TOKEN_IDENTIFIER, + 27, + Lexer::TOKEN_HORIZONTAL_WS + ) ) ), ]),