|
15 | 15 | use PHPStan\Analyser\Generator\GeneratorScope; |
16 | 16 | use PHPStan\Analyser\Generator\NodeCallbackRequest; |
17 | 17 | use PHPStan\Analyser\Generator\NodeHandler\ParamHandler; |
| 18 | +use PHPStan\Analyser\Generator\NoopNodeCallback; |
| 19 | +use PHPStan\Analyser\Generator\PersistStorageRequest; |
| 20 | +use PHPStan\Analyser\Generator\RestoreStorageRequest; |
18 | 21 | use PHPStan\Analyser\Generator\StmtAnalysisResult; |
19 | 22 | use PHPStan\Analyser\Generator\StmtsAnalysisRequest; |
20 | 23 | use PHPStan\Analyser\Generator\TypeExprRequest; |
|
29 | 32 | use PHPStan\Node\InvalidateExprNode; |
30 | 33 | use PHPStan\Node\PropertyAssignNode; |
31 | 34 | use PHPStan\Node\ReturnStatement; |
| 35 | +use PHPStan\Parser\ImmediatelyInvokedClosureVisitor; |
32 | 36 | use PHPStan\ShouldNotHappenException; |
33 | 37 | use PHPStan\TrinaryLogic; |
34 | 38 | use PHPStan\Type\ClosureType; |
@@ -85,7 +89,7 @@ public function analyseExpr( |
85 | 89 | return new ExprAnalysisResult( |
86 | 90 | $result->type, |
87 | 91 | $result->nativeType, |
88 | | - $result->scope, |
| 92 | + $closureScope, |
89 | 93 | hasYield: false, |
90 | 94 | isAlwaysTerminating: false, |
91 | 95 | throwPoints: [], |
@@ -170,14 +174,15 @@ public function processClosureNode( |
170 | 174 | } |
171 | 175 |
|
172 | 176 | $closureScope = $scope->enterAnonymousFunctionWithoutReflection($expr, $callableParameters); |
| 177 | + $closureScope = $closureScope->processClosureScope($scope, null, $byRefUses); |
173 | 178 | $gatheredReturnStatements = []; |
174 | 179 | $gatheredYieldStatements = []; |
175 | 180 | $onlyNeverExecutionEnds = null; |
176 | 181 | $closureImpurePoints = []; |
177 | 182 | $invalidateExpressions = []; |
178 | 183 | $executionEnds = []; |
179 | 184 |
|
180 | | - $closureStmtsCallback = static function (Node $node, Scope $scope, callable $nodeCallback) use ($closureScope, &$gatheredReturnStatements, &$gatheredYieldStatements, &$onlyNeverExecutionEnds, &$closureImpurePoints, &$invalidateExpressions, &$executionEnds): void { |
| 185 | + $closureStmtsCallback = static function (Node $node, Scope $scope, callable $nodeCallback) use (&$closureScope, &$gatheredReturnStatements, &$gatheredYieldStatements, &$onlyNeverExecutionEnds, &$closureImpurePoints, &$invalidateExpressions, &$executionEnds): void { |
181 | 186 | $nodeCallback($node, $scope); |
182 | 187 | if ($scope->getAnonymousFunctionReflection() !== $closureScope->getAnonymousFunctionReflection()) { |
183 | 188 | return; |
@@ -241,15 +246,44 @@ public function processClosureNode( |
241 | 246 | if (count($byRefUses) === 0) { |
242 | 247 | $closureStatementResult = yield new StmtsAnalysisRequest($expr->stmts, $closureScope, StatementContext::createTopLevel(), $closureStmtsCallback); |
243 | 248 | } else { |
244 | | - // todo |
245 | | - $closureStatementResultGen = $this->processClosureNodeAndStabilizeScope( |
246 | | - $expr, |
247 | | - $closureScope, |
248 | | - $closureStmtsCallback, |
249 | | - ); |
250 | | - yield from $closureStatementResultGen; |
251 | | - $closureStatementResult = $closureStatementResultGen->getReturn(); |
252 | | - $scope = $closureScope->processClosureScope($closureStatementResult->scope, $scope, $byRefUses); |
| 249 | + $count = 0; |
| 250 | + $closureResultScope = null; |
| 251 | + |
| 252 | + $storage = yield new PersistStorageRequest(); |
| 253 | + do { |
| 254 | + yield new RestoreStorageRequest($storage->duplicate()); |
| 255 | + $prevScope = $closureScope; |
| 256 | + |
| 257 | + $intermediaryClosureScopeResult = yield new StmtsAnalysisRequest($expr->stmts, $closureScope, StatementContext::createTopLevel(), new NoopNodeCallback()); |
| 258 | + $intermediaryClosureScope = $intermediaryClosureScopeResult->scope; |
| 259 | + foreach ($intermediaryClosureScopeResult->exitPoints as $exitPoint) { |
| 260 | + $intermediaryClosureScope = $intermediaryClosureScope->mergeWith($exitPoint->getScope()); |
| 261 | + } |
| 262 | + |
| 263 | + if ($expr->getAttribute(ImmediatelyInvokedClosureVisitor::ATTRIBUTE_NAME) === true) { |
| 264 | + $closureResultScope = $intermediaryClosureScope; |
| 265 | + break; |
| 266 | + } |
| 267 | + |
| 268 | + $closureScope = $scope->enterAnonymousFunctionWithoutReflection($expr, $callableParameters); |
| 269 | + $closureScope = $closureScope->processClosureScope($intermediaryClosureScope, $prevScope, $byRefUses); |
| 270 | + |
| 271 | + if ($closureScope->equals($prevScope)) { |
| 272 | + break; |
| 273 | + } |
| 274 | + if ($count >= 1) { |
| 275 | + $closureScope = $prevScope->generalizeWith($closureScope); |
| 276 | + } |
| 277 | + $count++; |
| 278 | + } while ($count < 3); |
| 279 | + |
| 280 | + if ($closureResultScope === null) { |
| 281 | + $closureResultScope = $closureScope; |
| 282 | + } |
| 283 | + |
| 284 | + yield new RestoreStorageRequest($storage->duplicate()); |
| 285 | + $closureStatementResult = yield new StmtsAnalysisRequest($expr->stmts, $closureScope, StatementContext::createTopLevel(), $closureStmtsCallback); |
| 286 | + $closureScope = $scope->processClosureScope($closureResultScope, null, $byRefUses); |
253 | 287 | } |
254 | 288 |
|
255 | 289 | $resultGen = $this->processClosureStatementResult( |
@@ -289,7 +323,7 @@ public function processClosureNode( |
289 | 323 | $alternativeNodeCallback, |
290 | 324 | ); |
291 | 325 |
|
292 | | - return $resultGen->getReturn(); |
| 326 | + return [$result, $closureScope]; |
293 | 327 | } |
294 | 328 |
|
295 | 329 | /** |
@@ -430,17 +464,4 @@ private function processClosureStatementResult( |
430 | 464 | ]; |
431 | 465 | } |
432 | 466 |
|
433 | | - /** |
434 | | - * @param callable(Node, Scope, callable(Node, Scope): void): void $closureStmtsCallback |
435 | | - * @return Generator<int, GeneratorTValueType, GeneratorTSendType, StmtAnalysisResult> |
436 | | - */ |
437 | | - private function processClosureNodeAndStabilizeScope( |
438 | | - Closure $expr, |
439 | | - GeneratorScope $closureScope, |
440 | | - callable $closureStmtsCallback, |
441 | | - ): Generator |
442 | | - { |
443 | | - return yield new StmtsAnalysisRequest($expr->stmts, $closureScope, StatementContext::createTopLevel(), $closureStmtsCallback); |
444 | | - } |
445 | | - |
446 | 467 | } |
0 commit comments