|
2 | 2 |
|
3 | 3 | namespace PHPStan\Type\Php;
|
4 | 4 |
|
| 5 | +use PhpParser\Node\Arg; |
5 | 6 | use PhpParser\Node\Expr\FuncCall;
|
| 7 | +use PhpParser\Node\Name; |
| 8 | +use PhpParser\Node\Scalar\String_; |
6 | 9 | use PHPStan\Analyser\Scope;
|
| 10 | +use PHPStan\Node\Expr\TypeExpr; |
| 11 | +use PHPStan\Parser\ArrayMapArgVisitor; |
7 | 12 | use PHPStan\Reflection\FunctionReflection;
|
8 | 13 | use PHPStan\Type\Accessory\AccessoryArrayListType;
|
9 | 14 | use PHPStan\Type\Accessory\NonEmptyArrayType;
|
10 | 15 | use PHPStan\Type\ArrayType;
|
| 16 | +use PHPStan\Type\ClosureType; |
11 | 17 | use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
|
12 | 18 | use PHPStan\Type\Constant\ConstantIntegerType;
|
| 19 | +use PHPStan\Type\Constant\ConstantStringType; |
13 | 20 | use PHPStan\Type\DynamicFunctionReturnTypeExtension;
|
14 | 21 | use PHPStan\Type\IntegerType;
|
15 | 22 | use PHPStan\Type\MixedType;
|
@@ -37,13 +44,27 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
|
37 | 44 | $singleArrayArgument = !isset($functionCall->getArgs()[2]);
|
38 | 45 | $callableType = $scope->getType($functionCall->getArgs()[0]->value);
|
39 | 46 | $callableIsNull = $callableType->isNull()->yes();
|
| 47 | + $callback = null; |
40 | 48 |
|
41 | 49 | if ($callableType->isCallable()->yes()) {
|
42 | 50 | $valueTypes = [new NeverType()];
|
43 | 51 | foreach ($callableType->getCallableParametersAcceptors($scope) as $parametersAcceptor) {
|
44 | 52 | $valueTypes[] = $parametersAcceptor->getReturnType();
|
45 | 53 | }
|
46 | 54 | $valueType = TypeCombinator::union(...$valueTypes);
|
| 55 | + $callback = $functionCall->getArgs()[0]->value; |
| 56 | + if ($callback instanceof String_) { |
| 57 | + /** @var non-falsy-string $callName */ |
| 58 | + $callName = $callback->value; |
| 59 | + $callback = new Name($callName); |
| 60 | + } elseif ($callback instanceof FuncCall && $callback->isFirstClassCallable() && |
| 61 | + $callback->getAttribute('phpstan_cache_printer') !== null && |
| 62 | + preg_match('/\A(?<name>\\\\?[^()]+)\(...\)\z/', $callback->getAttribute('phpstan_cache_printer'), $m) === 1 |
| 63 | + ) { |
| 64 | + /** @var non-falsy-string $callName */ |
| 65 | + $callName = $m['name']; |
| 66 | + $callback = new Name($callName); |
| 67 | + } |
47 | 68 | } elseif ($callableIsNull) {
|
48 | 69 | $arrayBuilder = ConstantArrayTypeBuilder::createEmpty();
|
49 | 70 | foreach (array_slice($functionCall->getArgs(), 1) as $index => $arg) {
|
@@ -73,7 +94,11 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
|
73 | 94 | foreach ($constantArray->getKeyTypes() as $i => $keyType) {
|
74 | 95 | $returnedArrayBuilder->setOffsetValueType(
|
75 | 96 | $keyType,
|
76 |
| - $valueType, |
| 97 | + $callback === null |
| 98 | + ? $valueType |
| 99 | + : $scope->getType(new FuncCall($callback, [ |
| 100 | + new Arg(new TypeExpr($constantArray->getValueTypes()[$i])), |
| 101 | + ])), |
77 | 102 | $constantArray->isOptionalKey($i),
|
78 | 103 | );
|
79 | 104 | }
|
|
0 commit comments