Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a2268bb

Browse files
committedAug 28, 2024··
Combine types into an ArrayShapeUnsealedTypeNode
1 parent f374c5e commit a2268bb

File tree

5 files changed

+170
-77
lines changed

5 files changed

+170
-77
lines changed
 

‎src/Ast/Type/ArrayShapeNode.php

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,8 @@ class ArrayShapeNode implements TypeNode
2222
/** @var self::KIND_* */
2323
public $kind;
2424

25-
/** @var TypeNode|null */
26-
public $extraKeyType;
27-
28-
/** @var TypeNode|null */
29-
public $extraValueType;
25+
/** @var ArrayShapeUnsealedTypeNode|null */
26+
public $unsealedType;
3027

3128
/**
3229
* @param ArrayShapeItemNode[] $items
@@ -36,15 +33,13 @@ public function __construct(
3633
array $items,
3734
bool $sealed = true,
3835
string $kind = self::KIND_ARRAY,
39-
?TypeNode $extraKeyType = null,
40-
?TypeNode $extraValueType = null
36+
?ArrayShapeUnsealedTypeNode $unsealedType = null
4137
)
4238
{
4339
$this->items = $items;
4440
$this->sealed = $sealed;
4541
$this->kind = $kind;
46-
$this->extraKeyType = $extraKeyType;
47-
$this->extraValueType = $extraValueType;
42+
$this->unsealedType = $unsealedType;
4843
}
4944

5045

@@ -53,16 +48,7 @@ public function __toString(): string
5348
$items = $this->items;
5449

5550
if (! $this->sealed) {
56-
$item = '...';
57-
if ($this->extraValueType !== null) {
58-
$extraTypes = [];
59-
if ($this->extraKeyType !== null) {
60-
$extraTypes[] = (string) $this->extraKeyType;
61-
}
62-
$extraTypes[] = (string) $this->extraValueType;
63-
$item .= '<' . implode(', ', $extraTypes) . '>';
64-
}
65-
$items[] = $item;
51+
$items[] = '...' . $this->unsealedType;
6652
}
6753

6854
return $this->kind . '{' . implode(', ', $items) . '}';
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\PhpDocParser\Ast\Type;
4+
5+
use PHPStan\PhpDocParser\Ast\NodeAttributes;
6+
use function sprintf;
7+
8+
class ArrayShapeUnsealedTypeNode implements TypeNode
9+
{
10+
11+
use NodeAttributes;
12+
13+
/** @var TypeNode */
14+
public $valueType;
15+
16+
/** @var TypeNode|null */
17+
public $keyType;
18+
19+
public function __construct(TypeNode $valueType, ?TypeNode $keyType)
20+
{
21+
$this->valueType = $valueType;
22+
$this->keyType = $keyType;
23+
}
24+
25+
public function __toString(): string
26+
{
27+
if ($this->keyType !== null) {
28+
return sprintf('<%s, %s>', $this->keyType, $this->valueType);
29+
}
30+
return sprintf('<%s>', $this->valueType);
31+
}
32+
33+
}

‎src/Parser/TypeParser.php

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -848,8 +848,7 @@ private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type,
848848

849849
$items = [];
850850
$sealed = true;
851-
$extraKeyType = null;
852-
$extraValueType = null;
851+
$unsealedType = null;
853852

854853
do {
855854
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
@@ -862,23 +861,12 @@ private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type,
862861
$sealed = false;
863862

864863
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
865-
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
866-
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
867-
868-
$extraValueType = $this->parse($tokens);
869-
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
870-
864+
if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) {
871865
if ($kind === Ast\Type\ArrayShapeNode::KIND_ARRAY) {
872-
if ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)) {
873-
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
874-
875-
$extraKeyType = $extraValueType;
876-
$extraValueType = $this->parse($tokens);
877-
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
878-
}
866+
$unsealedType = $this->parseArrayShapeUnsealedType($tokens);
867+
} else {
868+
$unsealedType = $this->parseListShapeUnsealedType($tokens);
879869
}
880-
881-
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET);
882870
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
883871
}
884872

@@ -894,7 +882,7 @@ private function parseArrayShape(TokenIterator $tokens, Ast\Type\TypeNode $type,
894882
$tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
895883
$tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET);
896884

897-
return new Ast\Type\ArrayShapeNode($items, $sealed, $kind, $extraKeyType, $extraValueType);
885+
return new Ast\Type\ArrayShapeNode($items, $sealed, $kind, $unsealedType);
898886
}
899887

900888

@@ -973,6 +961,63 @@ private function parseArrayShapeKey(TokenIterator $tokens)
973961
);
974962
}
975963

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+
9761021
/**
9771022
* @phpstan-impure
9781023
*/

‎src/Printer/Printer.php

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
4444
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode;
4545
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode;
46+
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeUnsealedTypeNode;
4647
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
4748
use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
4849
use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
@@ -366,16 +367,7 @@ private function printType(TypeNode $node): string
366367
}, $node->items);
367368

368369
if (! $node->sealed) {
369-
$item = '...';
370-
if ($node->extraValueType !== null) {
371-
$extraTypes = [];
372-
if ($node->extraKeyType !== null) {
373-
$extraTypes[] = $this->printType($node->extraKeyType);
374-
}
375-
$extraTypes[] = $this->printType($node->extraValueType);
376-
$item .= '<' . implode(', ', $extraTypes) . '>';
377-
}
378-
$items[] = $item;
370+
$items[] = '...' . ($node->unsealedType === null ? '' : $this->printType($node->unsealedType));
379371
}
380372

381373
return $node->kind . '{' . implode(', ', $items) . '}';
@@ -392,6 +384,12 @@ private function printType(TypeNode $node): string
392384

393385
return $this->printType($node->valueType);
394386
}
387+
if ($node instanceof ArrayShapeUnsealedTypeNode) {
388+
if ($node->keyType !== null) {
389+
return sprintf('<%s, %s>', $this->printType($node->keyType), $this->printType($node->valueType));
390+
}
391+
return sprintf('<%s>', $this->printType($node->valueType));
392+
}
395393
if ($node instanceof ArrayTypeNode) {
396394
return $this->printOffsetAccessType($node->type) . '[]';
397395
}

‎tests/PHPStan/Parser/TypeParserTest.php

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
1414
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeItemNode;
1515
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode;
16+
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeUnsealedTypeNode;
1617
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
1718
use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
1819
use PHPStan\PhpDocParser\Ast\Type\CallableTypeParameterNode;
@@ -766,8 +767,10 @@ public function provideParseData(): array
766767
[],
767768
false,
768769
ArrayShapeNode::KIND_ARRAY,
769-
null,
770-
new IdentifierTypeNode('string')
770+
new ArrayShapeUnsealedTypeNode(
771+
new IdentifierTypeNode('string'),
772+
null
773+
)
771774
),
772775
],
773776
[
@@ -787,8 +790,10 @@ public function provideParseData(): array
787790
],
788791
false,
789792
ArrayShapeNode::KIND_ARRAY,
790-
null,
791-
new IdentifierTypeNode('string')
793+
new ArrayShapeUnsealedTypeNode(
794+
new IdentifierTypeNode('string'),
795+
null
796+
)
792797
),
793798
],
794799
[
@@ -808,8 +813,10 @@ public function provideParseData(): array
808813
],
809814
false,
810815
ArrayShapeNode::KIND_ARRAY,
811-
null,
812-
new IdentifierTypeNode('string')
816+
new ArrayShapeUnsealedTypeNode(
817+
new IdentifierTypeNode('string'),
818+
null
819+
)
813820
),
814821
],
815822
[
@@ -834,8 +841,10 @@ public function provideParseData(): array
834841
],
835842
false,
836843
ArrayShapeNode::KIND_ARRAY,
837-
null,
838-
new IdentifierTypeNode('string')
844+
new ArrayShapeUnsealedTypeNode(
845+
new IdentifierTypeNode('string'),
846+
null
847+
)
839848
),
840849
],
841850
[
@@ -844,8 +853,10 @@ public function provideParseData(): array
844853
[],
845854
false,
846855
ArrayShapeNode::KIND_ARRAY,
847-
new IdentifierTypeNode('int'),
848-
new IdentifierTypeNode('string')
856+
new ArrayShapeUnsealedTypeNode(
857+
new IdentifierTypeNode('string'),
858+
new IdentifierTypeNode('int')
859+
)
849860
),
850861
],
851862
[
@@ -865,8 +876,10 @@ public function provideParseData(): array
865876
],
866877
false,
867878
ArrayShapeNode::KIND_ARRAY,
868-
new IdentifierTypeNode('int'),
869-
new IdentifierTypeNode('string')
879+
new ArrayShapeUnsealedTypeNode(
880+
new IdentifierTypeNode('string'),
881+
new IdentifierTypeNode('int')
882+
)
870883
),
871884
],
872885
[
@@ -886,8 +899,10 @@ public function provideParseData(): array
886899
],
887900
false,
888901
ArrayShapeNode::KIND_ARRAY,
889-
new IdentifierTypeNode('int'),
890-
new IdentifierTypeNode('string')
902+
new ArrayShapeUnsealedTypeNode(
903+
new IdentifierTypeNode('string'),
904+
new IdentifierTypeNode('int')
905+
)
891906
),
892907
],
893908
[
@@ -914,8 +929,10 @@ public function provideParseData(): array
914929
],
915930
false,
916931
ArrayShapeNode::KIND_ARRAY,
917-
new IdentifierTypeNode('int'),
918-
new IdentifierTypeNode('string')
932+
new ArrayShapeUnsealedTypeNode(
933+
new IdentifierTypeNode('string'),
934+
new IdentifierTypeNode('int')
935+
)
919936
),
920937
],
921938
[
@@ -924,8 +941,10 @@ public function provideParseData(): array
924941
[],
925942
false,
926943
ArrayShapeNode::KIND_LIST,
927-
null,
928-
new IdentifierTypeNode('string')
944+
new ArrayShapeUnsealedTypeNode(
945+
new IdentifierTypeNode('string'),
946+
null
947+
)
929948
),
930949
],
931950
[
@@ -945,8 +964,10 @@ public function provideParseData(): array
945964
],
946965
false,
947966
ArrayShapeNode::KIND_LIST,
948-
null,
949-
new IdentifierTypeNode('string')
967+
new ArrayShapeUnsealedTypeNode(
968+
new IdentifierTypeNode('string'),
969+
null
970+
)
950971
),
951972
],
952973
[
@@ -966,8 +987,10 @@ public function provideParseData(): array
966987
],
967988
false,
968989
ArrayShapeNode::KIND_LIST,
969-
null,
970-
new IdentifierTypeNode('string')
990+
new ArrayShapeUnsealedTypeNode(
991+
new IdentifierTypeNode('string'),
992+
null
993+
)
971994
),
972995
],
973996
[
@@ -992,8 +1015,10 @@ public function provideParseData(): array
9921015
],
9931016
false,
9941017
ArrayShapeNode::KIND_LIST,
995-
null,
996-
new IdentifierTypeNode('string')
1018+
new ArrayShapeUnsealedTypeNode(
1019+
new IdentifierTypeNode('string'),
1020+
null
1021+
)
9971022
),
9981023
],
9991024
[
@@ -1013,8 +1038,10 @@ public function provideParseData(): array
10131038
],
10141039
false,
10151040
ArrayShapeNode::KIND_LIST,
1016-
null,
1017-
new IdentifierTypeNode('string')
1041+
new ArrayShapeUnsealedTypeNode(
1042+
new IdentifierTypeNode('string'),
1043+
null
1044+
)
10181045
),
10191046
],
10201047
[
@@ -1034,8 +1061,10 @@ public function provideParseData(): array
10341061
],
10351062
false,
10361063
ArrayShapeNode::KIND_LIST,
1037-
null,
1038-
new IdentifierTypeNode('string')
1064+
new ArrayShapeUnsealedTypeNode(
1065+
new IdentifierTypeNode('string'),
1066+
null
1067+
)
10391068
),
10401069
],
10411070
[
@@ -1060,8 +1089,10 @@ public function provideParseData(): array
10601089
],
10611090
false,
10621091
ArrayShapeNode::KIND_LIST,
1063-
null,
1064-
new IdentifierTypeNode('string')
1092+
new ArrayShapeUnsealedTypeNode(
1093+
new IdentifierTypeNode('string'),
1094+
null
1095+
)
10651096
),
10661097
],
10671098
[

0 commit comments

Comments
 (0)
Please sign in to comment.