@@ -56,6 +56,10 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
5656 $ keyType = $ arrayArgType ->getIterableKeyType ();
5757 $ itemType = $ arrayArgType ->getIterableValueType ();
5858
59+ if ($ itemType instanceof NeverType || $ keyType instanceof NeverType) {
60+ return new ConstantArrayType ([], []);
61+ }
62+
5963 if ($ arrayArgType instanceof MixedType) {
6064 return new BenevolentUnionType ([
6165 new ArrayType (new MixedType (), new MixedType ()),
@@ -73,52 +77,48 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
7377 if ($ callbackArg instanceof Closure && count ($ callbackArg ->stmts ) === 1 && count ($ callbackArg ->params ) > 0 ) {
7478 $ statement = $ callbackArg ->stmts [0 ];
7579 if ($ statement instanceof Return_ && $ statement ->expr !== null ) {
76- [ $ itemType , $ keyType ] = $ this ->filterByTruthyValue ($ scope , $ callbackArg ->params [0 ]->var , $ itemType , null , $ keyType , $ statement ->expr );
80+ return $ this ->filterByTruthyValue ($ scope , $ callbackArg ->params [0 ]->var , $ arrayArgType , null , $ statement ->expr );
7781 }
7882 } elseif ($ callbackArg instanceof ArrowFunction && count ($ callbackArg ->params ) > 0 ) {
79- [ $ itemType , $ keyType ] = $ this ->filterByTruthyValue ($ scope , $ callbackArg ->params [0 ]->var , $ itemType , null , $ keyType , $ callbackArg ->expr );
83+ return $ this ->filterByTruthyValue ($ scope , $ callbackArg ->params [0 ]->var , $ arrayArgType , null , $ callbackArg ->expr );
8084 } elseif ($ callbackArg instanceof String_) {
8185 $ itemVar = new Variable ('item ' );
8286 $ expr = new FuncCall (new Name ($ callbackArg ->value ), [new Arg ($ itemVar )]);
83- [ $ itemType , $ keyType ] = $ this ->filterByTruthyValue ($ scope , $ itemVar , $ itemType , null , $ keyType , $ expr );
87+ return $ this ->filterByTruthyValue ($ scope , $ itemVar , $ arrayArgType , null , $ expr );
8488 }
8589 }
8690
8791 if ($ flagArg instanceof ConstFetch && $ flagArg ->name ->parts [0 ] === 'ARRAY_FILTER_USE_KEY ' ) {
8892 if ($ callbackArg instanceof Closure && count ($ callbackArg ->stmts ) === 1 && count ($ callbackArg ->params ) > 0 ) {
8993 $ statement = $ callbackArg ->stmts [0 ];
9094 if ($ statement instanceof Return_ && $ statement ->expr !== null ) {
91- [ $ itemType , $ keyType ] = $ this ->filterByTruthyValue ($ scope , null , $ itemType , $ callbackArg ->params [0 ]->var , $ keyType , $ statement ->expr );
95+ return $ this ->filterByTruthyValue ($ scope , null , $ arrayArgType , $ callbackArg ->params [0 ]->var , $ statement ->expr );
9296 }
9397 } elseif ($ callbackArg instanceof ArrowFunction && count ($ callbackArg ->params ) > 0 ) {
94- [ $ itemType , $ keyType ] = $ this ->filterByTruthyValue ($ scope , null , $ itemType , $ callbackArg ->params [0 ]->var , $ keyType , $ callbackArg ->expr );
98+ return $ this ->filterByTruthyValue ($ scope , null , $ arrayArgType , $ callbackArg ->params [0 ]->var , $ callbackArg ->expr );
9599 } elseif ($ callbackArg instanceof String_) {
96100 $ keyVar = new Variable ('key ' );
97101 $ expr = new FuncCall (new Name ($ callbackArg ->value ), [new Arg ($ keyVar )]);
98- [ $ itemType , $ keyType ] = $ this ->filterByTruthyValue ($ scope , null , $ itemType , $ keyVar, $ keyType , $ expr );
102+ return $ this ->filterByTruthyValue ($ scope , null , $ arrayArgType , $ keyVar , $ expr );
99103 }
100104 }
101105
102106 if ($ flagArg instanceof ConstFetch && $ flagArg ->name ->parts [0 ] === 'ARRAY_FILTER_USE_BOTH ' ) {
103107 if ($ callbackArg instanceof Closure && count ($ callbackArg ->stmts ) === 1 && count ($ callbackArg ->params ) > 0 ) {
104108 $ statement = $ callbackArg ->stmts [0 ];
105109 if ($ statement instanceof Return_ && $ statement ->expr !== null ) {
106- [ $ itemType , $ keyType ] = $ this ->filterByTruthyValue ($ scope , $ callbackArg ->params [0 ]->var , $ itemType , $ callbackArg ->params [1 ]->var ?? null , $ keyType , $ statement ->expr );
110+ return $ this ->filterByTruthyValue ($ scope , $ callbackArg ->params [0 ]->var , $ arrayArgType , $ callbackArg ->params [1 ]->var ?? null , $ statement ->expr );
107111 }
108112 } elseif ($ callbackArg instanceof ArrowFunction && count ($ callbackArg ->params ) > 0 ) {
109- [ $ itemType , $ keyType ] = $ this ->filterByTruthyValue ($ scope , $ callbackArg ->params [0 ]->var , $ itemType , $ callbackArg ->params [1 ]->var ?? null , $ keyType , $ callbackArg ->expr );
113+ return $ this ->filterByTruthyValue ($ scope , $ callbackArg ->params [0 ]->var , $ arrayArgType , $ callbackArg ->params [1 ]->var ?? null , $ callbackArg ->expr );
110114 } elseif ($ callbackArg instanceof String_) {
111115 $ itemVar = new Variable ('item ' );
112116 $ keyVar = new Variable ('key ' );
113117 $ expr = new FuncCall (new Name ($ callbackArg ->value ), [new Arg ($ itemVar ), new Arg ($ keyVar )]);
114- [ $ itemType , $ keyType ] = $ this ->filterByTruthyValue ($ scope , $ itemVar , $ itemType , $ keyVar, $ keyType , $ expr );
118+ return $ this ->filterByTruthyValue ($ scope , $ itemVar , $ arrayArgType , $ keyVar , $ expr );
115119 }
116120 }
117121
118- if ($ itemType instanceof NeverType || $ keyType instanceof NeverType) {
119- return new ConstantArrayType ([], []);
120- }
121-
122122 return new ArrayType ($ keyType , $ itemType );
123123 }
124124
@@ -157,15 +157,51 @@ public function removeFalsey(Type $type): Type
157157 return new ArrayType ($ keyType , $ valueType );
158158 }
159159
160- /**
161- * @return array{Type, Type}
162- */
163- private function filterByTruthyValue (Scope $ scope , Error |Variable |null $ itemVar , Type $ itemType , Error |Variable |null $ keyVar , Type $ keyType , Expr $ expr ): array
160+ private function filterByTruthyValue (Scope $ scope , Error |Variable |null $ itemVar , Type $ arrayType , Error |Variable |null $ keyVar , Expr $ expr ): Type
164161 {
165162 if (!$ scope instanceof MutatingScope) {
166163 throw new ShouldNotHappenException ();
167164 }
168165
166+ $ constantArrays = TypeUtils::getOldConstantArrays ($ arrayType );
167+ if (count ($ constantArrays ) > 0 ) {
168+ $ results = [];
169+ foreach ($ constantArrays as $ constantArray ) {
170+ $ builder = ConstantArrayTypeBuilder::createEmpty ();
171+ foreach ($ constantArray ->getKeyTypes () as $ i => $ keyType ) {
172+ $ itemType = $ constantArray ->getValueTypes ()[$ i ];
173+ [$ newKeyType , $ newItemType ] = $ this ->processKeyAndItemType ($ scope , $ keyType , $ itemType , $ itemVar , $ keyVar , $ expr );
174+ if ($ newKeyType instanceof NeverType || $ newItemType instanceof NeverType) {
175+ continue ;
176+ }
177+ if ($ itemType ->equals ($ newItemType ) && $ keyType ->equals ($ newKeyType )) {
178+ $ builder ->setOffsetValueType ($ keyType , $ itemType );
179+ continue ;
180+ }
181+
182+ $ builder ->setOffsetValueType ($ newKeyType , $ newItemType , true );
183+ }
184+
185+ $ results [] = $ builder ->getArray ();
186+ }
187+
188+ return TypeCombinator::union (...$ results );
189+ }
190+
191+ [$ newKeyType , $ newItemType ] = $ this ->processKeyAndItemType ($ scope , $ arrayType ->getIterableKeyType (), $ arrayType ->getIterableValueType (), $ itemVar , $ keyVar , $ expr );
192+
193+ if ($ newItemType instanceof NeverType || $ newKeyType instanceof NeverType) {
194+ return new ConstantArrayType ([], []);
195+ }
196+
197+ return new ArrayType ($ newKeyType , $ newItemType );
198+ }
199+
200+ /**
201+ * @return array{Type, Type}
202+ */
203+ private function processKeyAndItemType (MutatingScope $ scope , Type $ keyType , Type $ itemType , Error |Variable |null $ itemVar , Error |Variable |null $ keyVar , Expr $ expr ): array
204+ {
169205 $ itemVarName = null ;
170206 if ($ itemVar !== null ) {
171207 if (!$ itemVar instanceof Variable || !is_string ($ itemVar ->name )) {
@@ -187,8 +223,8 @@ private function filterByTruthyValue(Scope $scope, Error|Variable|null $itemVar,
187223 $ scope = $ scope ->filterByTruthyValue ($ expr );
188224
189225 return [
190- $ itemVarName !== null ? $ scope ->getVariableType ($ itemVarName ) : $ itemType ,
191226 $ keyVarName !== null ? $ scope ->getVariableType ($ keyVarName ) : $ keyType ,
227+ $ itemVarName !== null ? $ scope ->getVariableType ($ itemVarName ) : $ itemType ,
192228 ];
193229 }
194230
0 commit comments