1111use PHPStan \Analyser \TypeSpecifierContext ;
1212use PHPStan \Reflection \MethodReflection ;
1313use PHPStan \Type \ArrayType ;
14+ use PHPStan \Type \Constant \ConstantArrayType ;
15+ use PHPStan \Type \Constant \ConstantArrayTypeBuilder ;
1416use PHPStan \Type \Constant \ConstantStringType ;
1517use PHPStan \Type \IterableType ;
1618use PHPStan \Type \MixedType ;
1719use PHPStan \Type \ObjectType ;
1820use PHPStan \Type \StaticMethodTypeSpecifyingExtension ;
1921use PHPStan \Type \Type ;
2022use PHPStan \Type \TypeCombinator ;
23+ use PHPStan \Type \TypeUtils ;
2124
2225class AssertTypeSpecifyingExtension implements StaticMethodTypeSpecifyingExtension, TypeSpecifierAwareExtension
2326{
@@ -109,7 +112,13 @@ public function specifyTypes(
109112 reset ($ sureTypes );
110113 $ exprString = key ($ sureTypes );
111114 $ 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+ );
113122 }
114123 if (count ($ specifiedTypes ->getSureNotTypes ()) > 0 ) {
115124 throw new \PHPStan \ShouldNotHappenException ();
@@ -325,39 +334,35 @@ private function handleAllNot(
325334 ): SpecifiedTypes
326335 {
327336 if ($ methodName === 'allNotNull ' ) {
328- $ expr = $ node ->args [0 ]->value ;
329- $ currentType = $ scope ->getType ($ expr );
330337 return $ this ->arrayOrIterable (
331338 $ scope ,
332- $ expr ,
333- TypeCombinator::removeNull ($ currentType ->getIterableValueType ())
339+ $ node ->args [0 ]->value ,
340+ function (Type $ type ): Type {
341+ return TypeCombinator::removeNull ($ type );
342+ }
334343 );
335344 } elseif ($ methodName === 'allNotInstanceOf ' ) {
336345 $ classType = $ scope ->getType ($ node ->args [1 ]->value );
337346 if (!$ classType instanceof ConstantStringType) {
338347 return new SpecifiedTypes ([], []);
339348 }
340349
341- $ expr = $ node ->args [0 ]->value ;
342- $ currentType = $ scope ->getType ($ expr );
350+ $ objectType = new ObjectType ($ classType ->getValue ());
343351 return $ this ->arrayOrIterable (
344352 $ 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+ }
350357 );
351358 } elseif ($ methodName === 'allNotSame ' ) {
352- $ expr = $ node ->args [0 ]->value ;
353- $ currentType = $ scope ->getType ($ expr );
359+ $ valueType = $ scope ->getType ($ node ->args [1 ]->value );
354360 return $ this ->arrayOrIterable (
355361 $ 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+ }
361366 );
362367 }
363368
@@ -367,14 +372,29 @@ private function handleAllNot(
367372 private function arrayOrIterable (
368373 Scope $ scope ,
369374 \PhpParser \Node \Expr $ expr ,
370- Type $ type
375+ \ Closure $ typeCallback
371376 ): SpecifiedTypes
372377 {
373378 $ 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 );
376396 } 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 ()) );
378398 } else {
379399 return new SpecifiedTypes ([], []);
380400 }
0 commit comments