@@ -54,14 +54,17 @@ public function __construct(
5454 {
5555 }
5656
57+ /**
58+ * @return array{bool|null, list<string>}
59+ */
5760 public function findSpecifiedType (
5861 Scope $ scope ,
5962 Expr $ node ,
60- ): ? bool
63+ ): array
6164 {
6265 if ($ node instanceof FuncCall) {
6366 if ($ node ->isFirstClassCallable ()) {
64- return null ;
67+ return [ null , []] ;
6568 }
6669 $ argsCount = count ($ node ->getArgs ());
6770 if ($ node ->name instanceof Node \Name) {
@@ -70,34 +73,34 @@ public function findSpecifiedType(
7073 $ arg = $ node ->getArgs ()[0 ]->value ;
7174 $ assertValue = ($ this ->treatPhpDocTypesAsCertain ? $ scope ->getType ($ arg ) : $ scope ->getNativeType ($ arg ))->toBoolean ();
7275 if (!$ assertValue instanceof ConstantBooleanType) {
73- return null ;
76+ return [ null , []] ;
7477 }
7578
76- return $ assertValue ->getValue ();
79+ return [ $ assertValue ->getValue (), []] ;
7780 }
7881 if (in_array ($ functionName , [
7982 'class_exists ' ,
8083 'interface_exists ' ,
8184 'trait_exists ' ,
8285 'enum_exists ' ,
8386 ], true )) {
84- return null ;
87+ return [ null , []] ;
8588 }
8689 if (in_array ($ functionName , ['count ' , 'sizeof ' ], true )) {
87- return null ;
90+ return [ null , []] ;
8891 } elseif ($ functionName === 'defined ' ) {
89- return null ;
92+ return [ null , []] ;
9093 } elseif ($ functionName === 'array_search ' ) {
91- return null ;
94+ return [ null , []] ;
9295 } elseif ($ functionName === 'in_array ' && $ argsCount >= 2 ) {
9396 $ haystackArg = $ node ->getArgs ()[1 ]->value ;
9497 $ haystackType = ($ this ->treatPhpDocTypesAsCertain ? $ scope ->getType ($ haystackArg ) : $ scope ->getNativeType ($ haystackArg ));
9598 if ($ haystackType instanceof MixedType) {
96- return null ;
99+ return [ null , []] ;
97100 }
98101
99102 if (!$ haystackType ->isArray ()->yes ()) {
100- return null ;
103+ return [ null , []] ;
101104 }
102105
103106 $ needleArg = $ node ->getArgs ()[0 ]->value ;
@@ -114,7 +117,7 @@ public function findSpecifiedType(
114117 || $ haystackType ->getIterableValueType ()->isEnum ()->yes ();
115118
116119 if (!$ isStrictComparison ) {
117- return null ;
120+ return [ null , []] ;
118121 }
119122
120123 $ valueType = $ haystackType ->getIterableValueType ();
@@ -125,25 +128,25 @@ public function findSpecifiedType(
125128 if ($ haystackType ->isIterableAtLeastOnce ()->yes ()) {
126129 // In this case the generic implementation via typeSpecifier fails, because the argument types cannot be narrowed down.
127130 if ($ constantNeedleTypesCount === 1 && $ constantHaystackTypesCount === 1 ) {
128- if ($ isNeedleSupertype ->yes ()) {
129- return true ;
131+ if ($ isNeedleSupertype ->result -> yes ()) {
132+ return [ true , $ isNeedleSupertype -> reasons ] ;
130133 }
131- if ($ isNeedleSupertype ->no ()) {
132- return false ;
134+ if ($ isNeedleSupertype ->result -> no ()) {
135+ return [ false , $ isNeedleSupertype -> reasons ] ;
133136 }
134137 }
135138
136- return null ;
139+ return [ null , []] ;
137140 }
138141 }
139142
140143 if (!$ haystackType instanceof ConstantArrayType || count ($ haystackType ->getValueTypes ()) > 0 ) {
141144 $ haystackArrayTypes = $ haystackType ->getArrays ();
142145 if (count ($ haystackArrayTypes ) === 1 && $ haystackArrayTypes [0 ]->getIterableValueType () instanceof NeverType) {
143- return null ;
146+ return [ null , []] ;
144147 }
145148
146- if ($ isNeedleSupertype ->maybe () || $ isNeedleSupertype ->yes ()) {
149+ if ($ isNeedleSupertype ->result -> maybe () || $ isNeedleSupertype-> result ->yes ()) {
147150 foreach ($ haystackArrayTypes as $ haystackArrayType ) {
148151 if ($ haystackArrayType instanceof ConstantArrayType) {
149152 foreach ($ haystackArrayType ->getValueTypes () as $ i => $ haystackArrayValueType ) {
@@ -165,18 +168,18 @@ public function findSpecifiedType(
165168 }
166169 }
167170
168- return null ;
171+ return [ null , []] ;
169172 }
170173 }
171174
172- if ($ isNeedleSupertype ->yes ()) {
175+ if ($ isNeedleSupertype ->result -> yes ()) {
173176 $ hasConstantNeedleTypes = $ constantNeedleTypesCount > 0 ;
174177 $ hasConstantHaystackTypes = $ constantHaystackTypesCount > 0 ;
175178 if (
176179 (!$ hasConstantNeedleTypes && !$ hasConstantHaystackTypes )
177180 || $ hasConstantNeedleTypes !== $ hasConstantHaystackTypes
178181 ) {
179- return null ;
182+ return [ null , []] ;
180183 }
181184 }
182185 }
@@ -187,7 +190,7 @@ public function findSpecifiedType(
187190 if ($ objectType instanceof ConstantStringType
188191 && !$ this ->reflectionProvider ->hasClass ($ objectType ->getValue ())
189192 ) {
190- return false ;
193+ return [ false , []] ;
191194 }
192195
193196 $ methodArg = $ node ->getArgs ()[1 ]->value ;
@@ -200,11 +203,11 @@ public function findSpecifiedType(
200203
201204 if ($ objectType ->getObjectClassNames () !== []) {
202205 if ($ objectType ->hasMethod ($ methodType ->getValue ())->yes ()) {
203- return true ;
206+ return [ true , []] ;
204207 }
205208
206209 if ($ objectType ->hasMethod ($ methodType ->getValue ())->no ()) {
207- return false ;
210+ return [ false , []] ;
208211 }
209212 }
210213
@@ -220,15 +223,15 @@ public function findSpecifiedType(
220223
221224 if ($ genericType instanceof TypeWithClassName) {
222225 if ($ genericType ->hasMethod ($ methodType ->getValue ())->yes ()) {
223- return true ;
226+ return [ true , []] ;
224227 }
225228
226229 $ classReflection = $ genericType ->getClassReflection ();
227230 if (
228231 $ classReflection !== null
229232 && $ classReflection ->isFinal ()
230233 && $ genericType ->hasMethod ($ methodType ->getValue ())->no ()) {
231- return false ;
234+ return [ false , []] ;
232235 }
233236 }
234237 }
@@ -245,7 +248,7 @@ public function findSpecifiedType(
245248
246249 // don't validate types on overwrite
247250 if ($ specifiedTypes ->shouldOverwrite ()) {
248- return null ;
251+ return [ null , []] ;
249252 }
250253
251254 $ sureTypes = $ specifiedTypes ->getSureTypes ();
@@ -254,15 +257,15 @@ public function findSpecifiedType(
254257 $ rootExpr = $ specifiedTypes ->getRootExpr ();
255258 if ($ rootExpr !== null ) {
256259 if (self ::isSpecified ($ typeSpecifierScope , $ node , $ rootExpr )) {
257- return null ;
260+ return [ null , []] ;
258261 }
259262
260263 $ rootExprType = ($ this ->treatPhpDocTypesAsCertain ? $ scope ->getType ($ rootExpr ) : $ scope ->getNativeType ($ rootExpr ));
261264 if ($ rootExprType instanceof ConstantBooleanType) {
262- return $ rootExprType ->getValue ();
265+ return [ $ rootExprType ->getValue (), []] ;
263266 }
264267
265- return null ;
268+ return [ null , []] ;
266269 }
267270
268271 $ results = [];
@@ -282,7 +285,12 @@ public function findSpecifiedType(
282285 /** @var Type $resultType */
283286 $ resultType = $ sureType [1 ];
284287
285- $ results [] = $ resultType ->isSuperTypeOf ($ argumentType )->result ;
288+ $ isSuperType = $ resultType ->isSuperTypeOf ($ argumentType );
289+ if ($ isSuperType ->result ->no ()) {
290+ return [false , $ isSuperType ->reasons ];
291+ }
292+
293+ $ results [] = $ isSuperType ->result ;
286294 }
287295
288296 foreach ($ sureNotTypes as $ sureNotType ) {
@@ -300,15 +308,20 @@ public function findSpecifiedType(
300308 /** @var Type $resultType */
301309 $ resultType = $ sureNotType [1 ];
302310
303- $ results [] = $ resultType ->isSuperTypeOf ($ argumentType )->negate ()->result ;
311+ $ isSuperType = $ resultType ->isSuperTypeOf ($ argumentType );
312+ if ($ isSuperType ->result ->yes ()) {
313+ return [false , $ isSuperType ->reasons ];
314+ }
315+
316+ $ results [] = $ isSuperType ->result ->negate ();
304317 }
305318
306319 if (count ($ results ) === 0 ) {
307- return null ;
320+ return [ null , []] ;
308321 }
309322
310323 $ result = TrinaryLogic::createYes ()->and (...$ results );
311- return $ result ->maybe () ? null : $ result ->yes ();
324+ return $ result ->maybe () ? [ null , []] : [ $ result ->yes (), []] ;
312325 }
313326
314327 private static function isSpecified (Scope $ scope , Expr $ node , Expr $ expr ): bool
0 commit comments