type aliases: support @phpstan-type and @psalm-type tags

This commit is contained in:
Jiří Pudil 2020-12-30 16:13:25 +01:00 committed by Ondřej Mirtes
parent 2ce4c66233
commit 2d862ef746
4 changed files with 137 additions and 0 deletions

View File

@ -238,6 +238,20 @@ class PhpDocNode implements Node
}
/**
* @return TypeAliasTagValueNode[]
*/
public function getTypeAliasTagValues(string $tagName = '@phpstan-type'): array
{
return array_column(
array_filter($this->getTagsByName($tagName), static function (PhpDocTagNode $tag): bool {
return $tag->value instanceof TypeAliasTagValueNode;
}),
'value'
);
}
public function __toString(): string
{
return "/**\n * " . implode("\n * ", $this->children) . '*/';

View File

@ -0,0 +1,28 @@
<?php declare(strict_types = 1);
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
class TypeAliasTagValueNode implements PhpDocTagValueNode
{
/** @var string */
public $alias;
/** @var TypeNode */
public $type;
public function __construct(string $alias, TypeNode $type)
{
$this->alias = $alias;
$this->type = $type;
}
public function __toString(): string
{
return trim("{$this->alias} {$this->type}");
}
}

View File

@ -189,6 +189,11 @@ class PhpDocParser
$tagValue = $this->parseExtendsTagValue('@use', $tokens);
break;
case '@phpstan-type':
case '@psalm-type':
$tagValue = $this->parseTypeAliasTagValue($tokens);
break;
default:
$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescription($tokens));
break;
@ -364,6 +369,19 @@ class PhpDocParser
throw new \PHPStan\ShouldNotHappenException();
}
private function parseTypeAliasTagValue(TokenIterator $tokens): Ast\PhpDoc\TypeAliasTagValueNode
{
$alias = $tokens->currentTokenValue();
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
// support psalm-type syntax
$tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL);
$type = $this->typeParser->parse($tokens);
return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type);
}
private function parseOptionalVariableName(TokenIterator $tokens): string
{
if ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) {

View File

@ -23,6 +23,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\TypeAliasTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\UsesTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
@ -66,6 +67,7 @@ class PhpDocParserTest extends \PHPUnit\Framework\TestCase
* @dataProvider provideMultiLinePhpDocData
* @dataProvider provideTemplateTagsData
* @dataProvider provideExtendsTagsData
* @dataProvider provideTypeAliasTagsData
* @dataProvider provideRealWorldExampleData
* @dataProvider provideDescriptionWithOrWithoutHtml
* @param string $label
@ -2858,6 +2860,81 @@ some text in the middle'
];
}
public function provideTypeAliasTagsData(): \Iterator
{
yield [
'OK',
'/** @phpstan-type TypeAlias string|int */',
new PhpDocNode([
new PhpDocTagNode(
'@phpstan-type',
new TypeAliasTagValueNode(
'TypeAlias',
new UnionTypeNode([
new IdentifierTypeNode('string'),
new IdentifierTypeNode('int'),
])
)
),
]),
];
yield [
'OK with psalm syntax',
'/** @psalm-type TypeAlias=string|int */',
new PhpDocNode([
new PhpDocTagNode(
'@psalm-type',
new TypeAliasTagValueNode(
'TypeAlias',
new UnionTypeNode([
new IdentifierTypeNode('string'),
new IdentifierTypeNode('int'),
])
)
),
]),
];
yield [
'invalid without type',
'/** @phpstan-type TypeAlias */',
new PhpDocNode([
new PhpDocTagNode(
'@phpstan-type',
new InvalidTagValueNode(
'TypeAlias',
new ParserException(
'*/',
Lexer::TOKEN_CLOSE_PHPDOC,
28,
Lexer::TOKEN_IDENTIFIER
)
)
),
]),
];
yield [
'invalid empty',
'/** @phpstan-type */',
new PhpDocNode([
new PhpDocTagNode(
'@phpstan-type',
new InvalidTagValueNode(
'',
new ParserException(
'*/',
Lexer::TOKEN_CLOSE_PHPDOC,
18,
Lexer::TOKEN_IDENTIFIER
)
)
),
]),
];
}
public function providerDebug(): \Iterator
{
$sample = '/**