diff --git a/src/Ast/Type/ArrayShapeItemNode.php b/src/Ast/Type/ArrayShapeItemNode.php index bed62381..11c611d4 100644 --- a/src/Ast/Type/ArrayShapeItemNode.php +++ b/src/Ast/Type/ArrayShapeItemNode.php @@ -4,6 +4,7 @@ use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode; use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode; use PHPStan\PhpDocParser\Ast\Node; use PHPStan\PhpDocParser\Ast\NodeAttributes; use function sprintf; @@ -13,7 +14,7 @@ class ArrayShapeItemNode implements Node use NodeAttributes; - /** @var ConstExprIntegerNode|ConstExprStringNode|IdentifierTypeNode|null */ + /** @var ConstExprIntegerNode|ConstExprStringNode|ConstFetchNode|IdentifierTypeNode|null */ public $keyName; public bool $optional; @@ -21,7 +22,7 @@ class ArrayShapeItemNode implements Node public TypeNode $valueType; /** - * @param ConstExprIntegerNode|ConstExprStringNode|IdentifierTypeNode|null $keyName + * @param ConstExprIntegerNode|ConstExprStringNode|ConstFetchNode|IdentifierTypeNode|null $keyName */ public function __construct($keyName, bool $optional, TypeNode $valueType) { diff --git a/src/Parser/TypeParser.php b/src/Parser/TypeParser.php index fc225854..62f80406 100644 --- a/src/Parser/TypeParser.php +++ b/src/Parser/TypeParser.php @@ -970,7 +970,7 @@ private function parseArrayShapeItem(TokenIterator $tokens): Ast\Type\ArrayShape /** * @phpstan-impure - * @return Ast\ConstExpr\ConstExprIntegerNode|Ast\ConstExpr\ConstExprStringNode|Ast\Type\IdentifierTypeNode + * @return Ast\ConstExpr\ConstExprIntegerNode|Ast\ConstExpr\ConstExprStringNode|Ast\ConstExpr\ConstFetchNode|Ast\Type\IdentifierTypeNode */ private function parseArrayShapeKey(TokenIterator $tokens) { @@ -991,8 +991,17 @@ private function parseArrayShapeKey(TokenIterator $tokens) $tokens->next(); } else { - $key = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue()); + $identifier = $tokens->currentTokenValue(); $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_COLON)) { + $classConstantName = $tokens->currentTokenValue(); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + + $key = new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName); + } else { + $key = new Ast\Type\IdentifierTypeNode($identifier); + } } return $this->enrichWithAttributes( diff --git a/tests/PHPStan/Ast/ToString/TypeToStringTest.php b/tests/PHPStan/Ast/ToString/TypeToStringTest.php index 6716e433..254cff58 100644 --- a/tests/PHPStan/Ast/ToString/TypeToStringTest.php +++ b/tests/PHPStan/Ast/ToString/TypeToStringTest.php @@ -6,6 +6,7 @@ use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode; use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNullNode; use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode; +use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode; use PHPStan\PhpDocParser\Ast\Node; use PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode; use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode; @@ -75,6 +76,13 @@ public static function provideArrayCases(): Generator new ArrayShapeItemNode(new ConstExprIntegerNode('1'), false, new IdentifierTypeNode('Baz')), ]), ], + [ + 'array{Foo::BAR: Foo, Bar::FOO?: Bar}', + ArrayShapeNode::createSealed([ + new ArrayShapeItemNode(new ConstFetchNode('Foo', 'BAR'), false, new IdentifierTypeNode('Foo')), + new ArrayShapeItemNode(new ConstFetchNode('Bar', 'FOO'), true, new IdentifierTypeNode('Bar')), + ]), + ], ['list{}', ArrayShapeNode::createSealed([], 'list')], ['list{...}', ArrayShapeNode::createUnsealed([], null, 'list')], [ diff --git a/tests/PHPStan/Parser/TypeParserTest.php b/tests/PHPStan/Parser/TypeParserTest.php index 8fe96f5f..01a511b8 100644 --- a/tests/PHPStan/Parser/TypeParserTest.php +++ b/tests/PHPStan/Parser/TypeParserTest.php @@ -3041,6 +3041,16 @@ public function provideParseData(): array new IdentifierTypeNode('MongoCollection'), Lexer::TOKEN_OPEN_ANGLE_BRACKET, ], + [ + 'array{Foo::BAR: int}', + ArrayShapeNode::createSealed([ + new ArrayShapeItemNode( + new ConstFetchNode('Foo', 'BAR'), + false, + new IdentifierTypeNode('int'), + ), + ]), + ], ]; }