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 2d862ef

Browse files
jiripudilondrejmirtes
authored andcommitted
type aliases: support @phpstan-type and @psalm-type tags
1 parent 2ce4c66 commit 2d862ef

File tree

4 files changed

+137
-0
lines changed

4 files changed

+137
-0
lines changed

‎src/Ast/PhpDoc/PhpDocNode.php‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,20 @@ public function getMethodTagValues(string $tagName = '@method'): array
238238
}
239239

240240

241+
/**
242+
* @return TypeAliasTagValueNode[]
243+
*/
244+
public function getTypeAliasTagValues(string $tagName = '@phpstan-type'): array
245+
{
246+
return array_column(
247+
array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool {
248+
return $tag->value instanceof TypeAliasTagValueNode;
249+
}),
250+
'value'
251+
);
252+
}
253+
254+
241255
public function __toString(): string
242256
{
243257
return "/**\n * " . implode("\n * ", $this->children) . '*/';
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
4+
5+
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
6+
7+
class TypeAliasTagValueNode implements PhpDocTagValueNode
8+
{
9+
10+
/** @var string */
11+
public $alias;
12+
13+
/** @var TypeNode */
14+
public $type;
15+
16+
public function __construct(string $alias, TypeNode $type)
17+
{
18+
$this->alias = $alias;
19+
$this->type = $type;
20+
}
21+
22+
23+
public function __toString(): string
24+
{
25+
return trim("{$this->alias}{$this->type}");
26+
}
27+
28+
}

‎src/Parser/PhpDocParser.php‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,11 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph
189189
$tagValue = $this->parseExtendsTagValue('@use', $tokens);
190190
break;
191191

192+
case '@phpstan-type':
193+
case '@psalm-type':
194+
$tagValue = $this->parseTypeAliasTagValue($tokens);
195+
break;
196+
192197
default:
193198
$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescription($tokens));
194199
break;
@@ -364,6 +369,19 @@ private function parseExtendsTagValue(string $tagName, TokenIterator $tokens): A
364369
throw new \PHPStan\ShouldNotHappenException();
365370
}
366371

372+
private function parseTypeAliasTagValue(TokenIterator $tokens): Ast\PhpDoc\TypeAliasTagValueNode
373+
{
374+
$alias = $tokens->currentTokenValue();
375+
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
376+
377+
// support psalm-type syntax
378+
$tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL);
379+
380+
$type = $this->typeParser->parse($tokens);
381+
382+
return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type);
383+
}
384+
367385
private function parseOptionalVariableName(TokenIterator $tokens): string
368386
{
369387
if ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) {

‎tests/PHPStan/Parser/PhpDocParserTest.php‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
2424
use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
2525
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
26+
use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasTagValueNode;
2627
use PHPStan\PhpDocParser\Ast\PhpDoc\UsesTagValueNode;
2728
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
2829
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
@@ -66,6 +67,7 @@ protected function setUp(): void
6667
* @dataProvider provideMultiLinePhpDocData
6768
* @dataProvider provideTemplateTagsData
6869
* @dataProvider provideExtendsTagsData
70+
* @dataProvider provideTypeAliasTagsData
6971
* @dataProvider provideRealWorldExampleData
7072
* @dataProvider provideDescriptionWithOrWithoutHtml
7173
* @param string $label
@@ -2858,6 +2860,81 @@ public function provideExtendsTagsData(): \Iterator
28582860
];
28592861
}
28602862

2863+
public function provideTypeAliasTagsData(): \Iterator
2864+
{
2865+
yield [
2866+
'OK',
2867+
'/** @phpstan-type TypeAlias string|int */',
2868+
new PhpDocNode([
2869+
new PhpDocTagNode(
2870+
'@phpstan-type',
2871+
new TypeAliasTagValueNode(
2872+
'TypeAlias',
2873+
new UnionTypeNode([
2874+
new IdentifierTypeNode('string'),
2875+
new IdentifierTypeNode('int'),
2876+
])
2877+
)
2878+
),
2879+
]),
2880+
];
2881+
2882+
yield [
2883+
'OK with psalm syntax',
2884+
'/** @psalm-type TypeAlias=string|int */',
2885+
new PhpDocNode([
2886+
new PhpDocTagNode(
2887+
'@psalm-type',
2888+
new TypeAliasTagValueNode(
2889+
'TypeAlias',
2890+
new UnionTypeNode([
2891+
new IdentifierTypeNode('string'),
2892+
new IdentifierTypeNode('int'),
2893+
])
2894+
)
2895+
),
2896+
]),
2897+
];
2898+
2899+
yield [
2900+
'invalid without type',
2901+
'/** @phpstan-type TypeAlias */',
2902+
new PhpDocNode([
2903+
new PhpDocTagNode(
2904+
'@phpstan-type',
2905+
new InvalidTagValueNode(
2906+
'TypeAlias',
2907+
new ParserException(
2908+
'*/',
2909+
Lexer::TOKEN_CLOSE_PHPDOC,
2910+
28,
2911+
Lexer::TOKEN_IDENTIFIER
2912+
)
2913+
)
2914+
),
2915+
]),
2916+
];
2917+
2918+
yield [
2919+
'invalid empty',
2920+
'/** @phpstan-type */',
2921+
new PhpDocNode([
2922+
new PhpDocTagNode(
2923+
'@phpstan-type',
2924+
new InvalidTagValueNode(
2925+
'',
2926+
new ParserException(
2927+
'*/',
2928+
Lexer::TOKEN_CLOSE_PHPDOC,
2929+
18,
2930+
Lexer::TOKEN_IDENTIFIER
2931+
)
2932+
)
2933+
),
2934+
]),
2935+
];
2936+
}
2937+
28612938
public function providerDebug(): \Iterator
28622939
{
28632940
$sample = '/**

0 commit comments

Comments
(0)

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