diff --git a/src/Analyser/ExprHandler/MatchHandler.php b/src/Analyser/ExprHandler/MatchHandler.php index 626e6fc717..28081ef53c 100644 --- a/src/Analyser/ExprHandler/MatchHandler.php +++ b/src/Analyser/ExprHandler/MatchHandler.php @@ -335,7 +335,9 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex ExpressionContext::createTopLevel(), ); $armScope = $armResult->getScope(); - $armBodyScopes[] = $armScope; + if (!$armResult->isAlwaysTerminating()) { + $armBodyScopes[] = $armScope; + } $hasYield = $hasYield || $armResult->hasYield(); $throwPoints = array_merge($throwPoints, $armResult->getThrowPoints()); $impurePoints = array_merge($impurePoints, $armResult->getImpurePoints()); @@ -372,7 +374,9 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex $hasYield = $hasYield || $armResult->hasYield(); $throwPoints = array_merge($throwPoints, $armResult->getThrowPoints()); $impurePoints = array_merge($impurePoints, $armResult->getImpurePoints()); - $armBodyScopes[] = $matchScope; + if (!$armResult->isAlwaysTerminating()) { + $armBodyScopes[] = $matchScope; + } continue; } @@ -423,7 +427,9 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex ExpressionContext::createTopLevel(), ); $armScope = $armResult->getScope(); - $armBodyScopes[] = $armScope; + if (!$armResult->isAlwaysTerminating()) { + $armBodyScopes[] = $armScope; + } $hasYield = $hasYield || $armResult->hasYield(); $throwPoints = array_merge($throwPoints, $armResult->getThrowPoints()); $impurePoints = array_merge($impurePoints, $armResult->getImpurePoints()); diff --git a/src/Analyser/ExprHandler/ThrowHandler.php b/src/Analyser/ExprHandler/ThrowHandler.php index 7a51007317..3866bcfe57 100644 --- a/src/Analyser/ExprHandler/ThrowHandler.php +++ b/src/Analyser/ExprHandler/ThrowHandler.php @@ -36,7 +36,7 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex return new ExpressionResult( $scope, hasYield: false, - isAlwaysTerminating: $exprResult->isAlwaysTerminating(), + isAlwaysTerminating: true, throwPoints: array_merge($exprResult->getThrowPoints(), [InternalThrowPoint::createExplicit($scope, $scope->getType($expr->expr), $expr, false)]), impurePoints: $exprResult->getImpurePoints(), ); diff --git a/tests/PHPStan/Analyser/nsrt/bug-9601.php b/tests/PHPStan/Analyser/nsrt/bug-9601.php new file mode 100644 index 0000000000..952e76bb78 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-9601.php @@ -0,0 +1,31 @@ += 8.0 + +declare(strict_types = 1); + +namespace Bug9601; + +use function PHPStan\Testing\assertType; + +class HelloWorld +{ + public string $message = ''; +} + +class HelloWorld2 +{ + public string $message = ''; +} + +/** + * @param mixed $object + */ +function test($object): void +{ + $objectName = match (true) { + $object instanceof HelloWorld => $object::class, + $object instanceof HelloWorld2 => $object::class, + default => throw new \LogicException(), + }; + + assertType('Bug9601\HelloWorld|Bug9601\HelloWorld2', $object); +} diff --git a/tests/PHPStan/Rules/ScopeFunctionCallStackRuleTest.php b/tests/PHPStan/Rules/ScopeFunctionCallStackRuleTest.php index 6f652f9312..a4cbeadbea 100644 --- a/tests/PHPStan/Rules/ScopeFunctionCallStackRuleTest.php +++ b/tests/PHPStan/Rules/ScopeFunctionCallStackRuleTest.php @@ -26,19 +26,19 @@ public function testRule(): void ], [ "var_dump\nprint_r\nsleep", - 10, + 13, ], [ "var_dump\nprint_r\nsleep", - 13, + 19, ], [ 'ScopeFunctionCallStack\NamedArgumentTest::testMethod', - 31, + 37, ], [ 'ScopeFunctionCallStack\NamedArgumentTest::testMethod', - 42, + 48, ], ]); } diff --git a/tests/PHPStan/Rules/ScopeFunctionCallStackWithParametersRuleTest.php b/tests/PHPStan/Rules/ScopeFunctionCallStackWithParametersRuleTest.php index 25cb212ccb..db504b8c34 100644 --- a/tests/PHPStan/Rules/ScopeFunctionCallStackWithParametersRuleTest.php +++ b/tests/PHPStan/Rules/ScopeFunctionCallStackWithParametersRuleTest.php @@ -26,21 +26,21 @@ public function testRule(): void ], [ "var_dump (\$value)\nprint_r (\$value)\nsleep (\$seconds)", - 10, + 13, ], [ "var_dump (\$value)\nprint_r (\$value)\nsleep (\$seconds)", - 13, + 19, ], [ // Named argument test - should report $notImmediate, not $immediate 'ScopeFunctionCallStack\NamedArgumentTest::testMethod ($notImmediate)', - 31, + 37, ], [ // Named argument with missing required param - should still match by name 'ScopeFunctionCallStack\NamedArgumentTest::testMethod ($notImmediate)', - 42, + 48, ], ]); } diff --git a/tests/PHPStan/Rules/data/scope-function-call-stack.php b/tests/PHPStan/Rules/data/scope-function-call-stack.php index ae5eb9705e..7d49c17ee1 100644 --- a/tests/PHPStan/Rules/data/scope-function-call-stack.php +++ b/tests/PHPStan/Rules/data/scope-function-call-stack.php @@ -5,11 +5,17 @@ function (): void { var_dump(print_r(sleep(throw new \Exception()))); +}; +function (): void +{ var_dump(print_r(function () { sleep(throw new \Exception()); })); +}; +function (): void +{ var_dump(print_r(fn () => sleep(throw new \Exception()))); };