@@ -116,42 +116,21 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
116116 }
117117
118118 $ specifiedTypes = new SpecifiedTypes ();
119- if ($ context ->true ()) {
119+ $ narrowingValueType = $ this ->computeNeedleNarrowingType ($ context , $ needleType , $ arrayType , $ arrayValueType );
120+ if ($ narrowingValueType !== null ) {
120121 $ specifiedTypes = $ this ->typeSpecifier ->create (
121122 $ needleExpr ,
122- $ arrayValueType ,
123+ $ narrowingValueType ,
123124 $ context ,
124125 $ scope ,
125126 );
126127 if ($ needleExpr instanceof AlwaysRememberedExpr) {
127128 $ specifiedTypes = $ specifiedTypes ->unionWith ($ this ->typeSpecifier ->create (
128129 $ needleExpr ->getExpr (),
129- $ arrayValueType ,
130- $ context ,
131- $ scope ,
132- ));
133- }
134- } elseif (
135- $ context ->false ()
136- && count ($ needleType ->getFiniteTypes ()) > 0
137- && $ arrayType ->isIterableAtLeastOnce ()->yes ()
138- ) {
139- $ narrowingValueType = $ this ->computeGuaranteedValueType ($ arrayType , $ arrayValueType );
140- if (count ($ narrowingValueType ->getFiniteTypes ()) > 0 ) {
141- $ specifiedTypes = $ this ->typeSpecifier ->create (
142- $ needleExpr ,
143130 $ narrowingValueType ,
144131 $ context ,
145132 $ scope ,
146- );
147- if ($ needleExpr instanceof AlwaysRememberedExpr) {
148- $ specifiedTypes = $ specifiedTypes ->unionWith ($ this ->typeSpecifier ->create (
149- $ needleExpr ->getExpr (),
150- $ narrowingValueType ,
151- $ context ,
152- $ scope ,
153- ));
154- }
133+ ));
155134 }
156135 }
157136
@@ -188,6 +167,35 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
188167 return $ specifiedTypes ;
189168 }
190169
170+ /**
171+ * Computes the type to narrow the needle against, or null if no narrowing
172+ * should occur. In true context, returns the array value type directly.
173+ * In false context, returns only the values guaranteed to be in every
174+ * possible variant of the array.
175+ */
176+ private function computeNeedleNarrowingType (TypeSpecifierContext $ context , Type $ needleType , Type $ arrayType , Type $ arrayValueType ): ?Type
177+ {
178+ if ($ context ->true ()) {
179+ return $ arrayValueType ;
180+ }
181+
182+ if (
183+ !$ context ->false ()
184+ || count ($ needleType ->getFiniteTypes ()) === 0
185+ || !$ arrayType ->isIterableAtLeastOnce ()->yes ()
186+ ) {
187+ return null ;
188+ }
189+
190+ $ guaranteedValueType = $ this ->computeGuaranteedValueType ($ arrayType , $ arrayValueType );
191+
192+ if (count ($ guaranteedValueType ->getFiniteTypes ()) === 0 ) {
193+ return null ;
194+ }
195+
196+ return $ guaranteedValueType ;
197+ }
198+
191199 /**
192200 * Computes the type of values guaranteed to be in every possible variant
193201 * of the array. For union types like array{A}|array{B}, we intersect the
0 commit comments