Skip to content

Commit 5d58a8f

Browse files
committed
More precise types in immediately invoked callables
1 parent 5950910 commit 5d58a8f

File tree

3 files changed

+45
-11
lines changed

3 files changed

+45
-11
lines changed

src/Analyser/MutatingScope.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
use PHPStan\Node\Printer\ExprPrinter;
4747
use PHPStan\Node\PropertyAssignNode;
4848
use PHPStan\Parser\ArrayMapArgVisitor;
49-
use PHPStan\Parser\ImmediatelyInvokedClosureVisitor;
5049
use PHPStan\Parser\NewAssignedToPropertyVisitor;
5150
use PHPStan\Parser\Parser;
5251
use PHPStan\Php\PhpVersion;
@@ -4795,7 +4794,6 @@ private function processFinallyScopeVariableTypeHolders(
47954794
* @param Expr\ClosureUse[] $byRefUses
47964795
*/
47974796
public function processClosureScope(
4798-
Expr\Closure $expr,
47994797
self $closureScope,
48004798
?self $prevScope,
48014799
array $byRefUses,
@@ -4828,9 +4826,7 @@ public function processClosureScope(
48284826
$prevVariableType = $prevScope->getVariableType($variableName);
48294827
if (!$variableType->equals($prevVariableType)) {
48304828
$variableType = TypeCombinator::union($variableType, $prevVariableType);
4831-
if ($expr->getAttribute(ImmediatelyInvokedClosureVisitor::ATTRIBUTE_NAME) !== true) {
4832-
$variableType = self::generalizeType($variableType, $prevVariableType, 0);
4833-
}
4829+
$variableType = self::generalizeType($variableType, $prevVariableType, 0);
48344830
}
48354831
}
48364832

src/Analyser/NodeScopeResolver.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
use PHPStan\Node\VarTagChangedExpressionTypeNode;
120120
use PHPStan\Parser\ArrowFunctionArgVisitor;
121121
use PHPStan\Parser\ClosureArgVisitor;
122+
use PHPStan\Parser\ImmediatelyInvokedClosureVisitor;
122123
use PHPStan\Parser\Parser;
123124
use PHPStan\Php\PhpVersion;
124125
use PHPStan\PhpDoc\PhpDocInheritanceResolver;
@@ -4232,7 +4233,7 @@ private function processClosureNode(
42324233
}
42334234

42344235
$closureScope = $scope->enterAnonymousFunction($expr, $callableParameters);
4235-
$closureScope = $closureScope->processClosureScope($expr, $scope, null, $byRefUses);
4236+
$closureScope = $closureScope->processClosureScope($scope, null, $byRefUses);
42364237
$closureType = $closureScope->getAnonymousFunctionReflection();
42374238
if (!$closureType instanceof ClosureType) {
42384239
throw new ShouldNotHappenException();
@@ -4291,6 +4292,24 @@ private function processClosureNode(
42914292
return new ProcessClosureResult($scope, $statementResult->getThrowPoints(), $statementResult->getImpurePoints(), $invalidateExpressions);
42924293
}
42934294

4295+
if ($expr->getAttribute(ImmediatelyInvokedClosureVisitor::ATTRIBUTE_NAME) === true) {
4296+
$intermediaryClosureScopeResult = $this->processStmtNodes($expr, $expr->stmts, $closureScope, static function (): void {
4297+
}, StatementContext::createTopLevel());
4298+
$intermediaryClosureScope = $intermediaryClosureScopeResult->getScope();
4299+
4300+
$statementResult = $this->processStmtNodes($expr, $expr->stmts, $closureScope, $closureStmtsCallback, StatementContext::createTopLevel());
4301+
$nodeCallback(new ClosureReturnStatementsNode(
4302+
$expr,
4303+
$gatheredReturnStatements,
4304+
$gatheredYieldStatements,
4305+
$statementResult,
4306+
$executionEnds,
4307+
array_merge($statementResult->getImpurePoints(), $closureImpurePoints),
4308+
), $closureScope);
4309+
4310+
return new ProcessClosureResult($scope->processClosureScope($intermediaryClosureScope, null, $byRefUses), $statementResult->getThrowPoints(), $statementResult->getImpurePoints(), $invalidateExpressions);
4311+
}
4312+
42944313
$count = 0;
42954314
do {
42964315
$prevScope = $closureScope;
@@ -4302,7 +4321,7 @@ private function processClosureNode(
43024321
$intermediaryClosureScope = $intermediaryClosureScope->mergeWith($exitPoint->getScope());
43034322
}
43044323
$closureScope = $scope->enterAnonymousFunction($expr, $callableParameters);
4305-
$closureScope = $closureScope->processClosureScope($expr, $intermediaryClosureScope, $prevScope, $byRefUses);
4324+
$closureScope = $closureScope->processClosureScope($intermediaryClosureScope, $prevScope, $byRefUses);
43064325
if ($closureScope->equals($prevScope)) {
43074326
break;
43084327
}
@@ -4322,7 +4341,7 @@ private function processClosureNode(
43224341
array_merge($statementResult->getImpurePoints(), $closureImpurePoints),
43234342
), $closureScope);
43244343

4325-
return new ProcessClosureResult($scope->processClosureScope($expr, $closureScope, null, $byRefUses), $statementResult->getThrowPoints(), $statementResult->getImpurePoints(), $invalidateExpressions);
4344+
return new ProcessClosureResult($scope->processClosureScope($closureScope, null, $byRefUses), $statementResult->getThrowPoints(), $statementResult->getImpurePoints(), $invalidateExpressions);
43264345
}
43274346

43284347
/**

tests/PHPStan/Analyser/nsrt/bug-11561.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ function main(mixed $c): void{
1212
assertType('array{date: DateTime, id: 1}', $c);
1313

1414
$x = (function() use (&$c) {
15-
assertType("array{date: DateTime, id: 1, name?: 'ruud'}", $c);
15+
assertType("array{date: DateTime, id: 1}", $c);
1616
$c['name'] = 'ruud';
1717
assertType("array{date: DateTime, id: 1, name: 'ruud'}", $c);
1818
return 'x';
1919
})();
2020

21-
assertType("array{date: DateTime, id: 1, name?: 'ruud'}", $c);
21+
assertType("array{date: DateTime, id: 1, name: 'ruud'}", $c);
2222
}
2323

2424

@@ -30,11 +30,30 @@ function main2(mixed $c): void{
3030
assertType("array{date: DateTime, id: 1, name: 'staabm'}", $c);
3131

3232
$x = (function() use (&$c) {
33-
assertType("array{date: DateTime, id: 1, name: 'ruud'|'staabm'}", $c);
33+
assertType("array{date: DateTime, id: 1, name: 'staabm'}", $c);
3434
$c['name'] = 'ruud';
3535
assertType("array{date: DateTime, id: 1, name: 'ruud'}", $c);
3636
return 'x';
3737
})();
3838

39+
assertType("array{date: DateTime, id: 1, name: 'ruud'}", $c);
40+
}
41+
42+
/** @param array{date: DateTime} $c */
43+
function main3(mixed $c): void{
44+
assertType('array{date: DateTime}', $c);
45+
$c['id']=1;
46+
$c['name'] = 'staabm';
47+
assertType("array{date: DateTime, id: 1, name: 'staabm'}", $c);
48+
49+
$x = (function() use (&$c) {
50+
assertType("array{date: DateTime, id: 1, name: 'staabm'}", $c);
51+
if (rand(0,1)) {
52+
$c['name'] = 'ruud';
53+
}
54+
assertType("array{date: DateTime, id: 1, name: 'ruud'|'staabm'}", $c);
55+
return 'x';
56+
})();
57+
3958
assertType("array{date: DateTime, id: 1, name: 'ruud'|'staabm'}", $c);
4059
}

0 commit comments

Comments
 (0)