Skip to content

Commit 847540a

Browse files
arnaud-lbondrejmirtes
authored andcommitted
Support @template tag
1 parent 472d316 commit 847540a

File tree

4 files changed

+171
-0
lines changed

4 files changed

+171
-0
lines changed

src/Ast/PhpDoc/PhpDocNode.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,20 @@ public function getParamTagValues(): array
7070
}
7171

7272

73+
/**
74+
* @return TemplateTagValueNode[]
75+
*/
76+
public function getTemplateTagValues(): array
77+
{
78+
return array_column(
79+
array_filter($this->getTagsByName('@template'), static function (PhpDocTagNode $tag): bool {
80+
return $tag->value instanceof TemplateTagValueNode;
81+
}),
82+
'value'
83+
);
84+
}
85+
86+
7387
/**
7488
* @return ReturnTagValueNode[]
7589
*/
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
4+
5+
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
6+
7+
class TemplateTagValueNode implements PhpDocTagValueNode
8+
{
9+
10+
/** @var string */
11+
public $name;
12+
13+
/** @var TypeNode */
14+
public $bound;
15+
16+
/** @var string (may be empty) */
17+
public $description;
18+
19+
public function __construct(string $name, TypeNode $bound, string $description)
20+
{
21+
$this->name = $name;
22+
$this->bound = $bound;
23+
$this->description = $description;
24+
}
25+
26+
27+
public function __toString(): string
28+
{
29+
return trim("{$this->name} of {$this->bound} {$this->description}");
30+
}
31+
32+
}

src/Parser/PhpDocParser.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHPStan\PhpDocParser\Parser;
44

55
use PHPStan\PhpDocParser\Ast;
6+
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
67
use PHPStan\PhpDocParser\Lexer\Lexer;
78

89
class PhpDocParser
@@ -113,6 +114,10 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph
113114
$tagValue = $this->parseMethodTagValue($tokens);
114115
break;
115116

117+
case '@template':
118+
$tagValue = $this->parseTemplateTagValue($tokens);
119+
break;
120+
116121
default:
117122
$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescription($tokens));
118123
break;
@@ -243,6 +248,22 @@ private function parseMethodTagValueParameter(TokenIterator $tokens): Ast\PhpDoc
243248
return new Ast\PhpDoc\MethodTagValueParameterNode($parameterType, $isReference, $isVariadic, $parameterName, $defaultValue);
244249
}
245250

251+
private function parseTemplateTagValue(TokenIterator $tokens): Ast\PhpDoc\TemplateTagValueNode
252+
{
253+
$name = $tokens->currentTokenValue();
254+
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
255+
256+
if ($tokens->tryConsumeTokenValue('of')) {
257+
$bound = $this->typeParser->parse($tokens);
258+
259+
} else {
260+
$bound = new IdentifierTypeNode('mixed');
261+
}
262+
263+
$description = $this->parseOptionalDescription($tokens);
264+
265+
return new Ast\PhpDoc\TemplateTagValueNode($name, $bound, $description);
266+
}
246267

247268
private function parseOptionalVariableName(TokenIterator $tokens): string
248269
{

tests/PHPStan/Parser/PhpDocParserTest.php

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
1616
use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
1717
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
18+
use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
1819
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
1920
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
2021
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
@@ -49,6 +50,7 @@ protected function setUp(): void
4950
* @dataProvider provideMethodTagsData
5051
* @dataProvider provideSingleLinePhpDocData
5152
* @dataProvider provideMultiLinePhpDocData
53+
* @dataProvider provideTemplateTagsData
5254
* @param string $label
5355
* @param string $input
5456
* @param PhpDocNode $expectedPhpDocNode
@@ -2198,4 +2200,106 @@ public function provideMultiLinePhpDocData(): array
21982200
];
21992201
}
22002202

2203+
2204+
public function provideTemplateTagsData(): \Iterator
2205+
{
2206+
yield [
2207+
'OK without bound and description',
2208+
'/** @template T */',
2209+
new PhpDocNode([
2210+
new PhpDocTagNode(
2211+
'@template',
2212+
new TemplateTagValueNode(
2213+
'T',
2214+
new IdentifierTypeNode('mixed'),
2215+
''
2216+
)
2217+
),
2218+
]),
2219+
];
2220+
2221+
yield [
2222+
'OK without bound',
2223+
'/** @template T the value type*/',
2224+
new PhpDocNode([
2225+
new PhpDocTagNode(
2226+
'@template',
2227+
new TemplateTagValueNode(
2228+
'T',
2229+
new IdentifierTypeNode('mixed'),
2230+
'the value type'
2231+
)
2232+
),
2233+
]),
2234+
];
2235+
2236+
yield [
2237+
'OK without description',
2238+
'/** @template T of DateTime */',
2239+
new PhpDocNode([
2240+
new PhpDocTagNode(
2241+
'@template',
2242+
new TemplateTagValueNode(
2243+
'T',
2244+
new IdentifierTypeNode('DateTime'),
2245+
''
2246+
)
2247+
),
2248+
]),
2249+
];
2250+
2251+
yield [
2252+
'OK with bound and description',
2253+
'/** @template T of DateTime the value type */',
2254+
new PhpDocNode([
2255+
new PhpDocTagNode(
2256+
'@template',
2257+
new TemplateTagValueNode(
2258+
'T',
2259+
new IdentifierTypeNode('DateTime'),
2260+
'the value type'
2261+
)
2262+
),
2263+
]),
2264+
];
2265+
2266+
yield [
2267+
'invalid without bound and description',
2268+
'/** @template */',
2269+
new PhpDocNode([
2270+
new PhpDocTagNode(
2271+
'@template',
2272+
new InvalidTagValueNode(
2273+
'',
2274+
new \PHPStan\PhpDocParser\Parser\ParserException(
2275+
'*/',
2276+
Lexer::TOKEN_CLOSE_PHPDOC,
2277+
14,
2278+
Lexer::TOKEN_IDENTIFIER
2279+
)
2280+
)
2281+
),
2282+
]),
2283+
];
2284+
2285+
yield [
2286+
'invalid without bound and with description',
2287+
'/** @template #desc */',
2288+
new PhpDocNode([
2289+
new PhpDocTagNode(
2290+
'@template',
2291+
new InvalidTagValueNode(
2292+
'#desc',
2293+
new \PHPStan\PhpDocParser\Parser\ParserException(
2294+
'#desc',
2295+
Lexer::TOKEN_OTHER,
2296+
14,
2297+
Lexer::TOKEN_IDENTIFIER
2298+
)
2299+
)
2300+
),
2301+
]),
2302+
];
2303+
}
2304+
22012305
}

0 commit comments

Comments
 (0)