Skip to content

Commit

Permalink
type aliases: support @phpstan-type and @psalm-type tags
Browse files Browse the repository at this point in the history
  • Loading branch information
jiripudil authored and ondrejmirtes committed Feb 28, 2021
1 parent 2ce4c66 commit 2d862ef
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 0 deletions.
14 changes: 14 additions & 0 deletions src/Ast/PhpDoc/PhpDocNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,20 @@ public function getMethodTagValues(string $tagName = '@method'): array
}


/**
* @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) . '*/';
Expand Down
28 changes: 28 additions & 0 deletions src/Ast/PhpDoc/TypeAliasTagValueNode.php
Original file line number Diff line number Diff line change
@@ -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}");
}

}
18 changes: 18 additions & 0 deletions src/Parser/PhpDocParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\Ph
$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;
Expand Down Expand Up @@ -364,6 +369,19 @@ private function parseExtendsTagValue(string $tagName, TokenIterator $tokens): A
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)) {
Expand Down
77 changes: 77 additions & 0 deletions tests/PHPStan/Parser/PhpDocParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
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;
Expand Down Expand Up @@ -66,6 +67,7 @@ protected function setUp(): void
* @dataProvider provideMultiLinePhpDocData
* @dataProvider provideTemplateTagsData
* @dataProvider provideExtendsTagsData
* @dataProvider provideTypeAliasTagsData
* @dataProvider provideRealWorldExampleData
* @dataProvider provideDescriptionWithOrWithoutHtml
* @param string $label
Expand Down Expand Up @@ -2858,6 +2860,81 @@ public function provideExtendsTagsData(): \Iterator
];
}

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 = '/**
Expand Down

0 comments on commit 2d862ef

Please sign in to comment.