From d840b6956b47930f19eb9d8dbc9553650d5072d6 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 4 May 2020 22:30:43 +0200 Subject: [PATCH] Fix parsing const fetches with wildcard in complex types --- src/Lexer/Lexer.php | 12 +++++--- src/Parser/ConstExprParser.php | 4 +-- src/Parser/ParserException.php | 35 +++++------------------ src/Parser/TokenIterator.php | 27 ++--------------- tests/PHPStan/Parser/PhpDocParserTest.php | 8 ++++++ tests/PHPStan/Parser/TypeParserTest.php | 34 +++++++++++++++++----- 6 files changed, 55 insertions(+), 65 deletions(-) diff --git a/src/Lexer/Lexer.php b/src/Lexer/Lexer.php index 36cd196..f03f0db 100644 --- a/src/Lexer/Lexer.php +++ b/src/Lexer/Lexer.php @@ -18,10 +18,7 @@ class Lexer public const TOKEN_CLOSE_ANGLE_BRACKET = 7; public const TOKEN_OPEN_SQUARE_BRACKET = 8; public const TOKEN_CLOSE_SQUARE_BRACKET = 9; - public const TOKEN_OPEN_CURLY_BRACKET = 30; - public const TOKEN_CLOSE_CURLY_BRACKET = 31; public const TOKEN_COMMA = 10; - public const TOKEN_COLON = 29; public const TOKEN_VARIADIC = 11; public const TOKEN_DOUBLE_COLON = 12; public const TOKEN_DOUBLE_ARROW = 13; @@ -29,7 +26,6 @@ class Lexer public const TOKEN_OPEN_PHPDOC = 15; public const TOKEN_CLOSE_PHPDOC = 16; public const TOKEN_PHPDOC_TAG = 17; - public const TOKEN_PHPDOC_EOL = 26; public const TOKEN_FLOAT = 18; public const TOKEN_INTEGER = 19; public const TOKEN_SINGLE_QUOTED_STRING = 20; @@ -38,8 +34,13 @@ class Lexer public const TOKEN_THIS_VARIABLE = 23; public const TOKEN_VARIABLE = 24; public const TOKEN_HORIZONTAL_WS = 25; + public const TOKEN_PHPDOC_EOL = 26; public const TOKEN_OTHER = 27; public const TOKEN_END = 28; + public const TOKEN_COLON = 29; + public const TOKEN_WILDCARD = 30; + public const TOKEN_OPEN_CURLY_BRACKET = 31; + public const TOKEN_CLOSE_CURLY_BRACKET = 32; public const TOKEN_LABELS = [ self::TOKEN_REFERENCE => '\'&\'', @@ -74,6 +75,7 @@ class Lexer self::TOKEN_HORIZONTAL_WS => 'TOKEN_HORIZONTAL_WS', self::TOKEN_OTHER => 'TOKEN_OTHER', self::TOKEN_END => 'TOKEN_END', + self::TOKEN_WILDCARD => '*', ]; public const VALUE_OFFSET = 0; @@ -153,6 +155,8 @@ class Lexer self::TOKEN_HORIZONTAL_WS => '[\\x09\\x20]++', + self::TOKEN_WILDCARD => '\\*', + // anything but TOKEN_CLOSE_PHPDOC or TOKEN_HORIZONTAL_WS or TOKEN_EOL self::TOKEN_OTHER => '(?:(?!\\*/)[^\\s])++', ]; diff --git a/src/Parser/ConstExprParser.php b/src/Parser/ConstExprParser.php index 73e65d9..012a0cb 100644 --- a/src/Parser/ConstExprParser.php +++ b/src/Parser/ConstExprParser.php @@ -57,11 +57,11 @@ class ConstExprParser if ($tokens->currentTokenType() === Lexer::TOKEN_IDENTIFIER) { $classConstantName .= $tokens->currentTokenValue(); $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - if ($tokens->tryConsumeTokenValue('*')) { + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_WILDCARD)) { $classConstantName .= '*'; } } else { - $tokens->consumeTokenValue('*'); + $tokens->consumeTokenType(Lexer::TOKEN_WILDCARD); $classConstantName .= '*'; } diff --git a/src/Parser/ParserException.php b/src/Parser/ParserException.php index 2951af8..d6fd6fe 100644 --- a/src/Parser/ParserException.php +++ b/src/Parser/ParserException.php @@ -19,43 +19,27 @@ class ParserException extends \Exception /** @var int */ private $expectedTokenType; - /** @var string */ - private $expectedTokenValue; - public function __construct( string $currentTokenValue, int $currentTokenType, int $currentOffset, - ?int $expectedTokenType, - ?string $expectedTokenValue = null + int $expectedTokenType ) { $this->currentTokenValue = $currentTokenValue; $this->currentTokenType = $currentTokenType; $this->currentOffset = $currentOffset; $this->expectedTokenType = $expectedTokenType; - $this->expectedTokenValue = $expectedTokenValue; $json = json_encode($currentTokenValue, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); assert($json !== false); - if ($expectedTokenType !== null) { - parent::__construct(sprintf( - 'Unexpected token %s, expected %s at offset %d', - $json, - Lexer::TOKEN_LABELS[$expectedTokenType], - $currentOffset - )); - } elseif ($expectedTokenValue !== null) { - parent::__construct(sprintf( - 'Unexpected token value %s, expected value %s at offset %d', - $json, - $expectedTokenValue, - $currentOffset - )); - } else { - throw new \LogicException(); - } + parent::__construct(sprintf( + 'Unexpected token %s, expected %s at offset %d', + $json, + Lexer::TOKEN_LABELS[$expectedTokenType], + $currentOffset + )); } @@ -82,9 +66,4 @@ class ParserException extends \Exception return $this->expectedTokenType; } - public function getExpectedTokenValue(): string - { - return $this->expectedTokenValue; - } - } diff --git a/src/Parser/TokenIterator.php b/src/Parser/TokenIterator.php index 8b66173..382859f 100644 --- a/src/Parser/TokenIterator.php +++ b/src/Parser/TokenIterator.php @@ -69,25 +69,6 @@ class TokenIterator 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 @@ -194,18 +175,16 @@ class TokenIterator /** - * @param int|null $expectedTokenType - * @param string|null $expectedTokenValue + * @param int $expectedTokenType * @throws \PHPStan\PhpDocParser\Parser\ParserException */ - private function throwError(?int $expectedTokenType, ?string $expectedTokenValue): void + private function throwError(int $expectedTokenType): void { throw new \PHPStan\PhpDocParser\Parser\ParserException( $this->currentTokenValue(), $this->currentTokenType(), $this->currentTokenOffset(), - $expectedTokenType, - $expectedTokenValue + $expectedTokenType ); } diff --git a/tests/PHPStan/Parser/PhpDocParserTest.php b/tests/PHPStan/Parser/PhpDocParserTest.php index b8dd9ea..2487205 100644 --- a/tests/PHPStan/Parser/PhpDocParserTest.php +++ b/tests/PHPStan/Parser/PhpDocParserTest.php @@ -3046,6 +3046,14 @@ chunk. Must be higher that in the previous request.'), ), ]), ]; + + yield [ + 'malformed const fetch', + '/** @param Foo::** $a */', + new PhpDocNode([ + new PhpDocTagNode('@param', new InvalidTagValueNode('Foo::** $a', new ParserException('*', Lexer::TOKEN_WILDCARD, 17, Lexer::TOKEN_VARIABLE))), + ]), + ]; } public function dataParseTagValue(): array diff --git a/tests/PHPStan/Parser/TypeParserTest.php b/tests/PHPStan/Parser/TypeParserTest.php index fffd7e6..4809d65 100644 --- a/tests/PHPStan/Parser/TypeParserTest.php +++ b/tests/PHPStan/Parser/TypeParserTest.php @@ -864,13 +864,13 @@ class TypeParserTest extends \PHPUnit\Framework\TestCase ], [ 'Foo::**', - new \PHPStan\PhpDocParser\Parser\ParserException( - '**', - Lexer::TOKEN_END, - 5, - null, - '*' - ), + new ConstTypeNode(new ConstFetchNode('Foo', '*')), // fails later in PhpDocParser + Lexer::TOKEN_WILDCARD, + ], + [ + 'Foo::*a', + new ConstTypeNode(new ConstFetchNode('Foo', '*')), // fails later in PhpDocParser + Lexer::TOKEN_IDENTIFIER, ], [ '( "foo" | Foo::FOO_* )', @@ -879,6 +879,26 @@ class TypeParserTest extends \PHPUnit\Framework\TestCase new ConstTypeNode(new ConstFetchNode('Foo', 'FOO_*')), ]), ], + [ + 'DateTimeImmutable::*|DateTime::*', + new UnionTypeNode([ + new ConstTypeNode(new ConstFetchNode('DateTimeImmutable', '*')), + new ConstTypeNode(new ConstFetchNode('DateTime', '*')), + ]), + ], + [ + 'ParameterTier::*|null', + new UnionTypeNode([ + new ConstTypeNode(new ConstFetchNode('ParameterTier', '*')), + new IdentifierTypeNode('null'), + ]), + ], + [ + 'list', + new GenericTypeNode(new IdentifierTypeNode('list'), [ + new ConstTypeNode(new ConstFetchNode('QueueAttributeName', '*')), + ]), + ], ]; }