diff --git a/src/Analyser/ExprHandler/ClosureHandler.php b/src/Analyser/ExprHandler/ClosureHandler.php index b706a44dbe5..dc8e6916d06 100644 --- a/src/Analyser/ExprHandler/ClosureHandler.php +++ b/src/Analyser/ExprHandler/ClosureHandler.php @@ -36,7 +36,7 @@ public function supports(Expr $expr): bool public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Expr $expr, MutatingScope $scope, ExpressionResultStorage $storage, callable $nodeCallback, ExpressionContext $context): ExpressionResult { $processClosureResult = $nodeScopeResolver->processClosureNode($stmt, $expr, $scope, $storage, $nodeCallback, $context, null); - $scope = $processClosureResult->applyByRefUseScope($processClosureResult->getScope()); + $scope = $processClosureResult->applyByRefUseScope($processClosureResult->getScope(), null); return new ExpressionResult( $scope, diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index a43ba35dda5..7b7851601c5 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -3808,7 +3808,7 @@ public function processClosureScope( $variableType = $closureScope->getVariableType($variableName); - if ($prevScope !== null) { + if ($prevScope !== null && $prevScope->hasVariableType($variableName)->yes()) { $prevVariableType = $prevScope->getVariableType($variableName); if (!$variableType->equals($prevVariableType)) { $variableType = TypeCombinator::union($variableType, $prevVariableType); diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 001c8b5b368..550e0125923 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -3455,7 +3455,7 @@ public function processArgs( } foreach ($deferredByRefClosureResults as $deferredClosureResult) { - $scope = $deferredClosureResult->applyByRefUseScope($scope); + $scope = $deferredClosureResult->applyByRefUseScope($scope, $scope); } if ($parameters !== null) { diff --git a/src/Analyser/ProcessClosureResult.php b/src/Analyser/ProcessClosureResult.php index dd6cfa5d263..7f364ebd340 100644 --- a/src/Analyser/ProcessClosureResult.php +++ b/src/Analyser/ProcessClosureResult.php @@ -31,13 +31,13 @@ public function getScope(): MutatingScope return $this->scope; } - public function applyByRefUseScope(MutatingScope $scope): MutatingScope + public function applyByRefUseScope(MutatingScope $scope, ?MutatingScope $prevScope): MutatingScope { if ($this->byRefClosureResultScope === null) { return $scope; } - return $scope->processClosureScope($this->byRefClosureResultScope, null, $this->byRefUses); + return $scope->processClosureScope($this->byRefClosureResultScope, $prevScope, $this->byRefUses); } /** diff --git a/tests/PHPStan/Analyser/nsrt/bug-14096.php b/tests/PHPStan/Analyser/nsrt/bug-14096.php new file mode 100644 index 00000000000..5acb33e6127 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-14096.php @@ -0,0 +1,53 @@ +simulateAppCallback(static function (ServerRequestInterface $request) use ($createViewFx, &$view) { + $view = $createViewFx($request); + + return new App(); + }, static function () use ($simulateRequestFx, &$view) { + return $simulateRequestFx($view); + }); + + assertType('T of Bug14096\AbstractView (method Bug14096\Test::simulateViewCallback(), argument)|null', $view); + return $view; + } +}