diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 79b05cd0f8..ac1c9b80ea 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -3330,7 +3330,11 @@ public function processArgs( } $this->callNodeCallbackWithExpression($nodeCallback, $arg->value, $scopeToPass, $storage, $context); - $closureResult = $this->processClosureNode($stmt, $arg->value, $scopeToPass, $storage, $nodeCallback, $context, $parameterType ?? null); + $closureScopeToPass = $scopeToPass; + foreach ($deferredByRefClosureResults as $deferredClosureResult) { + $closureScopeToPass = $deferredClosureResult->applyByRefUseScope($closureScopeToPass); + } + $closureResult = $this->processClosureNode($stmt, $arg->value, $closureScopeToPass, $storage, $nodeCallback, $context, $parameterType ?? null); if ($this->callCallbackImmediately($parameter, $parameterType, $calleeReflection)) { $throwPoints = array_merge($throwPoints, array_map(static fn (InternalThrowPoint $throwPoint) => $throwPoint->isExplicit() ? InternalThrowPoint::createExplicit($scope, $throwPoint->getType(), $arg->value, $throwPoint->canContainAnyThrowable()) : InternalThrowPoint::createImplicit($scope, $arg->value), $closureResult->getThrowPoints())); $impurePoints = array_merge($impurePoints, $closureResult->getImpurePoints()); @@ -3348,7 +3352,7 @@ public function processArgs( $uses[] = $use->var->name; } - $scope = $closureResult->getScope(); + $scope = $scopeToPass; $deferredByRefClosureResults[] = $closureResult; $invalidateExpressions = $closureResult->getInvalidateExpressions(); if ($restoreThisScope !== null) { diff --git a/tests/PHPStan/Analyser/nsrt/bug-14096.php b/tests/PHPStan/Analyser/nsrt/bug-14096.php new file mode 100644 index 0000000000..99883cb14c --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-14096.php @@ -0,0 +1,56 @@ +simulateAppCallback(static function (ServerRequestInterface $request) use ($createViewFx, &$view) { + $view = $createViewFx($request); + + return new App(); + }, static function () use ($simulateRequestFx, &$view) { + assertType('T of Bug14096\AbstractView (method Bug14096\Test::simulateViewCallback(), argument)|null', $view); + + return $simulateRequestFx($view); + }); + + assertType('T of Bug14096\AbstractView (method Bug14096\Test::simulateViewCallback(), argument)|null', $view); + + return $view; + } +}