Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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
*/

‎src/Ast/PhpDoc/TemplateTagValueNode.php

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)

AltStyle によって変換されたページ (->オリジナル) /