lexer = new Lexer(); $this->phpDocParser = new PhpDocParser(new TypeParser(), new ConstExprParser()); } /** * @dataProvider provideParamTagsData * @dataProvider provideVarTagsData * @dataProvider provideReturnTagsData * @dataProvider provideThrowsTagsData * @dataProvider providePropertyTagsData * @dataProvider provideSingleLinePhpDocData * @dataProvider provideMultiLinePhpDocData * @param string $label * @param string $input * @param PhpDocNode $expectedPhpDocNode * @param int $nextTokenType */ public function testParse(string $label, string $input, PhpDocNode $expectedPhpDocNode, int $nextTokenType = Lexer::TOKEN_END) { $tokens = new TokenIterator($this->lexer->tokenize($input)); $actualPhpDocNode = $this->phpDocParser->parse($tokens); $this->assertEquals($expectedPhpDocNode, $actualPhpDocNode, $label); $this->assertSame((string) $expectedPhpDocNode, (string) $actualPhpDocNode); $this->assertSame($nextTokenType, $tokens->currentTokenType()); } public function provideParamTagsData(): iterable { yield [ 'OK without description', '/** @param Foo $foo */', new PhpDocNode([ new PhpDocTagNode( '@param', new ParamTagValueNode( new IdentifierTypeNode('Foo'), false, '$foo', '' ) ), ]), ]; yield [ 'OK with description', '/** @param Foo $foo optional description */', new PhpDocNode([ new PhpDocTagNode( '@param', new ParamTagValueNode( new IdentifierTypeNode('Foo'), false, '$foo', 'optional description' ) ), ]), ]; yield [ 'OK variadic without description', '/** @param Foo ...$foo */', new PhpDocNode([ new PhpDocTagNode( '@param', new ParamTagValueNode( new IdentifierTypeNode('Foo'), true, '$foo', '' ) ), ]), ]; yield [ 'OK variadic with description', '/** @param Foo ...$foo optional description */', new PhpDocNode([ new PhpDocTagNode( '@param', new ParamTagValueNode( new IdentifierTypeNode('Foo'), true, '$foo', 'optional description' ) ), ]), ]; yield [ 'invalid without type, parameter name and description', '/** @param */', new PhpDocNode([ new PhpDocTagNode( '@param', new InvalidTagValueNode( '', new \PHPStan\PhpDocParser\Parser\ParserException( '*/', Lexer::TOKEN_CLOSE_PHPDOC, 11, Lexer::TOKEN_IDENTIFIER ) ) ), ]), ]; yield [ 'invalid without type and parameter name and with description (1)', '/** @param #desc */', new PhpDocNode([ new PhpDocTagNode( '@param', new InvalidTagValueNode( '#desc', new \PHPStan\PhpDocParser\Parser\ParserException( '#desc', Lexer::TOKEN_OTHER, 11, Lexer::TOKEN_IDENTIFIER ) ) ), ]), ]; yield [ 'invalid without type and parameter name and with description (2)', '/** @param (Foo */', new PhpDocNode([ new PhpDocTagNode( '@param', new InvalidTagValueNode( '(Foo', new \PHPStan\PhpDocParser\Parser\ParserException( '*/', Lexer::TOKEN_CLOSE_PHPDOC, 16, Lexer::TOKEN_CLOSE_PARENTHESES ) ) ), ]), ]; yield [ 'invalid with broken type (1)', '/** @param (Foo $foo */', new PhpDocNode([ new PhpDocTagNode( '@param', new InvalidTagValueNode( '(Foo $foo', new \PHPStan\PhpDocParser\Parser\ParserException( '$foo', Lexer::TOKEN_VARIABLE, 16, Lexer::TOKEN_CLOSE_PARENTHESES ) ) ), ]), ]; yield [ 'invalid with broken type (2)', '/** @param Foo