Skip to content

Commit

Permalink
Support @template tag
Browse files Browse the repository at this point in the history
  • Loading branch information
arnaud-lb authored and ondrejmirtes committed May 21, 2019
1 parent 472d316 commit 847540a
Show file tree
Hide file tree
Showing 4 changed files with 171 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 @@ -70,6 +70,20 @@ public function getParamTagValues(): array
}


/**
* @return TemplateTagValueNode[]
*/
public function getTemplateTagValues(): array
{
return array_column(
array_filter($this->getTagsByName('@template'), static function (PhpDocTagNode $tag): bool {
return $tag->value instanceof TemplateTagValueNode;
}),
'value'
);
}


/**
* @return ReturnTagValueNode[]
*/
Expand Down
32 changes: 32 additions & 0 deletions src/Ast/PhpDoc/TemplateTagValueNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);

namespace PHPStan\PhpDocParser\Ast\PhpDoc;

use PHPStan\PhpDocParser\Ast\Type\TypeNode;

class TemplateTagValueNode implements PhpDocTagValueNode
{

/** @var string */
public $name;

/** @var TypeNode */
public $bound;

/** @var string (may be empty) */
public $description;

public function __construct(string $name, TypeNode $bound, string $description)
{
$this->name = $name;
$this->bound = $bound;
$this->description = $description;
}


public function __toString(): string
{
return trim("{$this->name} of {$this->bound} {$this->description}");
}

}
21 changes: 21 additions & 0 deletions src/Parser/PhpDocParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\PhpDocParser\Parser;

use PHPStan\PhpDocParser\Ast;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Lexer\Lexer;

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

case '@template':
$tagValue = $this->parseTemplateTagValue($tokens);
break;

default:
$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescription($tokens));
break;
Expand Down Expand Up @@ -243,6 +248,22 @@ private function parseMethodTagValueParameter(TokenIterator $tokens): Ast\PhpDoc
return new Ast\PhpDoc\MethodTagValueParameterNode($parameterType, $isReference, $isVariadic, $parameterName, $defaultValue);
}

private function parseTemplateTagValue(TokenIterator $tokens): Ast\PhpDoc\TemplateTagValueNode
{
$name = $tokens->currentTokenValue();
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);

if ($tokens->tryConsumeTokenValue('of')) {
$bound = $this->typeParser->parse($tokens);

} else {
$bound = new IdentifierTypeNode('mixed');
}

$description = $this->parseOptionalDescription($tokens);

return new Ast\PhpDoc\TemplateTagValueNode($name, $bound, $description);
}

private function parseOptionalVariableName(TokenIterator $tokens): string
{
Expand Down
104 changes: 104 additions & 0 deletions tests/PHPStan/Parser/PhpDocParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
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\VarTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
Expand Down Expand Up @@ -49,6 +50,7 @@ protected function setUp(): void
* @dataProvider provideMethodTagsData
* @dataProvider provideSingleLinePhpDocData
* @dataProvider provideMultiLinePhpDocData
* @dataProvider provideTemplateTagsData
* @param string $label
* @param string $input
* @param PhpDocNode $expectedPhpDocNode
Expand Down Expand Up @@ -2198,4 +2200,106 @@ public function provideMultiLinePhpDocData(): array
];
}


public function provideTemplateTagsData(): \Iterator
{
yield [
'OK without bound and description',
'/** @template T */',
new PhpDocNode([
new PhpDocTagNode(
'@template',
new TemplateTagValueNode(
'T',
new IdentifierTypeNode('mixed'),
''
)
),
]),
];

yield [
'OK without bound',
'/** @template T the value type*/',
new PhpDocNode([
new PhpDocTagNode(
'@template',
new TemplateTagValueNode(
'T',
new IdentifierTypeNode('mixed'),
'the value type'
)
),
]),
];

yield [
'OK without description',
'/** @template T of DateTime */',
new PhpDocNode([
new PhpDocTagNode(
'@template',
new TemplateTagValueNode(
'T',
new IdentifierTypeNode('DateTime'),
''
)
),
]),
];

yield [
'OK with bound and description',
'/** @template T of DateTime the value type */',
new PhpDocNode([
new PhpDocTagNode(
'@template',
new TemplateTagValueNode(
'T',
new IdentifierTypeNode('DateTime'),
'the value type'
)
),
]),
];

yield [
'invalid without bound and description',
'/** @template */',
new PhpDocNode([
new PhpDocTagNode(
'@template',
new InvalidTagValueNode(
'',
new \PHPStan\PhpDocParser\Parser\ParserException(
'*/',
Lexer::TOKEN_CLOSE_PHPDOC,
14,
Lexer::TOKEN_IDENTIFIER
)
)
),
]),
];

yield [
'invalid without bound and with description',
'/** @template #desc */',
new PhpDocNode([
new PhpDocTagNode(
'@template',
new InvalidTagValueNode(
'#desc',
new \PHPStan\PhpDocParser\Parser\ParserException(
'#desc',
Lexer::TOKEN_OTHER,
14,
Lexer::TOKEN_IDENTIFIER
)
)
),
]),
];
}

}

0 comments on commit 847540a

Please sign in to comment.