@@ -848,6 +848,7 @@ private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type,
848
848
849
849
$ items = [];
850
850
$ sealed = true ;
851
+ $ unsealedType = null ;
851
852
852
853
do {
853
854
$ tokens ->tryConsumeTokenType (Lexer::TOKEN_PHPDOC_EOL );
@@ -858,6 +859,17 @@ private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type,
858
859
859
860
if ($ tokens ->tryConsumeTokenType (Lexer::TOKEN_VARIADIC )) {
860
861
$ sealed = false ;
862
+
863
+ $ tokens ->tryConsumeTokenType (Lexer::TOKEN_PHPDOC_EOL );
864
+ if ($ tokens ->isCurrentTokenType (Lexer::TOKEN_OPEN_ANGLE_BRACKET )) {
865
+ if ($ kind === Ast \Type \ArrayShapeNode::KIND_ARRAY ) {
866
+ $ unsealedType = $ this ->parseArrayShapeUnsealedType ($ tokens );
867
+ } else {
868
+ $ unsealedType = $ this ->parseListShapeUnsealedType ($ tokens );
869
+ }
870
+ $ tokens ->tryConsumeTokenType (Lexer::TOKEN_PHPDOC_EOL );
871
+ }
872
+
861
873
$ tokens ->tryConsumeTokenType (Lexer::TOKEN_COMMA );
862
874
break ;
863
875
}
@@ -870,7 +882,7 @@ private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type,
870
882
$ tokens ->tryConsumeTokenType (Lexer::TOKEN_PHPDOC_EOL );
871
883
$ tokens ->consumeTokenType (Lexer::TOKEN_CLOSE_CURLY_BRACKET );
872
884
873
- return new Ast \Type \ArrayShapeNode ($ items , $ sealed , $ kind );
885
+ return new Ast \Type \ArrayShapeNode ($ items , $ sealed , $ kind, $ unsealedType );
874
886
}
875
887
876
888
@@ -949,6 +961,63 @@ private function parseArrayShapeKey(TokenIterator $tokens)
949
961
);
950
962
}
951
963
964
+ /**
965
+ * @phpstan-impure
966
+ */
967
+ private function parseArrayShapeUnsealedType (TokenIterator $ tokens ): Ast \Type \ArrayShapeUnsealedTypeNode
968
+ {
969
+ $ startLine = $ tokens ->currentTokenLine ();
970
+ $ startIndex = $ tokens ->currentTokenIndex ();
971
+
972
+ $ tokens ->consumeTokenType (Lexer::TOKEN_OPEN_ANGLE_BRACKET );
973
+ $ tokens ->tryConsumeTokenType (Lexer::TOKEN_PHPDOC_EOL );
974
+
975
+ $ valueType = $ this ->parse ($ tokens );
976
+ $ tokens ->tryConsumeTokenType (Lexer::TOKEN_PHPDOC_EOL );
977
+
978
+ $ keyType = null ;
979
+ if ($ tokens ->tryConsumeTokenType (Lexer::TOKEN_COMMA )) {
980
+ $ tokens ->tryConsumeTokenType (Lexer::TOKEN_PHPDOC_EOL );
981
+
982
+ $ keyType = $ valueType ;
983
+ $ valueType = $ this ->parse ($ tokens );
984
+ $ tokens ->tryConsumeTokenType (Lexer::TOKEN_PHPDOC_EOL );
985
+ }
986
+
987
+ $ tokens ->consumeTokenType (Lexer::TOKEN_CLOSE_ANGLE_BRACKET );
988
+
989
+ return $ this ->enrichWithAttributes (
990
+ $ tokens ,
991
+ new Ast \Type \ArrayShapeUnsealedTypeNode ($ valueType , $ keyType ),
992
+ $ startLine ,
993
+ $ startIndex
994
+ );
995
+ }
996
+
997
+ /**
998
+ * @phpstan-impure
999
+ */
1000
+ private function parseListShapeUnsealedType (TokenIterator $ tokens ): Ast \Type \ArrayShapeUnsealedTypeNode
1001
+ {
1002
+ $ startLine = $ tokens ->currentTokenLine ();
1003
+ $ startIndex = $ tokens ->currentTokenIndex ();
1004
+
1005
+ $ tokens ->consumeTokenType (Lexer::TOKEN_OPEN_ANGLE_BRACKET );
1006
+ $ tokens ->tryConsumeTokenType (Lexer::TOKEN_PHPDOC_EOL );
1007
+
1008
+ $ valueType = $ this ->parse ($ tokens );
1009
+ $ tokens ->tryConsumeTokenType (Lexer::TOKEN_PHPDOC_EOL );
1010
+
1011
+ $ tokens ->consumeTokenType (Lexer::TOKEN_CLOSE_ANGLE_BRACKET );
1012
+
1013
+ return $ this ->enrichWithAttributes (
1014
+ $ tokens ,
1015
+ new Ast \Type \ArrayShapeUnsealedTypeNode ($ valueType , null ),
1016
+ $ startLine ,
1017
+ $ startIndex
1018
+ );
1019
+ }
1020
+
952
1021
/**
953
1022
* @phpstan-impure
954
1023
*/
0 commit comments