ConstFetchNode - supports all constants wildcard

This commit is contained in:
Ondrej Mirtes 2020-04-30 19:11:49 +02:00
parent cfd8285ccc
commit fde6cca6d9
No known key found for this signature in database
GPG Key ID: 8E730BA25823D8B5
5 changed files with 80 additions and 19 deletions

View File

@ -53,8 +53,18 @@ class ConstExprParser
} }
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_COLON)) { if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_COLON)) {
$classConstantName = $tokens->currentTokenValue(); $classConstantName = '';
if ($tokens->currentTokenType() === Lexer::TOKEN_IDENTIFIER) {
$classConstantName .= $tokens->currentTokenValue();
$tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER);
if ($tokens->tryConsumeTokenValue('*')) {
$classConstantName .= '*';
}
} else {
$tokens->consumeTokenValue('*');
$classConstantName .= '*';
}
return new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName); return new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName);
} }

View File

@ -19,27 +19,43 @@ class ParserException extends \Exception
/** @var int */ /** @var int */
private $expectedTokenType; private $expectedTokenType;
/** @var string */
private $expectedTokenValue;
public function __construct( public function __construct(
string $currentTokenValue, string $currentTokenValue,
int $currentTokenType, int $currentTokenType,
int $currentOffset, int $currentOffset,
int $expectedTokenType ?int $expectedTokenType,
?string $expectedTokenValue = null
) )
{ {
$this->currentTokenValue = $currentTokenValue; $this->currentTokenValue = $currentTokenValue;
$this->currentTokenType = $currentTokenType; $this->currentTokenType = $currentTokenType;
$this->currentOffset = $currentOffset; $this->currentOffset = $currentOffset;
$this->expectedTokenType = $expectedTokenType; $this->expectedTokenType = $expectedTokenType;
$this->expectedTokenValue = $expectedTokenValue;
$json = json_encode($currentTokenValue, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); $json = json_encode($currentTokenValue, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
assert($json !== false); assert($json !== false);
if ($expectedTokenType !== null) {
parent::__construct(sprintf( parent::__construct(sprintf(
'Unexpected token %s, expected %s at offset %d', 'Unexpected token %s, expected %s at offset %d',
$json, $json,
Lexer::TOKEN_LABELS[$expectedTokenType], Lexer::TOKEN_LABELS[$expectedTokenType],
$currentOffset $currentOffset
)); ));
} elseif ($expectedTokenValue !== null) {
parent::__construct(sprintf(
'Unexpected token value %s, expected value %s at offset %d',
$json,
$expectedTokenValue,
$currentOffset
));
} else {
throw new \LogicException();
}
} }
@ -66,4 +82,9 @@ class ParserException extends \Exception
return $this->expectedTokenType; return $this->expectedTokenType;
} }
public function getExpectedTokenValue(): string
{
return $this->expectedTokenValue;
}
} }

View File

@ -69,6 +69,25 @@ class TokenIterator
return ($this->tokens[$this->index - 1][Lexer::TYPE_OFFSET] ?? -1) === Lexer::TOKEN_HORIZONTAL_WS; return ($this->tokens[$this->index - 1][Lexer::TYPE_OFFSET] ?? -1) === Lexer::TOKEN_HORIZONTAL_WS;
} }
/**
* @param string $tokenValue
* @throws \PHPStan\PhpDocParser\Parser\ParserException
*/
public function consumeTokenValue(string $tokenValue): void
{
if ($this->tokens[$this->index][Lexer::VALUE_OFFSET] !== $tokenValue) {
$this->throwError(null, $tokenValue);
}
$this->index++;
if (($this->tokens[$this->index][Lexer::TYPE_OFFSET] ?? -1) !== Lexer::TOKEN_HORIZONTAL_WS) {
return;
}
$this->index++;
}
/** /**
* @param int $tokenType * @param int $tokenType
@ -77,7 +96,7 @@ class TokenIterator
public function consumeTokenType(int $tokenType): void public function consumeTokenType(int $tokenType): void
{ {
if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType) { if ($this->tokens[$this->index][Lexer::TYPE_OFFSET] !== $tokenType) {
$this->throwError($tokenType); $this->throwError($tokenType, null);
} }
$this->index++; $this->index++;
@ -175,16 +194,18 @@ class TokenIterator
/** /**
* @param int $expectedTokenType * @param int|null $expectedTokenType
* @param string|null $expectedTokenValue
* @throws \PHPStan\PhpDocParser\Parser\ParserException * @throws \PHPStan\PhpDocParser\Parser\ParserException
*/ */
private function throwError(int $expectedTokenType): void private function throwError(?int $expectedTokenType, ?string $expectedTokenValue): void
{ {
throw new \PHPStan\PhpDocParser\Parser\ParserException( throw new \PHPStan\PhpDocParser\Parser\ParserException(
$this->currentTokenValue(), $this->currentTokenValue(),
$this->currentTokenType(), $this->currentTokenType(),
$this->currentTokenOffset(), $this->currentTokenOffset(),
$expectedTokenType $expectedTokenType,
$expectedTokenValue
); );
} }

View File

@ -108,12 +108,7 @@ class TypeParser
if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) { if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) {
throw $exception; throw $exception;
} }
if ($constExpr instanceof Ast\ConstExpr\ConstFetchNode) {
if ($tokens->currentTokenValue() === '*') {
$tokens->consumeTokenType($tokens->currentTokenType());
$constExpr = new Ast\ConstExpr\ConstFetchNode($constExpr->className, $constExpr->name . '*');
}
}
return new Ast\Type\ConstTypeNode($constExpr); return new Ast\Type\ConstTypeNode($constExpr);
} catch (\LogicException $e) { } catch (\LogicException $e) {
throw $exception; throw $exception;

View File

@ -858,6 +858,20 @@ class TypeParserTest extends \PHPUnit\Framework\TestCase
'Foo::FOO_*', 'Foo::FOO_*',
new ConstTypeNode(new ConstFetchNode('Foo', 'FOO_*')), new ConstTypeNode(new ConstFetchNode('Foo', 'FOO_*')),
], ],
[
'Foo::*',
new ConstTypeNode(new ConstFetchNode('Foo', '*')),
],
[
'Foo::**',
new \PHPStan\PhpDocParser\Parser\ParserException(
'**',
Lexer::TOKEN_END,
5,
null,
'*'
),
],
[ [
'( "foo" | Foo::FOO_* )', '( "foo" | Foo::FOO_* )',
new UnionTypeNode([ new UnionTypeNode([