lexer = new Lexer(); $this->phpDocParser = new PhpDocParser(new TypeParser(), new ConstExprParser()); } /** * @dataProvider provideParamTagsData * @dataProvider provideVarTagsData * @dataProvider provideReturnTagsData * @dataProvider provideThrowsTagsData * @dataProvider provideDeprecatedTagsData * @dataProvider providePropertyTagsData * @dataProvider provideMethodTagsData * @dataProvider provideSingleLinePhpDocData * @dataProvider provideMultiLinePhpDocData * @dataProvider provideTemplateTagsData * @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): void { $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(): \Iterator { 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 * * @deprecated in Drupal 8.6.0 and will be removed before Drupal 9.0.0. In * Drupal 9 there will be no way to set the status and in Drupal 8 this * ability has been removed because mb_*() functions are supplied using * Symfony\'s polyfill. */', new PhpDocNode([ new PhpDocTextNode('Sample class'), new PhpDocTextNode(''), new PhpDocTagNode( '@author', new GenericTagValueNode('Foo Baz ') ), new PhpDocTextNode(''), new PhpDocTagNode( '@deprecated', new DeprecatedTagValueNode('in Drupal 8.6.0 and will be removed before Drupal 9.0.0. In Drupal 9 there will be no way to set the status and in Drupal 8 this ability has been removed because mb_*() functions are supplied using Symfony\'s polyfill.') ), ]), ]; } public function provideMethodTagsData(): \Iterator { yield [ 'OK non-static, without return type', '/** @method foo() */', new PhpDocNode([ new PhpDocTagNode( '@method', new MethodTagValueNode( false, null, 'foo', [], '' ) ), ]), ]; yield [ 'OK non-static, with return type', '/** @method Foo foo() */', new PhpDocNode([ new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('Foo'), 'foo', [], '' ) ), ]), ]; yield [ 'OK non-static, with return static type', '/** @method static foo() */', new PhpDocNode([ new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('static'), 'foo', [], '' ) ), ]), ]; yield [ 'OK static, with return type', '/** @method static Foo foo() */', new PhpDocNode([ new PhpDocTagNode( '@method', new MethodTagValueNode( true, new IdentifierTypeNode('Foo'), 'foo', [], '' ) ), ]), ]; yield [ 'OK static, with return static type', '/** @method static static foo() */', new PhpDocNode([ new PhpDocTagNode( '@method', new MethodTagValueNode( true, new IdentifierTypeNode('static'), 'foo', [], '' ) ), ]), ]; yield [ 'OK non-static, with return type and description', '/** @method Foo foo() optional description */', new PhpDocNode([ new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('Foo'), 'foo', [], 'optional description' ) ), ]), ]; yield [ 'OK non-static, with return type and single parameter without type', '/** @method Foo foo($a) */', new PhpDocNode([ new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('Foo'), 'foo', [ new MethodTagValueParameterNode( null, false, false, '$a', null ), ], '' ) ), ]), ]; yield [ 'OK non-static, with return type and single parameter with type', '/** @method Foo foo(A $a) */', new PhpDocNode([ new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('Foo'), 'foo', [ new MethodTagValueParameterNode( new IdentifierTypeNode('A'), false, false, '$a', null ), ], '' ) ), ]), ]; yield [ 'OK non-static, with return type and single parameter with type that is passed by reference', '/** @method Foo foo(A &$a) */', new PhpDocNode([ new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('Foo'), 'foo', [ new MethodTagValueParameterNode( new IdentifierTypeNode('A'), true, false, '$a', null ), ], '' ) ), ]), ]; yield [ 'OK non-static, with return type and single variadic parameter with type', '/** @method Foo foo(A ...$a) */', new PhpDocNode([ new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('Foo'), 'foo', [ new MethodTagValueParameterNode( new IdentifierTypeNode('A'), false, true, '$a', null ), ], '' ) ), ]), ]; yield [ 'OK non-static, with return type and single variadic parameter with type that is passed by reference', '/** @method Foo foo(A &...$a) */', new PhpDocNode([ new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('Foo'), 'foo', [ new MethodTagValueParameterNode( new IdentifierTypeNode('A'), true, true, '$a', null ), ], '' ) ), ]), ]; yield [ 'OK non-static, with return type and single parameter with default value', '/** @method Foo foo($a = 123) */', new PhpDocNode([ new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('Foo'), 'foo', [ new MethodTagValueParameterNode( null, false, false, '$a', new ConstExprIntegerNode('123') ), ], '' ) ), ]), ]; yield [ 'OK non-static, with return type and single variadic parameter with type that is passed by reference and default value', '/** @method Foo foo(A &...$a = 123) */', new PhpDocNode([ new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('Foo'), 'foo', [ new MethodTagValueParameterNode( new IdentifierTypeNode('A'), true, true, '$a', new ConstExprIntegerNode('123') ), ], '' ) ), ]), ]; yield [ 'OK non-static, with return type and multiple parameters without type', '/** @method Foo foo($a, $b, $c) */', new PhpDocNode([ new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('Foo'), 'foo', [ new MethodTagValueParameterNode( null, false, false, '$a', null ), new MethodTagValueParameterNode( null, false, false, '$b', null ), new MethodTagValueParameterNode( null, false, false, '$c', null ), ], '' ) ), ]), ]; yield [ 'invalid non-static method without parentheses', '/** @method a b */', new PhpDocNode([ new PhpDocTagNode( '@method', new InvalidTagValueNode( 'a b', new \PHPStan\PhpDocParser\Parser\ParserException( '*/', Lexer::TOKEN_CLOSE_PHPDOC, 16, Lexer::TOKEN_OPEN_PARENTHESES ) ) ), ]), ]; yield [ 'invalid static method without parentheses', '/** @method static a b */', new PhpDocNode([ new PhpDocTagNode( '@method', new InvalidTagValueNode( 'static a b', new \PHPStan\PhpDocParser\Parser\ParserException( '*/', Lexer::TOKEN_CLOSE_PHPDOC, 23, Lexer::TOKEN_OPEN_PARENTHESES ) ) ), ]), ]; yield [ 'invalid non-static method without parameter name', '/** @method a b(x) */', new PhpDocNode([ new PhpDocTagNode( '@method', new InvalidTagValueNode( 'a b(x)', new \PHPStan\PhpDocParser\Parser\ParserException( ')', Lexer::TOKEN_CLOSE_PARENTHESES, 17, Lexer::TOKEN_VARIABLE ) ) ), ]), ]; } public function provideSingleLinePhpDocData(): \Iterator { yield [ 'empty', '/** */', new PhpDocNode([]), ]; yield [ 'edge-case', '/** /**/', new PhpDocNode([ new PhpDocTextNode( '/*' ), ]), ]; yield [ 'single text node', '/** text */', new PhpDocNode([ new PhpDocTextNode( 'text' ), ]), ]; yield [ 'single text node with tag in the middle', '/** text @foo bar */', new PhpDocNode([ new PhpDocTextNode( 'text @foo bar' ), ]), ]; yield [ 'single tag node without value', '/** @foo */', new PhpDocNode([ new PhpDocTagNode( '@foo', new GenericTagValueNode('') ), ]), ]; yield [ 'single tag node with value', '/** @foo lorem ipsum */', new PhpDocNode([ new PhpDocTagNode( '@foo', new GenericTagValueNode('lorem ipsum') ), ]), ]; yield [ 'single tag node with tag in the middle of value', '/** @foo lorem @bar ipsum */', new PhpDocNode([ new PhpDocTagNode( '@foo', new GenericTagValueNode('lorem @bar ipsum') ), ]), ]; yield [ 'single tag node without space between tag name and its value', '/** @varFoo $foo */', new PhpDocNode([ new PhpDocTagNode( '@varFoo', new GenericTagValueNode( '$foo' ) ), ]), ]; } public function provideMultiLinePhpDocData(): array { return [ [ 'multi-line with two tags', '/** * @param Foo $foo 1st multi world description * @param Bar $bar 2nd multi world description */', new PhpDocNode([ new PhpDocTagNode( '@param', new ParamTagValueNode( new IdentifierTypeNode('Foo'), false, '$foo', '1st multi world description' ) ), new PhpDocTagNode( '@param', new ParamTagValueNode( new IdentifierTypeNode('Bar'), false, '$bar', '2nd multi world description' ) ), ]), ], [ 'multi-line with two tags and text in the middle', '/** * @param Foo $foo 1st multi world description * some text in the middle * @param Bar $bar 2nd multi world description */', new PhpDocNode([ new PhpDocTagNode( '@param', new ParamTagValueNode( new IdentifierTypeNode('Foo'), false, '$foo', '1st multi world description some text in the middle' ) ), new PhpDocTagNode( '@param', new ParamTagValueNode( new IdentifierTypeNode('Bar'), false, '$bar', '2nd multi world description' ) ), ]), ], [ 'multi-line with two tags, text in the middle and some empty lines', '/** * * * @param Foo $foo 1st multi world description with empty lines * * * some text in the middle * * * @param Bar $bar 2nd multi world description with empty lines * * * test */', new PhpDocNode([ new PhpDocTextNode(''), new PhpDocTextNode(''), new PhpDocTagNode( '@param', new ParamTagValueNode( new IdentifierTypeNode('Foo'), false, '$foo', '1st multi world description with empty lines' ) ), new PhpDocTextNode(''), new PhpDocTextNode(''), new PhpDocTextNode('some text in the middle'), new PhpDocTextNode(''), new PhpDocTextNode(''), new PhpDocTagNode( '@param', new ParamTagValueNode( new IdentifierTypeNode('Bar'), false, '$bar', '2nd multi world description with empty lines' ) ), new PhpDocTextNode(''), new PhpDocTextNode(''), new PhpDocTextNode('test'), ]), ], [ 'multi-line with just empty lines', '/** * * */', new PhpDocNode([ new PhpDocTextNode(''), new PhpDocTextNode(''), ]), ], [ 'multi-line with tag mentioned as part of text node', '/** * Lets talk about @param * @param int $foo @param string $bar */', new PhpDocNode([ new PhpDocTextNode('Lets talk about @param'), new PhpDocTagNode( '@param', new ParamTagValueNode( new IdentifierTypeNode('int'), false, '$foo', '@param string $bar' ) ), ]), ], [ 'multi-line with a lot of @method tags', '/** * @method int getInteger(int $a, int $b) * @method void doSomething(int $a, $b) * @method self|Bar getFooOrBar() * @method methodWithNoReturnType() * @method static int getIntegerStatically(int $a, int $b) * @method static void doSomethingStatically(int $a, $b) * @method static self|Bar getFooOrBarStatically() * @method static methodWithNoReturnTypeStatically() * @method int getIntegerWithDescription(int $a, int $b) Get an integer with a description. * @method void doSomethingWithDescription(int $a, $b) Do something with a description. * @method self|Bar getFooOrBarWithDescription() Get a Foo or a Bar with a description. * @method methodWithNoReturnTypeWithDescription() Do something with a description but what, who knows! * @method static int getIntegerStaticallyWithDescription(int $a, int $b) Get an integer with a description statically. * @method static void doSomethingStaticallyWithDescription(int $a, $b) Do something with a description statically. * @method static self|Bar getFooOrBarStaticallyWithDescription() Get a Foo or a Bar with a description statically. * @method static methodWithNoReturnTypeStaticallyWithDescription() Do something with a description statically, but what, who knows! * @method static bool aStaticMethodThatHasAUniqueReturnTypeInThisClass() * @method static string aStaticMethodThatHasAUniqueReturnTypeInThisClassWithDescription() A Description. * @method int getIntegerNoParams() * @method void doSomethingNoParams() * @method self|Bar getFooOrBarNoParams() * @method methodWithNoReturnTypeNoParams() * @method static int getIntegerStaticallyNoParams() * @method static void doSomethingStaticallyNoParams() * @method static self|Bar getFooOrBarStaticallyNoParams() * @method static methodWithNoReturnTypeStaticallyNoParams() * @method int getIntegerWithDescriptionNoParams() Get an integer with a description. * @method void doSomethingWithDescriptionNoParams() Do something with a description. * @method self|Bar getFooOrBarWithDescriptionNoParams() Get a Foo or a Bar with a description. * @method static int getIntegerStaticallyWithDescriptionNoParams() Get an integer with a description statically. * @method static void doSomethingStaticallyWithDescriptionNoParams() Do something with a description statically. * @method static self|Bar getFooOrBarStaticallyWithDescriptionNoParams() Get a Foo or a Bar with a description statically. * @method static bool|string aStaticMethodThatHasAUniqueReturnTypeInThisClassNoParams() * @method static string|float aStaticMethodThatHasAUniqueReturnTypeInThisClassWithDescriptionNoParams() A Description. * @method \Aws\Result publish(array $args) * @method Image rotate(float & ... $angle = array(), $backgroundColor) * @method Foo overridenMethod() */', new PhpDocNode([ new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('int'), 'getInteger', [ new MethodTagValueParameterNode( new IdentifierTypeNode('int'), false, false, '$a', null ), new MethodTagValueParameterNode( new IdentifierTypeNode('int'), false, false, '$b', null ), ], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('void'), 'doSomething', [ new MethodTagValueParameterNode( new IdentifierTypeNode('int'), false, false, '$a', null ), new MethodTagValueParameterNode( null, false, false, '$b', null ), ], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new UnionTypeNode([ new IdentifierTypeNode('self'), new IdentifierTypeNode('Bar'), ]), 'getFooOrBar', [], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, null, 'methodWithNoReturnType', [], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new IdentifierTypeNode('int'), 'getIntegerStatically', [ new MethodTagValueParameterNode( new IdentifierTypeNode('int'), false, false, '$a', null ), new MethodTagValueParameterNode( new IdentifierTypeNode('int'), false, false, '$b', null ), ], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new IdentifierTypeNode('void'), 'doSomethingStatically', [ new MethodTagValueParameterNode( new IdentifierTypeNode('int'), false, false, '$a', null ), new MethodTagValueParameterNode( null, false, false, '$b', null ), ], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new UnionTypeNode([ new IdentifierTypeNode('self'), new IdentifierTypeNode('Bar'), ]), 'getFooOrBarStatically', [], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('static'), 'methodWithNoReturnTypeStatically', [], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('int'), 'getIntegerWithDescription', [ new MethodTagValueParameterNode( new IdentifierTypeNode('int'), false, false, '$a', null ), new MethodTagValueParameterNode( new IdentifierTypeNode('int'), false, false, '$b', null ), ], 'Get an integer with a description.' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('void'), 'doSomethingWithDescription', [ new MethodTagValueParameterNode( new IdentifierTypeNode('int'), false, false, '$a', null ), new MethodTagValueParameterNode( null, false, false, '$b', null ), ], 'Do something with a description.' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new UnionTypeNode([ new IdentifierTypeNode('self'), new IdentifierTypeNode('Bar'), ]), 'getFooOrBarWithDescription', [], 'Get a Foo or a Bar with a description.' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, null, 'methodWithNoReturnTypeWithDescription', [], 'Do something with a description but what, who knows!' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new IdentifierTypeNode('int'), 'getIntegerStaticallyWithDescription', [ new MethodTagValueParameterNode( new IdentifierTypeNode('int'), false, false, '$a', null ), new MethodTagValueParameterNode( new IdentifierTypeNode('int'), false, false, '$b', null ), ], 'Get an integer with a description statically.' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new IdentifierTypeNode('void'), 'doSomethingStaticallyWithDescription', [ new MethodTagValueParameterNode( new IdentifierTypeNode('int'), false, false, '$a', null ), new MethodTagValueParameterNode( null, false, false, '$b', null ), ], 'Do something with a description statically.' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new UnionTypeNode([ new IdentifierTypeNode('self'), new IdentifierTypeNode('Bar'), ]), 'getFooOrBarStaticallyWithDescription', [], 'Get a Foo or a Bar with a description statically.' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('static'), 'methodWithNoReturnTypeStaticallyWithDescription', [], 'Do something with a description statically, but what, who knows!' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new IdentifierTypeNode('bool'), 'aStaticMethodThatHasAUniqueReturnTypeInThisClass', [], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new IdentifierTypeNode('string'), 'aStaticMethodThatHasAUniqueReturnTypeInThisClassWithDescription', [], 'A Description.' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('int'), 'getIntegerNoParams', [], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('void'), 'doSomethingNoParams', [], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new UnionTypeNode([ new IdentifierTypeNode('self'), new IdentifierTypeNode('Bar'), ]), 'getFooOrBarNoParams', [], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, null, 'methodWithNoReturnTypeNoParams', [], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new IdentifierTypeNode('int'), 'getIntegerStaticallyNoParams', [], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new IdentifierTypeNode('void'), 'doSomethingStaticallyNoParams', [], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new UnionTypeNode([ new IdentifierTypeNode('self'), new IdentifierTypeNode('Bar'), ]), 'getFooOrBarStaticallyNoParams', [], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('static'), 'methodWithNoReturnTypeStaticallyNoParams', [], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('int'), 'getIntegerWithDescriptionNoParams', [], 'Get an integer with a description.' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('void'), 'doSomethingWithDescriptionNoParams', [], 'Do something with a description.' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new UnionTypeNode([ new IdentifierTypeNode('self'), new IdentifierTypeNode('Bar'), ]), 'getFooOrBarWithDescriptionNoParams', [], 'Get a Foo or a Bar with a description.' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new IdentifierTypeNode('int'), 'getIntegerStaticallyWithDescriptionNoParams', [], 'Get an integer with a description statically.' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new IdentifierTypeNode('void'), 'doSomethingStaticallyWithDescriptionNoParams', [], 'Do something with a description statically.' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new UnionTypeNode([ new IdentifierTypeNode('self'), new IdentifierTypeNode('Bar'), ]), 'getFooOrBarStaticallyWithDescriptionNoParams', [], 'Get a Foo or a Bar with a description statically.' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new UnionTypeNode([ new IdentifierTypeNode('bool'), new IdentifierTypeNode('string'), ]), 'aStaticMethodThatHasAUniqueReturnTypeInThisClassNoParams', [], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( true, new UnionTypeNode([ new IdentifierTypeNode('string'), new IdentifierTypeNode('float'), ]), 'aStaticMethodThatHasAUniqueReturnTypeInThisClassWithDescriptionNoParams', [], 'A Description.' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('\\Aws\\Result'), 'publish', [ new MethodTagValueParameterNode( new IdentifierTypeNode('array'), false, false, '$args', null ), ], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('Image'), 'rotate', [ new MethodTagValueParameterNode( new IdentifierTypeNode('float'), true, true, '$angle', new ConstExprArrayNode([]) ), new MethodTagValueParameterNode( null, false, false, '$backgroundColor', null ), ], '' ) ), new PhpDocTagNode( '@method', new MethodTagValueNode( false, new IdentifierTypeNode('Foo'), 'overridenMethod', [], '' ) ), ]), ], ]; } public function provideTemplateTagsData(): \Iterator { yield [ 'OK without bound and description', '/** @template T */', new PhpDocNode([ new PhpDocTagNode( '@template', new TemplateTagValueNode( 'T', new IdentifierTypeNode('mixed'), '' ) ), ]), ]; yield [ 'OK without bound', '/** @template T the value type*/', new PhpDocNode([ new PhpDocTagNode( '@template', new TemplateTagValueNode( 'T', new IdentifierTypeNode('mixed'), 'the value type' ) ), ]), ]; yield [ 'OK without description', '/** @template T of DateTime */', new PhpDocNode([ new PhpDocTagNode( '@template', new TemplateTagValueNode( 'T', new IdentifierTypeNode('DateTime'), '' ) ), ]), ]; yield [ 'OK with bound and description', '/** @template T of DateTime the value type */', new PhpDocNode([ new PhpDocTagNode( '@template', new TemplateTagValueNode( 'T', new IdentifierTypeNode('DateTime'), 'the value type' ) ), ]), ]; yield [ 'invalid without bound and description', '/** @template */', new PhpDocNode([ new PhpDocTagNode( '@template', new InvalidTagValueNode( '', new \PHPStan\PhpDocParser\Parser\ParserException( '*/', Lexer::TOKEN_CLOSE_PHPDOC, 14, Lexer::TOKEN_IDENTIFIER ) ) ), ]), ]; yield [ 'invalid without bound and with description', '/** @template #desc */', new PhpDocNode([ new PhpDocTagNode( '@template', new InvalidTagValueNode( '#desc', new \PHPStan\PhpDocParser\Parser\ParserException( '#desc', Lexer::TOKEN_OTHER, 14, Lexer::TOKEN_IDENTIFIER ) ) ), ]), ]; } }