From 20d091b2e5d7e4cb88df2cdea4603230e635d09d Mon Sep 17 00:00:00 2001 From: ondrejmirtes <104888+ondrejmirtes@users.noreply.github.com> Date: Thu, 12 Feb 2026 08:45:45 +0000 Subject: [PATCH] Fix #13211 --- src/Analyser/NodeScopeResolver.php | 2 +- tests/PHPStan/Analyser/nsrt/bug-13211.php | 115 ++++++++++++++++++++++ 2 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-13211.php diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 7aff6c5783..2803719b65 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -1845,7 +1845,7 @@ private function processStmtNode( } if ((!$hasDefaultCase && !$exhaustive) || $finalScope === null) { - $finalScope = $scope->mergeWith($finalScope); + $finalScope = $scopeForBranches->mergeWith($finalScope); } return new InternalStatementResult($finalScope, $hasYield, $alwaysTerminating, $exitPointsForOuterLoop, $throwPoints, $impurePoints); diff --git a/tests/PHPStan/Analyser/nsrt/bug-13211.php b/tests/PHPStan/Analyser/nsrt/bug-13211.php new file mode 100644 index 0000000000..0c2dd94367 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-13211.php @@ -0,0 +1,115 @@ + 5 ? new Foo() : null; + + switch (true) { + case $a === null: + exit; + } + + assertType('Bug13211\Foo', $a); +} + +function switchTrueWithReturnNarrows(): void +{ + $a = random_int(1, 10) > 5 ? new Foo() : null; + + switch (true) { + case $a === null: + return; + } + + assertType('Bug13211\Foo', $a); +} + +function switchTrueWithThrowNarrows(): void +{ + $a = random_int(1, 10) > 5 ? new Foo() : null; + + switch (true) { + case $a === null: + throw new \Exception(); + } + + assertType('Bug13211\Foo', $a); +} + +function switchTrueMultipleCases(): void +{ + /** @var int|string|null $a */ + $a = null; + + switch (true) { + case $a === null: + exit; + case is_string($a): + exit; + } + + assertType('int', $a); +} + +function switchTrueWithInstanceof(): void +{ + /** @var Foo|int|null $a */ + $a = null; + + switch (true) { + case $a instanceof Foo: + exit; + } + + assertType('int|null', $a); +} + +function switchTrueWithBreakDoesNotNarrow(): void +{ + $a = random_int(1, 10) > 5 ? new Foo() : null; + + switch (true) { + case $a === null: + break; + } + + assertType('Bug13211\Foo|null', $a); +} + +function switchTrueWithDefaultCase(): void +{ + $a = random_int(1, 10) > 5 ? new Foo() : null; + + switch (true) { + case $a === null: + exit; + default: + break; + } + + assertType('Bug13211\Foo', $a); +} + +function regularSwitchStillWorks(): void +{ + /** @var 1|2|3 $a */ + $a = 1; + + switch ($a) { + case 1: + exit; + } + + assertType('2|3', $a); +}