mirror of
https://github.com/danog/phpdoc-parser.git
synced 2024-11-30 04:29:20 +01:00
Support ConstExprNode as type
This commit is contained in:
parent
c3aaad7ef5
commit
d60ff778be
23
src/Ast/Type/ConstTypeNode.php
Normal file
23
src/Ast/Type/ConstTypeNode.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace PHPStan\PhpDocParser\Ast\Type;
|
||||
|
||||
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
|
||||
|
||||
class ConstTypeNode implements TypeNode
|
||||
{
|
||||
|
||||
/** @var ConstExprNode */
|
||||
public $constExpr;
|
||||
|
||||
public function __construct(ConstExprNode $constExpr)
|
||||
{
|
||||
$this->constExpr = $constExpr;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->constExpr->__toString();
|
||||
}
|
||||
|
||||
}
|
@ -8,7 +8,7 @@ use PHPStan\PhpDocParser\Lexer\Lexer;
|
||||
class ConstExprParser
|
||||
{
|
||||
|
||||
public function parse(TokenIterator $tokens): Ast\ConstExpr\ConstExprNode
|
||||
public function parse(TokenIterator $tokens, bool $trimStrings = false): Ast\ConstExpr\ConstExprNode
|
||||
{
|
||||
if ($tokens->isCurrentTokenType(Lexer::TOKEN_FLOAT)) {
|
||||
$value = $tokens->currentTokenValue();
|
||||
@ -22,11 +22,17 @@ class ConstExprParser
|
||||
|
||||
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) {
|
||||
$value = $tokens->currentTokenValue();
|
||||
if ($trimStrings) {
|
||||
$value = trim($tokens->currentTokenValue(), "'");
|
||||
}
|
||||
$tokens->next();
|
||||
return new Ast\ConstExpr\ConstExprStringNode($value);
|
||||
|
||||
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) {
|
||||
$value = $tokens->currentTokenValue();
|
||||
if ($trimStrings) {
|
||||
$value = trim($tokens->currentTokenValue(), '"');
|
||||
}
|
||||
$tokens->next();
|
||||
return new Ast\ConstExpr\ConstExprStringNode($value);
|
||||
|
||||
|
@ -8,6 +8,17 @@ use PHPStan\PhpDocParser\Lexer\Lexer;
|
||||
class TypeParser
|
||||
{
|
||||
|
||||
/** @var ConstExprParser|null */
|
||||
private $constExprParser;
|
||||
|
||||
/**
|
||||
* @param ConstExprParser|null $constExprParser
|
||||
*/
|
||||
public function __construct(?ConstExprParser $constExprParser = null)
|
||||
{
|
||||
$this->constExprParser = $constExprParser;
|
||||
}
|
||||
|
||||
public function parse(TokenIterator $tokens): Ast\Type\TypeNode
|
||||
{
|
||||
if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) {
|
||||
@ -35,40 +46,75 @@ class TypeParser
|
||||
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
|
||||
|
||||
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
|
||||
$type = $this->tryParseArray($tokens, $type);
|
||||
return $this->tryParseArray($tokens, $type);
|
||||
}
|
||||
|
||||
return $type;
|
||||
} elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) {
|
||||
$type = new Ast\Type\ThisTypeNode();
|
||||
|
||||
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
|
||||
$type = $this->tryParseArray($tokens, $type);
|
||||
return $this->tryParseArray($tokens, $type);
|
||||
}
|
||||
} else {
|
||||
$type = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue());
|
||||
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
|
||||
|
||||
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
|
||||
$type = $this->parseGeneric($tokens, $type);
|
||||
|
||||
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
|
||||
$type = $this->tryParseArray($tokens, $type);
|
||||
}
|
||||
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
|
||||
$type = $this->tryParseCallable($tokens, $type);
|
||||
|
||||
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
|
||||
$type = $this->tryParseArray($tokens, $type);
|
||||
|
||||
} elseif ($type->name === 'array' && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) {
|
||||
$type = $this->parseArrayShape($tokens, $type);
|
||||
|
||||
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
|
||||
$type = $this->tryParseArray($tokens, $type);
|
||||
}
|
||||
}
|
||||
return $type;
|
||||
}
|
||||
|
||||
return $type;
|
||||
$currentTokenValue = $tokens->currentTokenValue();
|
||||
$tokens->pushSavePoint(); // because of ConstFetchNode
|
||||
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) {
|
||||
$type = new Ast\Type\IdentifierTypeNode($currentTokenValue);
|
||||
|
||||
if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
|
||||
$tokens->dropSavePoint(); // because of ConstFetchNode
|
||||
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
|
||||
$type = $this->parseGeneric($tokens, $type);
|
||||
|
||||
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
|
||||
$type = $this->tryParseArray($tokens, $type);
|
||||
}
|
||||
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) {
|
||||
$type = $this->tryParseCallable($tokens, $type);
|
||||
|
||||
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
|
||||
$type = $this->tryParseArray($tokens, $type);
|
||||
|
||||
} elseif ($type->name === 'array' && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) {
|
||||
$type = $this->parseArrayShape($tokens, $type);
|
||||
|
||||
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) {
|
||||
$type = $this->tryParseArray($tokens, $type);
|
||||
}
|
||||
}
|
||||
|
||||
return $type;
|
||||
} else {
|
||||
$tokens->rollback(); // because of ConstFetchNode
|
||||
}
|
||||
} else {
|
||||
$tokens->dropSavePoint(); // because of ConstFetchNode
|
||||
}
|
||||
|
||||
$exception = new \PHPStan\PhpDocParser\Parser\ParserException(
|
||||
$tokens->currentTokenValue(),
|
||||
$tokens->currentTokenType(),
|
||||
$tokens->currentTokenOffset(),
|
||||
Lexer::TOKEN_IDENTIFIER
|
||||
);
|
||||
|
||||
if ($this->constExprParser === null) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
try {
|
||||
$constExpr = $this->constExprParser->parse($tokens, true);
|
||||
if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {
|
||||
throw $exception;
|
||||
}
|
||||
return new Ast\Type\ConstTypeNode($constExpr);
|
||||
} catch (\LogicException $e) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,7 +22,7 @@ class FuzzyTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
parent::setUp();
|
||||
$this->lexer = new Lexer();
|
||||
$this->typeParser = new TypeParser();
|
||||
$this->typeParser = new TypeParser(new ConstExprParser());
|
||||
$this->constExprParser = new ConstExprParser();
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ namespace PHPStan\PhpDocParser\Parser;
|
||||
|
||||
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprArrayNode;
|
||||
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
|
||||
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\DeprecatedTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ExtendsTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
|
||||
@ -24,6 +25,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
|
||||
@ -42,7 +44,8 @@ class PhpDocParserTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
parent::setUp();
|
||||
$this->lexer = new Lexer();
|
||||
$this->phpDocParser = new PhpDocParser(new TypeParser(), new ConstExprParser());
|
||||
$constExprParser = new ConstExprParser();
|
||||
$this->phpDocParser = new PhpDocParser(new TypeParser($constExprParser), $constExprParser);
|
||||
}
|
||||
|
||||
|
||||
@ -944,15 +947,37 @@ class PhpDocParserTest extends \PHPUnit\Framework\TestCase
|
||||
new InvalidTagValueNode(
|
||||
'A | B < 123',
|
||||
new \PHPStan\PhpDocParser\Parser\ParserException(
|
||||
'123',
|
||||
Lexer::TOKEN_INTEGER,
|
||||
20,
|
||||
Lexer::TOKEN_IDENTIFIER
|
||||
'*/',
|
||||
Lexer::TOKEN_CLOSE_PHPDOC,
|
||||
24,
|
||||
Lexer::TOKEN_CLOSE_ANGLE_BRACKET
|
||||
)
|
||||
)
|
||||
),
|
||||
]),
|
||||
];
|
||||
|
||||
yield [
|
||||
'OK with type and const expr as generic type variable',
|
||||
'/** @return A | B < 123 > */',
|
||||
new PhpDocNode([
|
||||
new PhpDocTagNode(
|
||||
'@return',
|
||||
new ReturnTagValueNode(
|
||||
new UnionTypeNode([
|
||||
new IdentifierTypeNode('A'),
|
||||
new GenericTypeNode(
|
||||
new IdentifierTypeNode('B'),
|
||||
[
|
||||
new ConstTypeNode(new ConstExprIntegerNode('123')),
|
||||
]
|
||||
),
|
||||
]),
|
||||
''
|
||||
)
|
||||
),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -2901,6 +2926,23 @@ chunk. Must be higher that in the previous request.'),
|
||||
),
|
||||
]),
|
||||
];
|
||||
|
||||
yield [
|
||||
'string literals in @return',
|
||||
"/** @return 'foo'|'bar' */",
|
||||
new PhpDocNode([
|
||||
new PhpDocTagNode(
|
||||
'@return',
|
||||
new ReturnTagValueNode(
|
||||
new UnionTypeNode([
|
||||
new ConstTypeNode(new ConstExprStringNode('foo')),
|
||||
new ConstTypeNode(new ConstExprStringNode('bar')),
|
||||
]),
|
||||
''
|
||||
)
|
||||
),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,13 +2,16 @@
|
||||
|
||||
namespace PHPStan\PhpDocParser\Parser;
|
||||
|
||||
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFloatNode;
|
||||
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
|
||||
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
|
||||
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
|
||||
@ -31,7 +34,7 @@ class TypeParserTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
parent::setUp();
|
||||
$this->lexer = new Lexer();
|
||||
$this->typeParser = new TypeParser();
|
||||
$this->typeParser = new TypeParser(new ConstExprParser());
|
||||
}
|
||||
|
||||
|
||||
@ -828,6 +831,29 @@ class TypeParserTest extends \PHPUnit\Framework\TestCase
|
||||
new CallableTypeParameterNode(new IdentifierTypeNode('mixed'), false, true, '', false),
|
||||
], new IdentifierTypeNode('TReturn')),
|
||||
],
|
||||
[
|
||||
"'foo'|'bar'",
|
||||
new UnionTypeNode([
|
||||
new ConstTypeNode(new ConstExprStringNode('foo')),
|
||||
new ConstTypeNode(new ConstExprStringNode('bar')),
|
||||
]),
|
||||
],
|
||||
[
|
||||
'Foo::FOO_CONSTANT',
|
||||
new ConstTypeNode(new ConstFetchNode('Foo', 'FOO_CONSTANT')),
|
||||
],
|
||||
[
|
||||
'123',
|
||||
new ConstTypeNode(new ConstExprIntegerNode('123')),
|
||||
],
|
||||
[
|
||||
'123.2',
|
||||
new ConstTypeNode(new ConstExprFloatNode('123.2')),
|
||||
],
|
||||
[
|
||||
'"bar"',
|
||||
new ConstTypeNode(new ConstExprStringNode('bar')),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user