mirror of
https://github.com/danog/phpdoc-parser.git
synced 2024-11-26 20:15:11 +01:00
Support @extends, @implements, @uses
This commit is contained in:
parent
8c4ef2aefd
commit
14346cb8b0
28
src/Ast/PhpDoc/ExtendsTagValueNode.php
Normal file
28
src/Ast/PhpDoc/ExtendsTagValueNode.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
|
||||
|
||||
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
|
||||
|
||||
class ExtendsTagValueNode implements PhpDocTagValueNode
|
||||
{
|
||||
|
||||
/** @var GenericTypeNode */
|
||||
public $type;
|
||||
|
||||
/** @var string (may be empty) */
|
||||
public $description;
|
||||
|
||||
public function __construct(GenericTypeNode $type, string $description)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return trim("{$this->type} {$this->description}");
|
||||
}
|
||||
|
||||
}
|
28
src/Ast/PhpDoc/ImplementsTagValueNode.php
Normal file
28
src/Ast/PhpDoc/ImplementsTagValueNode.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
|
||||
|
||||
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
|
||||
|
||||
class ImplementsTagValueNode implements PhpDocTagValueNode
|
||||
{
|
||||
|
||||
/** @var GenericTypeNode */
|
||||
public $type;
|
||||
|
||||
/** @var string (may be empty) */
|
||||
public $description;
|
||||
|
||||
public function __construct(GenericTypeNode $type, string $description)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return trim("{$this->type} {$this->description}");
|
||||
}
|
||||
|
||||
}
|
@ -84,6 +84,48 @@ class PhpDocNode implements Node
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return ExtendsTagValueNode[]
|
||||
*/
|
||||
public function getExtendsTagValues(): array
|
||||
{
|
||||
return array_column(
|
||||
array_filter($this->getTagsByName('@extends'), static function (PhpDocTagNode $tag): bool {
|
||||
return $tag->value instanceof ExtendsTagValueNode;
|
||||
}),
|
||||
'value'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return ImplementsTagValueNode[]
|
||||
*/
|
||||
public function getImplementsTagValues(): array
|
||||
{
|
||||
return array_column(
|
||||
array_filter($this->getTagsByName('@implements'), static function (PhpDocTagNode $tag): bool {
|
||||
return $tag->value instanceof ImplementsTagValueNode;
|
||||
}),
|
||||
'value'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return UsesTagValueNode[]
|
||||
*/
|
||||
public function getUsesTagValues(): array
|
||||
{
|
||||
return array_column(
|
||||
array_filter($this->getTagsByName('@uses'), static function (PhpDocTagNode $tag): bool {
|
||||
return $tag->value instanceof UsesTagValueNode;
|
||||
}),
|
||||
'value'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return ReturnTagValueNode[]
|
||||
*/
|
||||
|
28
src/Ast/PhpDoc/UsesTagValueNode.php
Normal file
28
src/Ast/PhpDoc/UsesTagValueNode.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace PHPStan\PhpDocParser\Ast\PhpDoc;
|
||||
|
||||
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
|
||||
|
||||
class UsesTagValueNode implements PhpDocTagValueNode
|
||||
{
|
||||
|
||||
/** @var GenericTypeNode */
|
||||
public $type;
|
||||
|
||||
/** @var string (may be empty) */
|
||||
public $description;
|
||||
|
||||
public function __construct(GenericTypeNode $type, string $description)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return trim("{$this->type} {$this->description}");
|
||||
}
|
||||
|
||||
}
|
@ -145,6 +145,12 @@ class PhpDocParser
|
||||
$tagValue = $this->parseTemplateTagValue($tokens);
|
||||
break;
|
||||
|
||||
case '@extends':
|
||||
case '@implements':
|
||||
case '@uses':
|
||||
$tagValue = $this->parseExtendsTagValue($tag, $tokens);
|
||||
break;
|
||||
|
||||
default:
|
||||
$tagValue = new Ast\PhpDoc\GenericTagValueNode($this->parseOptionalDescription($tokens));
|
||||
break;
|
||||
@ -292,6 +298,25 @@ class PhpDocParser
|
||||
return new Ast\PhpDoc\TemplateTagValueNode($name, $bound, $description);
|
||||
}
|
||||
|
||||
private function parseExtendsTagValue(string $tagName, TokenIterator $tokens): Ast\PhpDoc\PhpDocTagValueNode
|
||||
{
|
||||
$baseType = new IdentifierTypeNode($tokens->currentTokenValue());
|
||||
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
|
||||
|
||||
$type = $this->typeParser->parseGeneric($tokens, $baseType);
|
||||
|
||||
$description = $this->parseOptionalDescription($tokens);
|
||||
|
||||
switch ($tagName) {
|
||||
case '@extends':
|
||||
return new Ast\PhpDoc\ExtendsTagValueNode($type, $description);
|
||||
case '@implements':
|
||||
return new Ast\PhpDoc\ImplementsTagValueNode($type, $description);
|
||||
case '@uses':
|
||||
return new Ast\PhpDoc\UsesTagValueNode($type, $description);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseOptionalVariableName(TokenIterator $tokens): string
|
||||
{
|
||||
if ($tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE)) {
|
||||
|
@ -105,7 +105,7 @@ class TypeParser
|
||||
}
|
||||
|
||||
|
||||
private function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $baseType): Ast\Type\TypeNode
|
||||
public function parseGeneric(TokenIterator $tokens, Ast\Type\IdentifierTypeNode $baseType): Ast\Type\GenericTypeNode
|
||||
{
|
||||
$tokens->consumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET);
|
||||
$genericTypes[] = $this->parse($tokens);
|
||||
|
@ -5,7 +5,9 @@ namespace PHPStan\PhpDocParser\Parser;
|
||||
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayNode;
|
||||
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\DeprecatedTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ExtendsTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ImplementsTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode;
|
||||
@ -17,8 +19,10 @@ 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\UsesTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
|
||||
use PHPStan\PhpDocParser\Lexer\Lexer;
|
||||
@ -51,6 +55,7 @@ class PhpDocParserTest extends \PHPUnit\Framework\TestCase
|
||||
* @dataProvider provideSingleLinePhpDocData
|
||||
* @dataProvider provideMultiLinePhpDocData
|
||||
* @dataProvider provideTemplateTagsData
|
||||
* @dataProvider provideExtendsTagsData
|
||||
* @dataProvider provideRealWorldExampleData
|
||||
* @param string $label
|
||||
* @param string $input
|
||||
@ -2368,6 +2373,143 @@ some text in the middle'
|
||||
];
|
||||
}
|
||||
|
||||
public function provideExtendsTagsData(): \Iterator
|
||||
{
|
||||
yield [
|
||||
'OK with one argument',
|
||||
'/** @extends Foo<A> */',
|
||||
new PhpDocNode([
|
||||
new PhpDocTagNode(
|
||||
'@extends',
|
||||
new ExtendsTagValueNode(
|
||||
new GenericTypeNode(
|
||||
new IdentifierTypeNode('Foo'),
|
||||
[
|
||||
new IdentifierTypeNode('A'),
|
||||
]
|
||||
),
|
||||
''
|
||||
)
|
||||
),
|
||||
]),
|
||||
];
|
||||
|
||||
yield [
|
||||
'OK with two arguments',
|
||||
'/** @extends Foo<A,B> */',
|
||||
new PhpDocNode([
|
||||
new PhpDocTagNode(
|
||||
'@extends',
|
||||
new ExtendsTagValueNode(
|
||||
new GenericTypeNode(
|
||||
new IdentifierTypeNode('Foo'),
|
||||
[
|
||||
new IdentifierTypeNode('A'),
|
||||
new IdentifierTypeNode('B'),
|
||||
]
|
||||
),
|
||||
''
|
||||
)
|
||||
),
|
||||
]),
|
||||
];
|
||||
|
||||
yield [
|
||||
'OK @implements',
|
||||
'/** @implements Foo<A,B> */',
|
||||
new PhpDocNode([
|
||||
new PhpDocTagNode(
|
||||
'@implements',
|
||||
new ImplementsTagValueNode(
|
||||
new GenericTypeNode(
|
||||
new IdentifierTypeNode('Foo'),
|
||||
[
|
||||
new IdentifierTypeNode('A'),
|
||||
new IdentifierTypeNode('B'),
|
||||
]
|
||||
),
|
||||
''
|
||||
)
|
||||
),
|
||||
]),
|
||||
];
|
||||
|
||||
yield [
|
||||
'OK @uses',
|
||||
'/** @uses Foo<A,B> */',
|
||||
new PhpDocNode([
|
||||
new PhpDocTagNode(
|
||||
'@uses',
|
||||
new UsesTagValueNode(
|
||||
new GenericTypeNode(
|
||||
new IdentifierTypeNode('Foo'),
|
||||
[
|
||||
new IdentifierTypeNode('A'),
|
||||
new IdentifierTypeNode('B'),
|
||||
]
|
||||
),
|
||||
''
|
||||
)
|
||||
),
|
||||
]),
|
||||
];
|
||||
|
||||
yield [
|
||||
'OK with description',
|
||||
'/** @extends Foo<A> extends foo*/',
|
||||
new PhpDocNode([
|
||||
new PhpDocTagNode(
|
||||
'@extends',
|
||||
new ExtendsTagValueNode(
|
||||
new GenericTypeNode(
|
||||
new IdentifierTypeNode('Foo'),
|
||||
[new IdentifierTypeNode('A')]
|
||||
),
|
||||
'extends foo'
|
||||
)
|
||||
),
|
||||
]),
|
||||
];
|
||||
|
||||
yield [
|
||||
'invalid without type',
|
||||
'/** @extends */',
|
||||
new PhpDocNode([
|
||||
new PhpDocTagNode(
|
||||
'@extends',
|
||||
new InvalidTagValueNode(
|
||||
'',
|
||||
new \PHPStan\PhpDocParser\Parser\ParserException(
|
||||
'*/',
|
||||
Lexer::TOKEN_CLOSE_PHPDOC,
|
||||
13,
|
||||
Lexer::TOKEN_IDENTIFIER
|
||||
)
|
||||
)
|
||||
),
|
||||
]),
|
||||
];
|
||||
|
||||
yield [
|
||||
'invalid without arguments',
|
||||
'/** @extends Foo */',
|
||||
new PhpDocNode([
|
||||
new PhpDocTagNode(
|
||||
'@extends',
|
||||
new InvalidTagValueNode(
|
||||
'Foo',
|
||||
new \PHPStan\PhpDocParser\Parser\ParserException(
|
||||
'*/',
|
||||
Lexer::TOKEN_CLOSE_PHPDOC,
|
||||
17,
|
||||
Lexer::TOKEN_OPEN_ANGLE_BRACKET
|
||||
)
|
||||
)
|
||||
),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
public function providerDebug(): \Iterator
|
||||
{
|
||||
$sample = '/**
|
||||
|
Loading…
Reference in New Issue
Block a user