From 0ebf212252417c42076b924bf674421e11a6efb5 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 16 Jan 2026 07:35:17 +0100 Subject: [PATCH 1/2] Drop MutatingScope->rememberConstructorScope() --- src/Analyser/MutatingScope.php | 22 ---------------------- src/Analyser/NodeScopeResolver.php | 2 +- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index c0a9489e3f..d283b8efb8 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -373,28 +373,6 @@ private function rememberConstructorExpressions(array $currentExpressionTypes): return $expressionTypes; } - public function rememberConstructorScope(): self - { - return $this->scopeFactory->create( - $this->context, - $this->isDeclareStrictTypes(), - null, - $this->getNamespace(), - $this->rememberConstructorExpressions($this->expressionTypes), - $this->rememberConstructorExpressions($this->nativeExpressionTypes), - $this->conditionalExpressions, - $this->inClosureBindScopeClasses, - $this->anonymousFunctionReflection, - $this->inFirstLevelStatement, - [], - [], - $this->inFunctionCallsStack, - $this->afterExtractCall, - $this->parentScope, - $this->nativeTypesPromoted, - ); - } - private function isReadonlyPropertyFetch(PropertyFetch $expr, bool $allowOnlyOnThis): bool { if (!$this->phpVersion->supportsReadOnlyProperties()) { diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 98e7e5f579..4ec3f317ec 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -871,7 +871,7 @@ private function processStmtNode( } if (count($scopesToMerge) > 0) { - $scope = $scopesToMerge[0]->mergeWith(...array_slice($scopesToMerge, 1))->rememberConstructorScope(); + $scope = $scopesToMerge[0]->mergeWith(...array_slice($scopesToMerge, 1)); } } From eba6f6685ee3564e14b92ea7ff77df248ab1651e Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 16 Jan 2026 07:45:26 +0100 Subject: [PATCH 2/2] Ensure remembered types in inherited scopes --- ...remember-readonly-constructor-narrowed.php | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/PHPStan/Analyser/nsrt/remember-readonly-constructor-narrowed.php b/tests/PHPStan/Analyser/nsrt/remember-readonly-constructor-narrowed.php index 55f8351fad..ab12c2b2cb 100644 --- a/tests/PHPStan/Analyser/nsrt/remember-readonly-constructor-narrowed.php +++ b/tests/PHPStan/Analyser/nsrt/remember-readonly-constructor-narrowed.php @@ -143,3 +143,28 @@ public function doFoo() { assertType('int', $this->prop->writable); } } + +class HelloWorldReadonlyPropertyInClosureScope { + private readonly int $i; + + public function __construct() + { + if (rand(0,1)) { + $this->i = 4; + } else { + $this->i = 10; + } + } + + public function doFoo() { + assertType('4|10', $this->i); + + (function() { + assertType('4|10', $this->i); + })(); + + $func = function() { + assertType('4|10', $this->i); + }; + } +}