8
8
use PHPStan \Analyser \TypeSpecifier ;
9
9
use PHPStan \Analyser \TypeSpecifierContext ;
10
10
use PHPStan \Type \ArrayType ;
11
+ use PHPStan \Type \Constant \ConstantArrayType ;
12
+ use PHPStan \Type \Constant \ConstantArrayTypeBuilder ;
11
13
use PHPStan \Type \Constant \ConstantStringType ;
12
14
use PHPStan \Type \IterableType ;
13
15
use PHPStan \Type \MixedType ;
14
16
use PHPStan \Type \ObjectType ;
15
17
use PHPStan \Type \Type ;
16
18
use PHPStan \Type \TypeCombinator ;
19
+ use PHPStan \Type \TypeUtils ;
17
20
18
21
class AssertHelper
19
22
{
@@ -92,7 +95,9 @@ public static function handleAll(
92
95
reset ($ sureTypes );
93
96
$ exprString = key ($ sureTypes );
94
97
$ sureType = $ sureTypes [$ exprString ];
95
- return self ::arrayOrIterable ($ typeSpecifier , $ scope , $ sureType [0 ], $ sureType [1 ]);
98
+ return self ::arrayOrIterable ($ typeSpecifier , $ scope , $ sureType [0 ], function () use ($ sureType ): Type {
99
+ return $ sureType [1 ];
100
+ });
96
101
}
97
102
if (count ($ specifiedTypes ->getSureNotTypes ()) > 0 ) {
98
103
throw new \PHPStan \ShouldNotHappenException ();
@@ -116,42 +121,38 @@ public static function handleAllNot(
116
121
): SpecifiedTypes
117
122
{
118
123
if ($ assertName === 'notNull ' ) {
119
- $ expr = $ args [0 ]->value ;
120
- $ currentType = $ scope ->getType ($ expr );
121
124
return self ::arrayOrIterable (
122
125
$ typeSpecifier ,
123
126
$ scope ,
124
- $ expr ,
125
- TypeCombinator::removeNull ($ currentType ->getIterableValueType ())
127
+ $ args [0 ]->value ,
128
+ function (Type $ type ): Type {
129
+ return TypeCombinator::removeNull ($ type );
130
+ }
126
131
);
127
132
} elseif ($ assertName === 'notIsInstanceOf ' ) {
128
133
$ classType = $ scope ->getType ($ args [1 ]->value );
129
134
if (!$ classType instanceof ConstantStringType) {
130
135
return new SpecifiedTypes ([], []);
131
136
}
132
137
133
- $ expr = $ args [0 ]->value ;
134
- $ currentType = $ scope ->getType ($ expr );
138
+ $ objectType = new ObjectType ($ classType ->getValue ());
135
139
return self ::arrayOrIterable (
136
140
$ typeSpecifier ,
137
141
$ scope ,
138
- $ expr ,
139
- TypeCombinator::remove (
140
- $ currentType ->getIterableValueType (),
141
- new ObjectType ($ classType ->getValue ())
142
- )
142
+ $ args [0 ]->value ,
143
+ function (Type $ type ) use ($ objectType ): Type {
144
+ return TypeCombinator::remove ($ type , $ objectType );
145
+ }
143
146
);
144
147
} elseif ($ assertName === 'notSame ' ) {
145
- $ expr = $ args [0 ]->value ;
146
- $ currentType = $ scope ->getType ($ expr );
148
+ $ valueType = $ scope ->getType ($ args [1 ]->value );
147
149
return self ::arrayOrIterable (
148
150
$ typeSpecifier ,
149
151
$ scope ,
150
- $ expr ,
151
- TypeCombinator::remove (
152
- $ currentType ->getIterableValueType (),
153
- $ scope ->getType ($ args [1 ]->value )
154
- )
152
+ $ args [0 ]->value ,
153
+ function (Type $ type ) use ($ valueType ): Type {
154
+ return TypeCombinator::remove ($ type , $ valueType );
155
+ }
155
156
);
156
157
}
157
158
@@ -162,14 +163,29 @@ private static function arrayOrIterable(
162
163
TypeSpecifier $ typeSpecifier ,
163
164
Scope $ scope ,
164
165
\PhpParser \Node \Expr $ expr ,
165
- Type $ type
166
+ \ Closure $ typeCallback
166
167
): SpecifiedTypes
167
168
{
168
169
$ currentType = $ scope ->getType ($ expr );
169
- if ((new ArrayType (new MixedType (), new MixedType ()))->isSuperTypeOf ($ currentType )->yes ()) {
170
- $ specifiedType = new ArrayType ($ currentType ->getIterableKeyType (), $ type );
170
+ $ arrayTypes = TypeUtils::getArrays ($ currentType );
171
+ if (count ($ arrayTypes ) > 0 ) {
172
+ $ newArrayTypes = [];
173
+ foreach ($ arrayTypes as $ arrayType ) {
174
+ if ($ arrayType instanceof ConstantArrayType) {
175
+ $ builder = ConstantArrayTypeBuilder::createEmpty ();
176
+ foreach ($ arrayType ->getKeyTypes () as $ i => $ keyType ) {
177
+ $ valueType = $ arrayType ->getValueTypes ()[$ i ];
178
+ $ builder ->setOffsetValueType ($ keyType , $ typeCallback ($ valueType ));
179
+ }
180
+ $ newArrayTypes [] = $ builder ->getArray ();
181
+ } else {
182
+ $ newArrayTypes [] = new ArrayType ($ arrayType ->getKeyType (), $ typeCallback ($ arrayType ->getItemType ()));
183
+ }
184
+ }
185
+
186
+ $ specifiedType = TypeCombinator::union (...$ newArrayTypes );
171
187
} elseif ((new IterableType (new MixedType (), new MixedType ()))->isSuperTypeOf ($ currentType )->yes ()) {
172
- $ specifiedType = new IterableType ($ currentType ->getIterableKeyType (), $ type );
188
+ $ specifiedType = new IterableType ($ currentType ->getIterableKeyType (), $ typeCallback ( $ currentType -> getIterableValueType ()) );
173
189
} else {
174
190
return new SpecifiedTypes ([], []);
175
191
}
0 commit comments