From e0e5c18cb9eb376eb97be0bcd12222777196ed91 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 23 Jan 2020 17:37:46 +0100 Subject: [PATCH] Fix parsing of [] in various cases - closes #36 * Array shapes * Generics * $this * Nullables --- src/Parser/TypeParser.php | 23 ++++++- tests/PHPStan/Parser/TypeParserTest.php | 87 +++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 3 deletions(-) diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php index 9279b5a5..b519885f 100644 --- a/src/Parser/TypeParser.php +++ b/src/Parser/TypeParser.php @@ -37,10 +37,12 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { $type = $this->tryParseArray($tokens, $type); } - } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) { - return new Ast\Type\ThisTypeNode(); + $type = new Ast\Type\ThisTypeNode(); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArray($tokens, $type); + } } else { $type = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue()); $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); @@ -48,6 +50,9 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode 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); @@ -56,6 +61,10 @@ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode } 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); + } } } @@ -101,6 +110,10 @@ private function parseNullable(TokenIterator $tokens): Ast\Type\TypeNode $type = $this->parseArrayShape($tokens, $type); } + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArray($tokens, $type); + } + return new Ast\Type\NullableTypeNode($type); } @@ -179,6 +192,10 @@ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNo } } + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArray($tokens, $type); + } + return $type; } @@ -218,7 +235,7 @@ private function tryParseArray(TokenIterator $tokens, Ast\Type\TypeNode $type): } - private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode + private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\ArrayShapeNode { $tokens->consumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET); $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); diff --git a/tests/PHPStan/Parser/TypeParserTest.php b/tests/PHPStan/Parser/TypeParserTest.php index c7b6c9e4..d3e97409 100644 --- a/tests/PHPStan/Parser/TypeParserTest.php +++ b/tests/PHPStan/Parser/TypeParserTest.php @@ -711,6 +711,93 @@ public function provideParseData(): array new IdentifierTypeNode("\xA009") ), ], + [ + 'Collection[]', + new ArrayTypeNode( + new GenericTypeNode( + new IdentifierTypeNode('Collection'), + [ + new IdentifierTypeNode('array-key'), + new IdentifierTypeNode('int'), + ] + ) + ), + ], + [ + 'int | Collection[]', + new UnionTypeNode([ + new IdentifierTypeNode('int'), + new ArrayTypeNode( + new GenericTypeNode( + new IdentifierTypeNode('Collection'), + [ + new IdentifierTypeNode('array-key'), + new IdentifierTypeNode('int'), + ] + ) + ), + ]), + ], + [ + 'array{foo: int}[]', + new ArrayTypeNode( + new ArrayShapeNode([ + new ArrayShapeItemNode( + new IdentifierTypeNode('foo'), + false, + new IdentifierTypeNode('int') + ), + ]) + ), + ], + [ + 'int | array{foo: int}[]', + new UnionTypeNode([ + new IdentifierTypeNode('int'), + new ArrayTypeNode( + new ArrayShapeNode([ + new ArrayShapeItemNode( + new IdentifierTypeNode('foo'), + false, + new IdentifierTypeNode('int') + ), + ]) + ), + ]), + ], + [ + '$this[]', + new ArrayTypeNode( + new ThisTypeNode() + ), + ], + [ + 'int | $this[]', + new UnionTypeNode([ + new IdentifierTypeNode('int'), + new ArrayTypeNode( + new ThisTypeNode() + ), + ]), + ], + [ + 'callable(): int[]', + new CallableTypeNode( + new IdentifierTypeNode('callable'), + [], + new ArrayTypeNode( + new IdentifierTypeNode('int') + ) + ), + ], + [ + '?int[]', + new NullableTypeNode( + new ArrayTypeNode( + new IdentifierTypeNode('int') + ) + ), + ], ]; }