Skip to content

Commit 79b5ccd

Browse files
committed
CallableType and ClosureType - resolve template type from an unpassed argument to bounds
1 parent 01f99c1 commit 79b5ccd

File tree

4 files changed

+33
-7
lines changed

4 files changed

+33
-7
lines changed

src/Type/CallableType.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
use PHPStan\Reflection\ParameterReflection;
99
use PHPStan\Reflection\ParametersAcceptor;
1010
use PHPStan\TrinaryLogic;
11+
use PHPStan\Type\Generic\TemplateType;
12+
use PHPStan\Type\Generic\TemplateTypeHelper;
1113
use PHPStan\Type\Generic\TemplateTypeMap;
1214
use PHPStan\Type\Generic\TemplateTypeVariance;
1315
use PHPStan\Type\Traits\MaybeIterableTypeTrait;
@@ -232,21 +234,28 @@ public function inferTemplateTypes(Type $receivedType): TemplateTypeMap
232234
$typeMap = TemplateTypeMap::createEmpty();
233235

234236
foreach ($parametersAcceptors as $parametersAcceptor) {
235-
$typeMap = $typeMap->union($this->inferTemplateTypesOnParametersAcceptor($receivedType, $parametersAcceptor));
237+
$typeMap = $typeMap->union($this->inferTemplateTypesOnParametersAcceptor($parametersAcceptor));
236238
}
237239

238240
return $typeMap;
239241
}
240242

241-
private function inferTemplateTypesOnParametersAcceptor(Type $receivedType, ParametersAcceptor $parametersAcceptor): TemplateTypeMap
243+
private function inferTemplateTypesOnParametersAcceptor(ParametersAcceptor $parametersAcceptor): TemplateTypeMap
242244
{
243245
$typeMap = TemplateTypeMap::createEmpty();
244246
$args = $parametersAcceptor->getParameters();
245247
$returnType = $parametersAcceptor->getReturnType();
246248

247249
foreach ($this->getParameters() as $i => $param) {
248-
$argType = isset($args[$i]) ? $args[$i]->getType() : new NeverType();
249250
$paramType = $param->getType();
251+
if (isset($args[$i])) {
252+
$argType = $args[$i]->getType();
253+
} elseif ($paramType instanceof TemplateType) {
254+
$argType = TemplateTypeHelper::resolveToBounds($paramType);
255+
} else {
256+
$argType = new NeverType();
257+
}
258+
250259
$typeMap = $typeMap->union($paramType->inferTemplateTypes($argType)->convertToLowerBoundTypes());
251260
}
252261

src/Type/ClosureType.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
use PHPStan\Type\Constant\ConstantArrayType;
1919
use PHPStan\Type\Constant\ConstantBooleanType;
2020
use PHPStan\Type\Constant\ConstantIntegerType;
21+
use PHPStan\Type\Generic\TemplateType;
22+
use PHPStan\Type\Generic\TemplateTypeHelper;
2123
use PHPStan\Type\Generic\TemplateTypeMap;
2224
use PHPStan\Type\Traits\NonGenericTypeTrait;
2325
use PHPStan\Type\Traits\UndecidedComparisonTypeTrait;
@@ -347,21 +349,28 @@ public function inferTemplateTypes(Type $receivedType): TemplateTypeMap
347349
$typeMap = TemplateTypeMap::createEmpty();
348350

349351
foreach ($parametersAcceptors as $parametersAcceptor) {
350-
$typeMap = $typeMap->union($this->inferTemplateTypesOnParametersAcceptor($receivedType, $parametersAcceptor));
352+
$typeMap = $typeMap->union($this->inferTemplateTypesOnParametersAcceptor($parametersAcceptor));
351353
}
352354

353355
return $typeMap;
354356
}
355357

356-
private function inferTemplateTypesOnParametersAcceptor(Type $receivedType, ParametersAcceptor $parametersAcceptor): TemplateTypeMap
358+
private function inferTemplateTypesOnParametersAcceptor(ParametersAcceptor $parametersAcceptor): TemplateTypeMap
357359
{
358360
$typeMap = TemplateTypeMap::createEmpty();
359361
$args = $parametersAcceptor->getParameters();
360362
$returnType = $parametersAcceptor->getReturnType();
361363

362364
foreach ($this->getParameters() as $i => $param) {
363-
$argType = isset($args[$i]) ? $args[$i]->getType() : new NeverType();
364365
$paramType = $param->getType();
366+
if (isset($args[$i])) {
367+
$argType = $args[$i]->getType();
368+
} elseif ($paramType instanceof TemplateType) {
369+
$argType = TemplateTypeHelper::resolveToBounds($paramType);
370+
} else {
371+
$argType = new NeverType();
372+
}
373+
365374
$typeMap = $typeMap->union($paramType->inferTemplateTypes($argType)->convertToLowerBoundTypes());
366375
}
367376

tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ public function testArrayWalkCallback(): void
577577
{
578578
$this->analyse([__DIR__ . '/data/array_walk.php'], [
579579
[
580-
'Parameter #2 $callback of function array_walk expects callable(int, string, TUser): mixed, Closure(stdClass, float): \'\' given.',
580+
'Parameter #2 $callback of function array_walk expects callable(int, string, mixed): mixed, Closure(stdClass, float): \'\' given.',
581581
6,
582582
],
583583
[

tests/PHPStan/Rules/Functions/data/array_walk.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,11 @@ function(int $in, string $key, int $extra): string {
1616
},
1717
'extra'
1818
);
19+
20+
$array = ['foo' => 1, 'bar' => 2];
21+
array_walk(
22+
$array,
23+
function(int $value, string $key, int $extra): string {
24+
return '';
25+
}
26+
);

0 commit comments

Comments
 (0)