Skip to content

Commit 49f87ce

Browse files
committed
Revert "Improve closure parameter in-call type inference"
This reverts commit 5cebee3.
1 parent 5cebee3 commit 49f87ce

File tree

8 files changed

+91
-72
lines changed

8 files changed

+91
-72
lines changed

src/Analyser/MutatingScope.php

Lines changed: 19 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,25 @@ private function resolveType(string $exprString, Expr $node): Type
12071207
}
12081208
}
12091209

1210+
foreach ($node->params as $i => $param) {
1211+
if ($param->variadic) {
1212+
$isVariadic = true;
1213+
}
1214+
if (!$param->var instanceof Variable || !is_string($param->var->name)) {
1215+
throw new ShouldNotHappenException();
1216+
}
1217+
$parameters[] = new NativeParameterReflection(
1218+
$param->var->name,
1219+
$firstOptionalParameterIndex !== null && $i >= $firstOptionalParameterIndex,
1220+
$this->getFunctionType($param->type, $this->isParameterValueNullable($param), false),
1221+
$param->byRef
1222+
? PassedByReference::createCreatesNewVariable()
1223+
: PassedByReference::createNo(),
1224+
$param->variadic,
1225+
$param->default !== null ? $this->getType($param->default) : null,
1226+
);
1227+
}
1228+
12101229
$callableParameters = null;
12111230
$arrayMapArgs = $node->getAttribute(ArrayMapArgVisitor::ATTRIBUTE_NAME);
12121231
if ($arrayMapArgs !== null) {
@@ -1226,28 +1245,6 @@ private function resolveType(string $exprString, Expr $node): Type
12261245

12271246
if ($node instanceof Expr\ArrowFunction) {
12281247
$arrowScope = $this->enterArrowFunctionWithoutReflection($node, $callableParameters);
1229-
foreach ($node->params as $i => $param) {
1230-
if ($param->variadic) {
1231-
$isVariadic = true;
1232-
}
1233-
if (!$param->var instanceof Variable || !is_string($param->var->name)) {
1234-
throw new ShouldNotHappenException();
1235-
}
1236-
$parameterType = $arrowScope->getType($param->var);
1237-
if ($param->variadic) {
1238-
$parameterType = $parameterType->getIterableValueType();
1239-
}
1240-
$parameters[] = new NativeParameterReflection(
1241-
$param->var->name,
1242-
$firstOptionalParameterIndex !== null && $i >= $firstOptionalParameterIndex,
1243-
$parameterType,
1244-
$param->byRef
1245-
? PassedByReference::createCreatesNewVariable()
1246-
: PassedByReference::createNo(),
1247-
$param->variadic,
1248-
$param->default !== null ? $this->getType($param->default) : null,
1249-
);
1250-
}
12511248

12521249
if ($node->expr instanceof Expr\Yield_ || $node->expr instanceof Expr\YieldFrom) {
12531250
$yieldNode = $node->expr;
@@ -1319,28 +1316,6 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu
13191316
$usedVariables = [];
13201317
} else {
13211318
$closureScope = $this->enterAnonymousFunctionWithoutReflection($node, $callableParameters);
1322-
foreach ($node->params as $i => $param) {
1323-
if ($param->variadic) {
1324-
$isVariadic = true;
1325-
}
1326-
if (!$param->var instanceof Variable || !is_string($param->var->name)) {
1327-
throw new ShouldNotHappenException();
1328-
}
1329-
$parameterType = $closureScope->getType($param->var);
1330-
if ($param->variadic) {
1331-
$parameterType = $parameterType->getIterableValueType();
1332-
}
1333-
$parameters[] = new NativeParameterReflection(
1334-
$param->var->name,
1335-
$firstOptionalParameterIndex !== null && $i >= $firstOptionalParameterIndex,
1336-
$parameterType,
1337-
$param->byRef
1338-
? PassedByReference::createCreatesNewVariable()
1339-
: PassedByReference::createNo(),
1340-
$param->variadic,
1341-
$param->default !== null ? $this->getType($param->default) : null,
1342-
);
1343-
}
13441319
$closureReturnStatements = [];
13451320
$closureYieldStatements = [];
13461321
$closureExecutionEnds = [];

tests/PHPStan/Analyser/TestClosureTypeRuleTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ public function testRule(): void
2020
{
2121
$this->analyse([__DIR__ . '/data/closure-passed-to-type.php'], [
2222
[
23-
'Closure type: Closure(1|2|3): (1|2|3)',
23+
'Closure type: Closure(mixed): (1|2|3)',
2424
25,
2525
],
2626
[
27-
'Closure type: Closure(1|2|3): (1|2|3)',
27+
'Closure type: Closure(mixed): (1|2|3)',
2828
35,
2929
],
3030
]);

tests/PHPStan/Analyser/data/bug-3158.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ function (): void {
5454
assertType(A::class, $proxy);
5555

5656
$proxy = createProxy2(function(A $a, B $o):void {});
57-
// assertType(B::class, $proxy);
58-
assertType('Bug3158\B&T of object (function Bug3158\createProxy2(), parameter)', $proxy);
57+
assertType(B::class, $proxy);
5958
};
6059

6160
function (): void {

tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ public function testCallToFunctionWithoutParameters(): void
3939
public function testCallToFunctionWithIncorrectParameters(): void
4040
{
4141
$setErrorHandlerError = PHP_VERSION_ID < 80000
42-
? 'Parameter #1 $callback of function set_error_handler expects (callable(int, string, string, int, array): bool)|null, Closure(int, string, string, int): void given.'
43-
: 'Parameter #1 $callback of function set_error_handler expects (callable(int, string, string, int): bool)|null, Closure(int, string, string, int): void given.';
42+
? 'Parameter #1 $callback of function set_error_handler expects (callable(int, string, string, int, array): bool)|null, Closure(mixed, mixed, mixed, mixed): void given.'
43+
: 'Parameter #1 $callback of function set_error_handler expects (callable(int, string, string, int): bool)|null, Closure(mixed, mixed, mixed, mixed): void given.';
4444

4545
require_once __DIR__ . '/data/incorrect-call-to-function-definition.php';
4646
$this->analyse([__DIR__ . '/data/incorrect-call-to-function.php'], [
@@ -558,14 +558,14 @@ public function testArrayReduceCallback(): void
558558
5,
559559
],
560560
[
561-
'Parameter #2 $callback of function array_reduce expects callable(non-empty-string|null, 1|2|3): (non-empty-string|null), Closure(non-empty-string, 1|2|3): non-falsy-string given.',
561+
'Parameter #2 $callback of function array_reduce expects callable(non-empty-string|null, 1|2|3): (non-empty-string|null), Closure(string, int): non-falsy-string given.',
562562
13,
563-
'Type non-empty-string of parameter #1 $foo of passed callable needs to be same or wider than parameter type non-empty-string|null of accepting callable.',
563+
'Type string of parameter #1 $foo of passed callable needs to be same or wider than parameter type string|null of accepting callable.',
564564
],
565565
[
566-
'Parameter #2 $callback of function array_reduce expects callable(non-empty-string|null, 1|2|3): (non-empty-string|null), Closure(non-empty-string, 1|2|3): non-falsy-string given.',
566+
'Parameter #2 $callback of function array_reduce expects callable(non-empty-string|null, 1|2|3): (non-empty-string|null), Closure(string, int): non-falsy-string given.',
567567
22,
568-
'Type non-empty-string of parameter #1 $foo of passed callable needs to be same or wider than parameter type non-empty-string|null of accepting callable.',
568+
'Type string of parameter #1 $foo of passed callable needs to be same or wider than parameter type string|null of accepting callable.',
569569
],
570570
]);
571571
}
@@ -578,14 +578,14 @@ public function testArrayReduceArrowFunctionCallback(): void
578578
5,
579579
],
580580
[
581-
'Parameter #2 $callback of function array_reduce expects callable(non-empty-string|null, 1|2|3): (non-empty-string|null), Closure(non-empty-string, 1|2|3): non-falsy-string given.',
581+
'Parameter #2 $callback of function array_reduce expects callable(non-empty-string|null, 1|2|3): (non-empty-string|null), Closure(string, int): non-falsy-string given.',
582582
11,
583-
'Type non-empty-string of parameter #1 $foo of passed callable needs to be same or wider than parameter type non-empty-string|null of accepting callable.',
583+
'Type string of parameter #1 $foo of passed callable needs to be same or wider than parameter type string|null of accepting callable.',
584584
],
585585
[
586-
'Parameter #2 $callback of function array_reduce expects callable(non-empty-string|null, 1|2|3): (non-empty-string|null), Closure(non-empty-string, 1|2|3): non-falsy-string given.',
586+
'Parameter #2 $callback of function array_reduce expects callable(non-empty-string|null, 1|2|3): (non-empty-string|null), Closure(string, int): non-falsy-string given.',
587587
18,
588-
'Type non-empty-string of parameter #1 $foo of passed callable needs to be same or wider than parameter type non-empty-string|null of accepting callable.',
588+
'Type string of parameter #1 $foo of passed callable needs to be same or wider than parameter type string|null of accepting callable.',
589589
],
590590
]);
591591
}
@@ -598,11 +598,11 @@ public function testArrayWalkCallback(): void
598598
6,
599599
],
600600
[
601-
'Parameter #2 $callback of function array_walk expects callable(1|2, \'bar\'|\'foo\', \'extra\'): mixed, Closure(1|2, \'bar\'|\'foo\', int): \'\' given.',
601+
'Parameter #2 $callback of function array_walk expects callable(1|2, \'bar\'|\'foo\', \'extra\'): mixed, Closure(int, string, int): \'\' given.',
602602
14,
603603
],
604604
[
605-
'Parameter #2 $callback of function array_walk expects callable(1|2, \'bar\'|\'foo\'): mixed, Closure(1|2, \'bar\'|\'foo\', int): \'\' given.',
605+
'Parameter #2 $callback of function array_walk expects callable(1|2, \'bar\'|\'foo\'): mixed, Closure(int, string, int): \'\' given.',
606606
23,
607607
'Parameter #3 $extra of passed callable is required but accepting callable does not have that parameter. It will be called without it.',
608608
],
@@ -617,11 +617,11 @@ public function testArrayWalkArrowFunctionCallback(): void
617617
6,
618618
],
619619
[
620-
'Parameter #2 $callback of function array_walk expects callable(1|2, \'bar\'|\'foo\', \'extra\'): mixed, Closure(1|2, \'bar\'|\'foo\', int): \'\' given.',
620+
'Parameter #2 $callback of function array_walk expects callable(1|2, \'bar\'|\'foo\', \'extra\'): mixed, Closure(int, string, int): \'\' given.',
621621
12,
622622
],
623623
[
624-
'Parameter #2 $callback of function array_walk expects callable(1|2, \'bar\'|\'foo\'): mixed, Closure(1|2, \'bar\'|\'foo\', int): \'\' given.',
624+
'Parameter #2 $callback of function array_walk expects callable(1|2, \'bar\'|\'foo\'): mixed, Closure(int, string, int): \'\' given.',
625625
19,
626626
'Parameter #3 $extra of passed callable is required but accepting callable does not have that parameter. It will be called without it.',
627627
],
@@ -636,7 +636,7 @@ public function testArrayUdiffCallback(): void
636636
6,
637637
],
638638
[
639-
'Parameter #3 $data_comp_func of function array_udiff expects callable(1|2|3|4|5|6, 1|2|3|4|5|6): int, Closure(1|2|3|4|5|6, 1|2|3|4|5|6): (literal-string&non-falsy-string) given.',
639+
'Parameter #3 $data_comp_func of function array_udiff expects callable(1|2|3|4|5|6, 1|2|3|4|5|6): int, Closure(int, int): (literal-string&non-falsy-string) given.',
640640
14,
641641
],
642642
[
@@ -666,7 +666,7 @@ public function testPregReplaceCallback(): void
666666
13,
667667
],
668668
[
669-
'Parameter #2 $callback of function preg_replace_callback expects callable(array<int|string, string>): string, Closure(array<int|string, string>): void given.',
669+
'Parameter #2 $callback of function preg_replace_callback expects callable(array<int|string, string>): string, Closure(array): void given.',
670670
20,
671671
],
672672
[
@@ -688,7 +688,7 @@ public function testMbEregReplaceCallback(): void
688688
13,
689689
],
690690
[
691-
'Parameter #2 $callback of function mb_ereg_replace_callback expects callable(array<int|string, string>): string, Closure(array<int|string, string>): void given.',
691+
'Parameter #2 $callback of function mb_ereg_replace_callback expects callable(array<int|string, string>): string, Closure(array): void given.',
692692
20,
693693
],
694694
[
@@ -850,7 +850,7 @@ public function testArrayMapMultiple(bool $checkExplicitMixed): void
850850
$this->checkExplicitMixed = $checkExplicitMixed;
851851
$this->analyse([__DIR__ . '/data/array_map_multiple.php'], [
852852
[
853-
'Parameter #1 $callback of function array_map expects (callable(1|2, \'bar\'|\'foo\'): mixed)|null, Closure(1|2, int): void given.',
853+
'Parameter #1 $callback of function array_map expects (callable(1|2, \'bar\'|\'foo\'): mixed)|null, Closure(int, int): void given.',
854854
58,
855855
],
856856
]);
@@ -1617,11 +1617,11 @@ public function testDiscussion10454(): void
16171617
{
16181618
$this->analyse([__DIR__ . '/data/discussion-10454.php'], [
16191619
[
1620-
"Parameter #2 \$callback of function array_filter expects (callable('bar'|'baz'|'foo'|'quux'|'qux'): bool)|null, Closure('bar'|'baz'|'foo'|'quux'|'qux'): stdClass given.",
1620+
"Parameter #2 \$callback of function array_filter expects (callable('bar'|'baz'|'foo'|'quux'|'qux'): bool)|null, Closure(string): stdClass given.",
16211621
13,
16221622
],
16231623
[
1624-
"Parameter #2 \$callback of function array_filter expects (callable('bar'|'baz'|'foo'|'quux'|'qux'): bool)|null, Closure('bar'|'baz'|'foo'|'quux'|'qux'): stdClass given.",
1624+
"Parameter #2 \$callback of function array_filter expects (callable('bar'|'baz'|'foo'|'quux'|'qux'): bool)|null, Closure(string): stdClass given.",
16251625
23,
16261626
],
16271627
]);

tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ public function testCallMethods(): void
504504
1512,
505505
],
506506
[
507-
'Parameter #1 $a of method Test\\CallableWithMixedArray::doBar() expects callable(array<string>): array<string>, Closure(array<string>): (array{\'foo\'}|null) given.',
507+
'Parameter #1 $a of method Test\\CallableWithMixedArray::doBar() expects callable(array<string>): array<string>, Closure(array): (array{\'foo\'}|null) given.',
508508
1533,
509509
],
510510
[
@@ -828,7 +828,7 @@ public function testCallMethodsOnThisOnly(): void
828828
1512,
829829
],
830830
[
831-
'Parameter #1 $a of method Test\\CallableWithMixedArray::doBar() expects callable(array<string>): array<string>, Closure(array<string>): (array{\'foo\'}|null) given.',
831+
'Parameter #1 $a of method Test\\CallableWithMixedArray::doBar() expects callable(array<string>): array<string>, Closure(array): (array{\'foo\'}|null) given.',
832832
1533,
833833
],
834834
[

tests/PHPStan/Rules/Methods/data/unable-to-resolve-callback-parameter-type.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ public function test(): void
5959
$cb = $this->callback(function (int $i): bool {
6060
return true;
6161
});
62-
// assertType(Callback::class . '<int>', $cb);
63-
assertType('UnableToResolveCallbackParameterType\Callback<CallbackInput (method UnableToResolveCallbackParameterType\Foo::callback(), parameter)&int>', $cb);
62+
assertType(Callback::class . '<int>', $cb);
6463
}
6564

6665
}

tests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,4 +608,10 @@ public function testUnset(): void
608608
]);
609609
}
610610

611+
public function testGenericsInCallableInConstructor(): void
612+
{
613+
$this->checkExplicitMixed = true;
614+
$this->analyse([__DIR__ . '/data/generics-in-callable-in-constructor.php'], []);
615+
}
616+
611617
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace GenericsInCallableInConstructor;
4+
5+
interface Node
6+
{
7+
8+
}
9+
10+
/**
11+
* @template T
12+
*/
13+
class Differ
14+
{
15+
16+
/**
17+
* Create differ over the given equality relation.
18+
*
19+
* @param callable(T, T): bool $isEqual Equality relation
20+
*/
21+
public function __construct(callable $isEqual)
22+
{
23+
}
24+
25+
}
26+
27+
class Foo
28+
{
29+
30+
/** @var Differ<Node> */
31+
private $differ;
32+
33+
public function doFoo(): void
34+
{
35+
$this->differ = new Differ(static function ($a, $b) {
36+
return false;
37+
});
38+
}
39+
40+
}

0 commit comments

Comments
 (0)