|
7 | 7 | use PHPStan\Analyser\Scope; |
8 | 8 | use PHPStan\DependencyInjection\AutowiredService; |
9 | 9 | use PHPStan\Node\Expr\TypeExpr; |
| 10 | +use PHPStan\Parser\ArrayMapArgVisitor; |
10 | 11 | use PHPStan\Reflection\FunctionReflection; |
| 12 | +use PHPStan\TrinaryLogic; |
11 | 13 | use PHPStan\Type\Accessory\AccessoryArrayListType; |
12 | 14 | use PHPStan\Type\Accessory\AccessoryType; |
13 | 15 | use PHPStan\Type\Accessory\HasOffsetValueType; |
14 | 16 | use PHPStan\Type\Accessory\NonEmptyArrayType; |
15 | 17 | use PHPStan\Type\ArrayType; |
| 18 | +use PHPStan\Type\Constant\ConstantArrayType; |
16 | 19 | use PHPStan\Type\Constant\ConstantArrayTypeBuilder; |
17 | 20 | use PHPStan\Type\Constant\ConstantIntegerType; |
18 | 21 | use PHPStan\Type\DynamicFunctionReturnTypeExtension; |
@@ -128,9 +131,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, |
128 | 131 | if (count($constantArrays) > 0) { |
129 | 132 | $totalCount = TypeCombinator::countConstantArrayValueTypes($constantArrays) * TypeCombinator::countConstantArrayValueTypes([$valueType]); |
130 | 133 | if ($totalCount < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) { |
131 | | - $mappedArrayType = $arrayType->mapValueType(static fn (Type $type): Type => $scope->getType(new FuncCall($callback, [ |
132 | | - new Node\Arg(new TypeExpr($type)), |
133 | | - ]))); |
| 134 | + $mappedArrayType = $arrayType->mapValueType(static fn (Type $type): Type => self::resolveCallbackReturnType($scope, $callback, $type)); |
134 | 135 | } else { |
135 | 136 | $mappedArrayType = TypeCombinator::intersect(new ArrayType( |
136 | 137 | $arrayType->getIterableKeyType(), |
@@ -162,6 +163,26 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, |
162 | 163 | return $mappedArrayType; |
163 | 164 | } |
164 | 165 |
|
| 166 | + private static function resolveCallbackReturnType(Scope $scope, Node\Expr $callback, Type $argType): Type |
| 167 | + { |
| 168 | + if ($callback instanceof Node\Expr\Closure || $callback instanceof Node\Expr\ArrowFunction) { |
| 169 | + $clone = clone $callback; |
| 170 | + $wrappedType = new ConstantArrayType( |
| 171 | + [new ConstantIntegerType(0)], |
| 172 | + [$argType], |
| 173 | + isList: TrinaryLogic::createYes(), |
| 174 | + ); |
| 175 | + $clone->setAttribute(ArrayMapArgVisitor::ATTRIBUTE_NAME, [new Node\Arg(new TypeExpr($wrappedType))]); |
| 176 | + $clone->setAttribute('phpstanCachedTypes', []); |
| 177 | + |
| 178 | + return $scope->getType($clone)->getCallableParametersAcceptors($scope)[0]->getReturnType(); |
| 179 | + } |
| 180 | + |
| 181 | + return $scope->getType(new FuncCall($callback, [ |
| 182 | + new Node\Arg(new TypeExpr($argType)), |
| 183 | + ])); |
| 184 | + } |
| 185 | + |
165 | 186 | /** |
166 | 187 | * @return list<AccessoryType> |
167 | 188 | */ |
|
0 commit comments