@@ -53,14 +53,17 @@ public function __construct(
5353 {
5454 }
5555
56+ /**
57+ * @return array{bool|null, list<string>}
58+ */
5659 public function findSpecifiedType (
5760 Scope $ scope ,
5861 Expr $ node ,
59- ): ? bool
62+ ): array
6063 {
6164 if ($ node instanceof FuncCall) {
6265 if ($ node ->isFirstClassCallable ()) {
63- return null ;
66+ return [ null , []] ;
6467 }
6568 $ argsCount = count ($ node ->getArgs ());
6669 if ($ node ->name instanceof Node \Name) {
@@ -69,34 +72,34 @@ public function findSpecifiedType(
6972 $ arg = $ node ->getArgs ()[0 ]->value ;
7073 $ assertValue = ($ this ->treatPhpDocTypesAsCertain ? $ scope ->getType ($ arg ) : $ scope ->getNativeType ($ arg ))->toBoolean ();
7174 if (!$ assertValue instanceof ConstantBooleanType) {
72- return null ;
75+ return [ null , []] ;
7376 }
7477
75- return $ assertValue ->getValue ();
78+ return [ $ assertValue ->getValue (), []] ;
7679 }
7780 if (in_array ($ functionName , [
7881 'class_exists ' ,
7982 'interface_exists ' ,
8083 'trait_exists ' ,
8184 'enum_exists ' ,
8285 ], true )) {
83- return null ;
86+ return [ null , []] ;
8487 }
8588 if (in_array ($ functionName , ['count ' , 'sizeof ' ], true )) {
86- return null ;
89+ return [ null , []] ;
8790 } elseif ($ functionName === 'defined ' ) {
88- return null ;
91+ return [ null , []] ;
8992 } elseif ($ functionName === 'array_search ' ) {
90- return null ;
93+ return [ null , []] ;
9194 } elseif ($ functionName === 'in_array ' && $ argsCount >= 2 ) {
9295 $ haystackArg = $ node ->getArgs ()[1 ]->value ;
9396 $ haystackType = ($ this ->treatPhpDocTypesAsCertain ? $ scope ->getType ($ haystackArg ) : $ scope ->getNativeType ($ haystackArg ));
9497 if ($ haystackType instanceof MixedType) {
95- return null ;
98+ return [ null , []] ;
9699 }
97100
98101 if (!$ haystackType ->isArray ()->yes ()) {
99- return null ;
102+ return [ null , []] ;
100103 }
101104
102105 $ needleArg = $ node ->getArgs ()[0 ]->value ;
@@ -113,36 +116,36 @@ public function findSpecifiedType(
113116 || $ haystackType ->getIterableValueType ()->isEnum ()->yes ();
114117
115118 if (!$ isStrictComparison ) {
116- return null ;
119+ return [ null , []] ;
117120 }
118121
119122 $ valueType = $ haystackType ->getIterableValueType ();
120123 $ constantNeedleTypesCount = count ($ needleType ->getFiniteTypes ());
121124 $ constantHaystackTypesCount = count ($ valueType ->getFiniteTypes ());
122- $ isNeedleSupertype = $ needleType ->isSuperTypeOf ($ valueType );
125+ $ isNeedleSupertype = $ needleType ->isSuperTypeOfWithReason ($ valueType );
123126 if ($ haystackType ->isConstantArray ()->no ()) {
124127 if ($ haystackType ->isIterableAtLeastOnce ()->yes ()) {
125128 // In this case the generic implementation via typeSpecifier fails, because the argument types cannot be narrowed down.
126129 if ($ constantNeedleTypesCount === 1 && $ constantHaystackTypesCount === 1 ) {
127- if ($ isNeedleSupertype ->yes ()) {
128- return true ;
130+ if ($ isNeedleSupertype ->result -> yes ()) {
131+ return [ true , $ isNeedleSupertype -> reasons ] ;
129132 }
130- if ($ isNeedleSupertype ->no ()) {
131- return false ;
133+ if ($ isNeedleSupertype ->result -> no ()) {
134+ return [ false , $ isNeedleSupertype -> reasons ] ;
132135 }
133136 }
134137
135- return null ;
138+ return [ null , []] ;
136139 }
137140 }
138141
139142 if (!$ haystackType instanceof ConstantArrayType || count ($ haystackType ->getValueTypes ()) > 0 ) {
140143 $ haystackArrayTypes = $ haystackType ->getArrays ();
141144 if (count ($ haystackArrayTypes ) === 1 && $ haystackArrayTypes [0 ]->getIterableValueType () instanceof NeverType) {
142- return null ;
145+ return [ null , []] ;
143146 }
144147
145- if ($ isNeedleSupertype ->maybe () || $ isNeedleSupertype ->yes ()) {
148+ if ($ isNeedleSupertype ->result -> maybe () || $ isNeedleSupertype-> result ->yes ()) {
146149 foreach ($ haystackArrayTypes as $ haystackArrayType ) {
147150 if ($ haystackArrayType instanceof ConstantArrayType) {
148151 foreach ($ haystackArrayType ->getValueTypes () as $ i => $ haystackArrayValueType ) {
@@ -164,18 +167,18 @@ public function findSpecifiedType(
164167 }
165168 }
166169
167- return null ;
170+ return [ null , []] ;
168171 }
169172 }
170173
171- if ($ isNeedleSupertype ->yes ()) {
174+ if ($ isNeedleSupertype ->result -> yes ()) {
172175 $ hasConstantNeedleTypes = $ constantNeedleTypesCount > 0 ;
173176 $ hasConstantHaystackTypes = $ constantHaystackTypesCount > 0 ;
174177 if (
175178 (!$ hasConstantNeedleTypes && !$ hasConstantHaystackTypes )
176179 || $ hasConstantNeedleTypes !== $ hasConstantHaystackTypes
177180 ) {
178- return null ;
181+ return [ null , []] ;
179182 }
180183 }
181184 }
@@ -186,7 +189,7 @@ public function findSpecifiedType(
186189 if ($ objectType instanceof ConstantStringType
187190 && !$ this ->reflectionProvider ->hasClass ($ objectType ->getValue ())
188191 ) {
189- return false ;
192+ return [ false , []] ;
190193 }
191194
192195 $ methodArg = $ node ->getArgs ()[1 ]->value ;
@@ -199,11 +202,11 @@ public function findSpecifiedType(
199202
200203 if ($ objectType ->getObjectClassNames () !== []) {
201204 if ($ objectType ->hasMethod ($ methodType ->getValue ())->yes ()) {
202- return true ;
205+ return [ true , []] ;
203206 }
204207
205208 if ($ objectType ->hasMethod ($ methodType ->getValue ())->no ()) {
206- return false ;
209+ return [ false , []] ;
207210 }
208211 }
209212
@@ -219,15 +222,15 @@ public function findSpecifiedType(
219222
220223 if ($ genericType instanceof TypeWithClassName) {
221224 if ($ genericType ->hasMethod ($ methodType ->getValue ())->yes ()) {
222- return true ;
225+ return [ true , []] ;
223226 }
224227
225228 $ classReflection = $ genericType ->getClassReflection ();
226229 if (
227230 $ classReflection !== null
228231 && $ classReflection ->isFinal ()
229232 && $ genericType ->hasMethod ($ methodType ->getValue ())->no ()) {
230- return false ;
233+ return [ false , []] ;
231234 }
232235 }
233236 }
@@ -240,7 +243,7 @@ public function findSpecifiedType(
240243
241244 // don't validate types on overwrite
242245 if ($ specifiedTypes ->shouldOverwrite ()) {
243- return null ;
246+ return [ null , []] ;
244247 }
245248
246249 $ sureTypes = $ specifiedTypes ->getSureTypes ();
@@ -249,15 +252,15 @@ public function findSpecifiedType(
249252 $ rootExpr = $ specifiedTypes ->getRootExpr ();
250253 if ($ rootExpr !== null ) {
251254 if (self ::isSpecified ($ typeSpecifierScope , $ node , $ rootExpr )) {
252- return null ;
255+ return [ null , []] ;
253256 }
254257
255258 $ rootExprType = ($ this ->treatPhpDocTypesAsCertain ? $ scope ->getType ($ rootExpr ) : $ scope ->getNativeType ($ rootExpr ));
256259 if ($ rootExprType instanceof ConstantBooleanType) {
257- return $ rootExprType ->getValue ();
260+ return [ $ rootExprType ->getValue (), []] ;
258261 }
259262
260- return null ;
263+ return [ null , []] ;
261264 }
262265
263266 $ results = [];
@@ -277,7 +280,12 @@ public function findSpecifiedType(
277280 /** @var Type $resultType */
278281 $ resultType = $ sureType [1 ];
279282
280- $ results [] = $ resultType ->isSuperTypeOf ($ argumentType );
283+ $ isSuperType = $ resultType ->isSuperTypeOfWithReason ($ argumentType );
284+ if ($ isSuperType ->result ->no ()) {
285+ return [false , $ isSuperType ->reasons ];
286+ }
287+
288+ $ results [] = $ isSuperType ->result ;
281289 }
282290
283291 foreach ($ sureNotTypes as $ sureNotType ) {
@@ -295,15 +303,20 @@ public function findSpecifiedType(
295303 /** @var Type $resultType */
296304 $ resultType = $ sureNotType [1 ];
297305
298- $ results [] = $ resultType ->isSuperTypeOf ($ argumentType )->negate ();
306+ $ isSuperType = $ resultType ->isSuperTypeOfWithReason ($ argumentType );
307+ if ($ isSuperType ->result ->yes ()) {
308+ return [false , $ isSuperType ->reasons ];
309+ }
310+
311+ $ results [] = $ isSuperType ->result ->negate ();
299312 }
300313
301314 if (count ($ results ) === 0 ) {
302- return null ;
315+ return [ null , []] ;
303316 }
304317
305318 $ result = TrinaryLogic::createYes ()->and (...$ results );
306- return $ result ->maybe () ? null : $ result ->yes ();
319+ return $ result ->maybe () ? [ null , []] : [ $ result ->yes (), []] ;
307320 }
308321
309322 private static function isSpecified (Scope $ scope , Expr $ node , Expr $ expr ): bool
0 commit comments