Skip to content

Commit 8bf26a5

Browse files
staabmondrejmirtes
authored andcommitted
Fix big constant arrays not recognized as oversized
1 parent 9334ff3 commit 8bf26a5

File tree

5 files changed

+295
-3
lines changed

5 files changed

+295
-3
lines changed

Diff for: src/PhpDoc/TypeNodeResolver.php

+3
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,9 @@ function (CallableTypeParameterNode $parameterNode) use ($nameScope, &$isVariadi
805805
private function resolveArrayShapeNode(ArrayShapeNode $typeNode, NameScope $nameScope): Type
806806
{
807807
$builder = ConstantArrayTypeBuilder::createEmpty();
808+
if (count($typeNode->items) > ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) {
809+
$builder->degradeToGeneralArray(true);
810+
}
808811

809812
foreach ($typeNode->items as $itemNode) {
810813
$offsetType = null;

Diff for: src/Type/Constant/ConstantArrayTypeBuilder.php

+11-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use PHPStan\ShouldNotHappenException;
66
use PHPStan\Type\Accessory\AccessoryArrayListType;
77
use PHPStan\Type\Accessory\NonEmptyArrayType;
8+
use PHPStan\Type\Accessory\OversizedArrayType;
89
use PHPStan\Type\ArrayType;
910
use PHPStan\Type\Type;
1011
use PHPStan\Type\TypeCombinator;
@@ -27,6 +28,8 @@ class ConstantArrayTypeBuilder
2728

2829
private bool $degradeToGeneralArray = false;
2930

31+
private bool $oversized = false;
32+
3033
/**
3134
* @param array<int, Type> $keyTypes
3235
* @param array<int, Type> $valueTypes
@@ -59,7 +62,7 @@ public static function createFromConstantArray(ConstantArrayType $startArrayType
5962
);
6063

6164
if (count($startArrayType->getKeyTypes()) > self::ARRAY_COUNT_LIMIT) {
62-
$builder->degradeToGeneralArray();
65+
$builder->degradeToGeneralArray(true);
6366
}
6467

6568
return $builder;
@@ -260,9 +263,10 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $opt
260263
$this->degradeToGeneralArray = true;
261264
}
262265

263-
public function degradeToGeneralArray(): void
266+
public function degradeToGeneralArray(bool $oversized = false): void
264267
{
265268
$this->degradeToGeneralArray = true;
269+
$this->oversized = $this->oversized || $oversized;
266270
}
267271

268272
public function getArray(): Type
@@ -286,6 +290,11 @@ public function getArray(): Type
286290
if (count($this->optionalKeys) < $keyTypesCount) {
287291
$array = TypeCombinator::intersect($array, new NonEmptyArrayType());
288292
}
293+
294+
if ($this->oversized) {
295+
$array = TypeCombinator::intersect($array, new OversizedArrayType());
296+
}
297+
289298
if ($this->isList) {
290299
$array = AccessoryArrayListType::intersectWith($array);
291300
}

Diff for: src/Type/ConstantTypeHelper.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public static function getTypeFromValue($value): Type
4646
} elseif (is_array($value)) {
4747
$arrayBuilder = ConstantArrayTypeBuilder::createEmpty();
4848
if (count($value) > ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) {
49-
$arrayBuilder->degradeToGeneralArray();
49+
$arrayBuilder->degradeToGeneralArray(true);
5050
}
5151
foreach ($value as $k => $v) {
5252
$arrayBuilder->setOffsetValueType(self::getTypeFromValue($k), self::getTypeFromValue($v));

Diff for: tests/PHPStan/Analyser/NodeScopeResolverTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,7 @@ public function dataFileAsserts(): iterable
11781178
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-3019.php');
11791179

11801180
yield from $this->gatherAssertTypes(__DIR__ . '/data/callsite-cast-narrowing.php');
1181+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8775.php');
11811182
}
11821183

11831184
/**

Diff for: tests/PHPStan/Analyser/data/bug-8775.php

+279
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
<?php
2+
3+
namespace Bug8775;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @phpstan-type Fixtures array{
9+
* '1': string,
10+
* '2': int,
11+
* '3': int,
12+
* '4': int,
13+
* '5': int,
14+
* '6': int,
15+
* '7': int,
16+
* '8': int,
17+
* '9': int,
18+
* '10': int,
19+
* '11': int,
20+
* '12': int,
21+
* '13': int,
22+
* '14': int,
23+
* '15': int,
24+
* '16': int,
25+
* '17': int,
26+
* '18': int,
27+
* '19': int,
28+
* '20': int,
29+
* '21': int,
30+
* '22': int,
31+
* '23': int,
32+
* '24': int,
33+
* '25': int,
34+
* '26': int,
35+
* '27': int,
36+
* '28': int,
37+
* '29': int,
38+
* '30': int,
39+
* '31': int,
40+
* '32': int,
41+
* '33': int,
42+
* '34': int,
43+
* '35': int,
44+
* '36': int,
45+
* '37': int,
46+
* '38': int,
47+
* '39': int,
48+
* '40': int,
49+
* '41': int,
50+
* '42': int,
51+
* '43': int,
52+
* '44': int,
53+
* '45': int,
54+
* '46': int,
55+
* '47': int,
56+
* '48': int,
57+
* '49': int,
58+
* '50': int,
59+
* '51': int,
60+
* '52': int,
61+
* '53': int,
62+
* '54': int,
63+
* '55': int,
64+
* '56': int,
65+
* '57': int,
66+
* '58': int,
67+
* '59': int,
68+
* '60': int,
69+
* '61': int,
70+
* '62': int,
71+
* '63': int,
72+
* '64': int,
73+
* '65': int,
74+
* '66': int,
75+
* '67': int,
76+
* '68': int,
77+
* '69': int,
78+
* '70': int,
79+
* '71': int,
80+
* '72': int,
81+
* '73': int,
82+
* '74': int,
83+
* '75': int,
84+
* '76': int,
85+
* '77': int,
86+
* '78': int,
87+
* '79': int,
88+
* '80': int,
89+
* '81': int,
90+
* '82': int,
91+
* '83': int,
92+
* '84': int,
93+
* '85': int,
94+
* '86': int,
95+
* '87': int,
96+
* '88': int,
97+
* '89': int,
98+
* '90': int,
99+
* '91': int,
100+
* '92': int,
101+
* '93': int,
102+
* '94': int,
103+
* '95': int,
104+
* '96': int,
105+
* '97': int,
106+
* '98': int,
107+
* '99': int,
108+
* '100': int,
109+
* '101': int,
110+
* '102': int,
111+
* '103': int,
112+
* '104': int,
113+
* '105': int,
114+
* '106': int,
115+
* '107': int,
116+
* '108': int,
117+
* '109': int,
118+
* '110': int,
119+
* '111': int,
120+
* '112': int,
121+
* '113': int,
122+
* '114': int,
123+
* '115': int,
124+
* '116': int,
125+
* '117': int,
126+
* '118': int,
127+
* '119': int,
128+
* '120': int,
129+
* '121': int,
130+
* '122': int,
131+
* '123': int,
132+
* '124': int,
133+
* '125': int,
134+
* '126': int,
135+
* '127': int,
136+
* '128': int,
137+
* '129': int,
138+
* '130': int,
139+
* '131': int,
140+
* '132': int,
141+
* '133': int,
142+
* '134': int,
143+
* '135': int,
144+
* '136': int,
145+
* '137': int,
146+
* '138': int,
147+
* '139': int,
148+
* '140': int,
149+
* '141': int,
150+
* '142': int,
151+
* '143': int,
152+
* '144': int,
153+
* '145': int,
154+
* '146': int,
155+
* '147': int,
156+
* '148': int,
157+
* '149': int,
158+
* '150': int,
159+
* '151': int,
160+
* '152': int,
161+
* '153': int,
162+
* '154': int,
163+
* '155': int,
164+
* '156': int,
165+
* '157': int,
166+
* '158': int,
167+
* '159': int,
168+
* '160': int,
169+
* '161': int,
170+
* '162': int,
171+
* '163': int,
172+
* '164': int,
173+
* '165': int,
174+
* '166': int,
175+
* '167': int,
176+
* '168': int,
177+
* '169': int,
178+
* '170': int,
179+
* '171': int,
180+
* '172': int,
181+
* '173': int,
182+
* '174': int,
183+
* '175': int,
184+
* '176': int,
185+
* '177': int,
186+
* '178': int,
187+
* '179': int,
188+
* '180': int,
189+
* '181': int,
190+
* '182': int,
191+
* '183': int,
192+
* '184': int,
193+
* '185': int,
194+
* '186': int,
195+
* '187': int,
196+
* '188': int,
197+
* '189': int,
198+
* '190': int,
199+
* '191': int,
200+
* '192': int,
201+
* '193': int,
202+
* '194': int,
203+
* '195': int,
204+
* '196': int,
205+
* '197': int,
206+
* '198': int,
207+
* '199': int,
208+
* '200': int,
209+
* '201': int,
210+
* '202': int,
211+
* '203': int,
212+
* '204': int,
213+
* '205': int,
214+
* '206': int,
215+
* '207': int,
216+
* '208': int,
217+
* '209': int,
218+
* '210': int,
219+
* '211': int,
220+
* '212': int,
221+
* '213': int,
222+
* '214': int,
223+
* '215': int,
224+
* '216': int,
225+
* '217': int,
226+
* '218': int,
227+
* '219': int,
228+
* '220': int,
229+
* '221': int,
230+
* '222': int,
231+
* '223': int,
232+
* '224': int,
233+
* '225': int,
234+
* '226': int,
235+
* '227': int,
236+
* '228': int,
237+
* '229': int,
238+
* '230': int,
239+
* '231': int,
240+
* '232': int,
241+
* '233': int,
242+
* '234': int,
243+
* '235': int,
244+
* '236': int,
245+
* '237': int,
246+
* '238': int,
247+
* '239': int,
248+
* '240': int,
249+
* '241': int,
250+
* '242': int,
251+
* '243': int,
252+
* '244': int,
253+
* '245': int,
254+
* '246': int,
255+
* '247': int,
256+
* '248': int,
257+
* '249': int,
258+
* '250': int,
259+
* '251': int,
260+
* '252': int,
261+
* '253': int,
262+
* '254': int,
263+
* '255': int,
264+
* '256': int,
265+
* '257': int,
266+
* }
267+
*/
268+
class Test
269+
{
270+
/**
271+
* @var Fixtures
272+
*/
273+
public static $fixtures;
274+
275+
function doFoo():void {
276+
assertType('(int|string)', Test::$fixtures['257']);
277+
}
278+
}
279+

0 commit comments

Comments
 (0)