11
11
use PHPStan \Analyser \TypeSpecifierContext ;
12
12
use PHPStan \Reflection \MethodReflection ;
13
13
use PHPStan \Type \ArrayType ;
14
+ use PHPStan \Type \Constant \ConstantArrayType ;
15
+ use PHPStan \Type \Constant \ConstantArrayTypeBuilder ;
14
16
use PHPStan \Type \Constant \ConstantStringType ;
15
17
use PHPStan \Type \IterableType ;
16
18
use PHPStan \Type \MixedType ;
17
19
use PHPStan \Type \ObjectType ;
18
20
use PHPStan \Type \StaticMethodTypeSpecifyingExtension ;
19
21
use PHPStan \Type \Type ;
20
22
use PHPStan \Type \TypeCombinator ;
23
+ use PHPStan \Type \TypeUtils ;
21
24
22
25
class AssertTypeSpecifyingExtension implements StaticMethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
23
26
{
@@ -109,7 +112,13 @@ public function specifyTypes(
109
112
reset ($ sureTypes );
110
113
$ exprString = key ($ sureTypes );
111
114
$ sureType = $ sureTypes [$ exprString ];
112
- return $ this ->arrayOrIterable ($ scope , $ sureType [0 ], $ sureType [1 ]);
115
+ return $ this ->arrayOrIterable (
116
+ $ scope ,
117
+ $ sureType [0 ],
118
+ function () use ($ sureType ): Type {
119
+ return $ sureType [1 ];
120
+ }
121
+ );
113
122
}
114
123
if (count ($ specifiedTypes ->getSureNotTypes ()) > 0 ) {
115
124
throw new \PHPStan \ShouldNotHappenException ();
@@ -325,39 +334,35 @@ private function handleAllNot(
325
334
): SpecifiedTypes
326
335
{
327
336
if ($ methodName === 'allNotNull ' ) {
328
- $ expr = $ node ->args [0 ]->value ;
329
- $ currentType = $ scope ->getType ($ expr );
330
337
return $ this ->arrayOrIterable (
331
338
$ scope ,
332
- $ expr ,
333
- TypeCombinator::removeNull ($ currentType ->getIterableValueType ())
339
+ $ node ->args [0 ]->value ,
340
+ function (Type $ type ): Type {
341
+ return TypeCombinator::removeNull ($ type );
342
+ }
334
343
);
335
344
} elseif ($ methodName === 'allNotInstanceOf ' ) {
336
345
$ classType = $ scope ->getType ($ node ->args [1 ]->value );
337
346
if (!$ classType instanceof ConstantStringType) {
338
347
return new SpecifiedTypes ([], []);
339
348
}
340
349
341
- $ expr = $ node ->args [0 ]->value ;
342
- $ currentType = $ scope ->getType ($ expr );
350
+ $ objectType = new ObjectType ($ classType ->getValue ());
343
351
return $ this ->arrayOrIterable (
344
352
$ scope ,
345
- $ expr ,
346
- TypeCombinator::remove (
347
- $ currentType ->getIterableValueType (),
348
- new ObjectType ($ classType ->getValue ())
349
- )
353
+ $ node ->args [0 ]->value ,
354
+ function (Type $ type ) use ($ objectType ): Type {
355
+ return TypeCombinator::remove ($ type , $ objectType );
356
+ }
350
357
);
351
358
} elseif ($ methodName === 'allNotSame ' ) {
352
- $ expr = $ node ->args [0 ]->value ;
353
- $ currentType = $ scope ->getType ($ expr );
359
+ $ valueType = $ scope ->getType ($ node ->args [1 ]->value );
354
360
return $ this ->arrayOrIterable (
355
361
$ scope ,
356
- $ expr ,
357
- TypeCombinator::remove (
358
- $ currentType ->getIterableValueType (),
359
- $ scope ->getType ($ node ->args [1 ]->value )
360
- )
362
+ $ node ->args [0 ]->value ,
363
+ function (Type $ type ) use ($ valueType ): Type {
364
+ return TypeCombinator::remove ($ type , $ valueType );
365
+ }
361
366
);
362
367
}
363
368
@@ -367,14 +372,29 @@ private function handleAllNot(
367
372
private function arrayOrIterable (
368
373
Scope $ scope ,
369
374
\PhpParser \Node \Expr $ expr ,
370
- Type $ type
375
+ \ Closure $ typeCallback
371
376
): SpecifiedTypes
372
377
{
373
378
$ currentType = $ scope ->getType ($ expr );
374
- if ((new ArrayType (new MixedType (), new MixedType ()))->isSuperTypeOf ($ currentType )->yes ()) {
375
- $ specifiedType = new ArrayType ($ currentType ->getIterableKeyType (), $ type );
379
+ $ arrayTypes = TypeUtils::getArrays ($ currentType );
380
+ if (count ($ arrayTypes ) > 0 ) {
381
+ $ newArrayTypes = [];
382
+ foreach ($ arrayTypes as $ arrayType ) {
383
+ if ($ arrayType instanceof ConstantArrayType) {
384
+ $ builder = ConstantArrayTypeBuilder::createEmpty ();
385
+ foreach ($ arrayType ->getKeyTypes () as $ i => $ keyType ) {
386
+ $ valueType = $ arrayType ->getValueTypes ()[$ i ];
387
+ $ builder ->setOffsetValueType ($ keyType , $ typeCallback ($ valueType ));
388
+ }
389
+ $ newArrayTypes [] = $ builder ->getArray ();
390
+ } else {
391
+ $ newArrayTypes [] = new ArrayType ($ arrayType ->getKeyType (), $ typeCallback ($ arrayType ->getItemType ()));
392
+ }
393
+ }
394
+
395
+ $ specifiedType = TypeCombinator::union (...$ newArrayTypes );
376
396
} elseif ((new IterableType (new MixedType (), new MixedType ()))->isSuperTypeOf ($ currentType )->yes ()) {
377
- $ specifiedType = new IterableType ($ currentType ->getIterableKeyType (), $ type );
397
+ $ specifiedType = new IterableType ($ currentType ->getIterableKeyType (), $ typeCallback ( $ currentType -> getIterableValueType ()) );
378
398
} else {
379
399
return new SpecifiedTypes ([], []);
380
400
}
0 commit comments