Skip to content

Commit c71be49

Browse files
committed
Properly preserve TemplateConstantArrayType
1 parent 3c61234 commit c71be49

File tree

2 files changed

+57
-13
lines changed

2 files changed

+57
-13
lines changed

src/Type/Constant/ConstantArrayType.php

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,23 @@ public function __construct(
122122
$this->isList = $isList;
123123
}
124124

125+
/**
126+
* @param list<ConstantIntegerType|ConstantStringType> $keyTypes
127+
* @param array<int, Type> $valueTypes
128+
* @param non-empty-list<int> $nextAutoIndexes
129+
* @param int[] $optionalKeys
130+
*/
131+
protected function recreate(
132+
array $keyTypes,
133+
array $valueTypes,
134+
array $nextAutoIndexes = [0],
135+
array $optionalKeys = [],
136+
?TrinaryLogic $isList = null,
137+
): self
138+
{
139+
return new self($keyTypes, $valueTypes, $nextAutoIndexes, $optionalKeys, $isList);
140+
}
141+
125142
public function getConstantArrays(): array
126143
{
127144
return [$this];
@@ -765,7 +782,7 @@ public function unsetOffset(Type $offsetType, bool $preserveListCertainty = fals
765782
return new NeverType();
766783
}
767784

768-
return new self($newKeyTypes, $newValueTypes, $this->nextAutoIndexes, $newOptionalKeys, $newIsList);
785+
return $this->recreate($newKeyTypes, $newValueTypes, $this->nextAutoIndexes, $newOptionalKeys, $newIsList);
769786
}
770787

771788
return $this;
@@ -812,7 +829,7 @@ public function unsetOffset(Type $offsetType, bool $preserveListCertainty = fals
812829
return new NeverType();
813830
}
814831

815-
return new self($this->keyTypes, $this->valueTypes, $this->nextAutoIndexes, $optionalKeys, $newIsList);
832+
return $this->recreate($this->keyTypes, $this->valueTypes, $this->nextAutoIndexes, $optionalKeys, $newIsList);
816833
}
817834

818835
$optionalKeys = $this->optionalKeys;
@@ -842,7 +859,7 @@ public function unsetOffset(Type $offsetType, bool $preserveListCertainty = fals
842859
return new NeverType();
843860
}
844861

845-
return new self($this->keyTypes, $this->valueTypes, $this->nextAutoIndexes, $optionalKeys, $newIsList);
862+
return $this->recreate($this->keyTypes, $this->valueTypes, $this->nextAutoIndexes, $optionalKeys, $newIsList);
846863
}
847864

848865
/**
@@ -1066,7 +1083,7 @@ public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $pre
10661083

10671084
if ($length === 0 || ($offset < 0 && $length < 0 && $offset - $length >= 0)) {
10681085
// 0 / 0, 3 / 0 or e.g. -3 / -3 or -3 / -4 and so on never extract anything
1069-
return new self([], []);
1086+
return $this->recreate([], []);
10701087
}
10711088

10721089
if ($length < 0) {
@@ -1370,7 +1387,7 @@ private function removeLastElements(int $length): self
13701387
$optionalKeysRemoved--;
13711388
}
13721389

1373-
return new self(
1390+
return $this->recreate(
13741391
$keyTypes,
13751392
$valueTypes,
13761393
$nextAutoindexes,
@@ -1474,7 +1491,7 @@ public function generalizeValues(): self
14741491
$valueTypes[] = $valueType->generalize(GeneralizePrecision::lessSpecific());
14751492
}
14761493

1477-
return new self($this->keyTypes, $valueTypes, $this->nextAutoIndexes, $this->optionalKeys, $this->isList);
1494+
return $this->recreate($this->keyTypes, $valueTypes, $this->nextAutoIndexes, $this->optionalKeys, $this->isList);
14781495
}
14791496

14801497
private function degradeToGeneralArray(): Type
@@ -1522,7 +1539,7 @@ private function getKeysOrValuesArray(array $types): self
15221539
static fn (int $i): ConstantIntegerType => new ConstantIntegerType($i),
15231540
array_keys($types),
15241541
);
1525-
return new self($keyTypes, $types, $autoIndexes, $this->optionalKeys, TrinaryLogic::createYes());
1542+
return $this->recreate($keyTypes, $types, $autoIndexes, $this->optionalKeys, TrinaryLogic::createYes());
15261543
}
15271544

15281545
$keyTypes = [];
@@ -1551,7 +1568,7 @@ private function getKeysOrValuesArray(array $types): self
15511568
$maxIndex++;
15521569
}
15531570

1554-
return new self($keyTypes, $valueTypes, $autoIndexes, $optionalKeys, TrinaryLogic::createYes());
1571+
return $this->recreate($keyTypes, $valueTypes, $autoIndexes, $optionalKeys, TrinaryLogic::createYes());
15551572
}
15561573

15571574
public function describe(VerbosityLevel $level): string
@@ -1716,7 +1733,7 @@ public function traverse(callable $cb): Type
17161733
return $this;
17171734
}
17181735

1719-
return new self($this->keyTypes, $valueTypes, $this->nextAutoIndexes, $this->optionalKeys, $this->isList);
1736+
return $this->recreate($this->keyTypes, $valueTypes, $this->nextAutoIndexes, $this->optionalKeys, $this->isList);
17201737
}
17211738

17221739
public function traverseSimultaneously(Type $right, callable $cb): Type
@@ -1742,7 +1759,7 @@ public function traverseSimultaneously(Type $right, callable $cb): Type
17421759
return $this;
17431760
}
17441761

1745-
return new self($this->keyTypes, $valueTypes, $this->nextAutoIndexes, $this->optionalKeys, $this->isList);
1762+
return $this->recreate($this->keyTypes, $valueTypes, $this->nextAutoIndexes, $this->optionalKeys, $this->isList);
17461763
}
17471764

17481765
public function isKeysSupersetOf(self $otherArray): bool
@@ -1820,7 +1837,7 @@ public function mergeWith(self $otherArray): self
18201837
$nextAutoIndexes = array_values(array_unique(array_merge($this->nextAutoIndexes, $otherArray->nextAutoIndexes)));
18211838
sort($nextAutoIndexes);
18221839

1823-
return new self($this->keyTypes, $valueTypes, $nextAutoIndexes, $optionalKeys, $this->isList->and($otherArray->isList));
1840+
return $this->recreate($this->keyTypes, $valueTypes, $nextAutoIndexes, $optionalKeys, $this->isList->and($otherArray->isList));
18241841
}
18251842

18261843
/**
@@ -1874,7 +1891,7 @@ public function makeOffsetRequired(Type $offsetType): self
18741891
}
18751892

18761893
if (count($this->optionalKeys) !== count($optionalKeys)) {
1877-
return new self($this->keyTypes, $this->valueTypes, $this->nextAutoIndexes, array_values($optionalKeys), $this->isList);
1894+
return $this->recreate($this->keyTypes, $this->valueTypes, $this->nextAutoIndexes, array_values($optionalKeys), $this->isList);
18781895
}
18791896

18801897
break;
@@ -1893,7 +1910,7 @@ public function makeList(): Type
18931910
return new NeverType();
18941911
}
18951912

1896-
return new self($this->keyTypes, $this->valueTypes, $this->nextAutoIndexes, $this->optionalKeys, TrinaryLogic::createYes());
1913+
return $this->recreate($this->keyTypes, $this->valueTypes, $this->nextAutoIndexes, $this->optionalKeys, TrinaryLogic::createYes());
18971914
}
18981915

18991916
public function toPhpDocNode(): TypeNode

src/Type/Generic/TemplateConstantArrayType.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
namespace PHPStan\Type\Generic;
44

5+
use PHPStan\TrinaryLogic;
56
use PHPStan\Type\Constant\ConstantArrayType;
7+
use PHPStan\Type\Constant\ConstantIntegerType;
8+
use PHPStan\Type\Constant\ConstantStringType;
69
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
710
use PHPStan\Type\Type;
811

@@ -35,4 +38,28 @@ public function __construct(
3538
$this->default = $default;
3639
}
3740

41+
/**
42+
* @param list<ConstantIntegerType|ConstantStringType> $keyTypes
43+
* @param array<int, Type> $valueTypes
44+
* @param non-empty-list<int> $nextAutoIndexes
45+
* @param int[] $optionalKeys
46+
*/
47+
protected function recreate(
48+
array $keyTypes,
49+
array $valueTypes,
50+
array $nextAutoIndexes = [0],
51+
array $optionalKeys = [],
52+
?TrinaryLogic $isList = null,
53+
): ConstantArrayType
54+
{
55+
return new self(
56+
$this->scope,
57+
$this->strategy,
58+
$this->variance,
59+
$this->name,
60+
new ConstantArrayType($keyTypes, $valueTypes, $nextAutoIndexes, $optionalKeys, $isList),
61+
$this->default,
62+
);
63+
}
64+
3865
}

0 commit comments

Comments
 (0)