diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 86f616953b..b3abca6c66 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -1607,7 +1607,8 @@ private function processStmtNode( $alwaysIterates = $condBooleanType->isTrue()->yes(); } - $this->callNodeCallback($nodeCallback, new DoWhileLoopConditionNode($stmt->cond, $bodyScopeResult->toPublic()->getExitPoints()), $bodyScope, $storage); + $publicResult = $bodyScopeResult->toPublic(); + $this->callNodeCallback($nodeCallback, new DoWhileLoopConditionNode($stmt->cond, $publicResult->getExitPoints(), $publicResult->getThrowPoints()), $bodyScope, $storage); if ($alwaysIterates) { $alwaysTerminating = count($bodyScopeResult->getExitPointsByType(Break_::class)) === 0; diff --git a/src/Node/DoWhileLoopConditionNode.php b/src/Node/DoWhileLoopConditionNode.php index 5e589416eb..daace47ef2 100644 --- a/src/Node/DoWhileLoopConditionNode.php +++ b/src/Node/DoWhileLoopConditionNode.php @@ -6,14 +6,16 @@ use PhpParser\Node\Expr; use PhpParser\NodeAbstract; use PHPStan\Analyser\StatementExitPoint; +use PHPStan\Analyser\ThrowPoint; final class DoWhileLoopConditionNode extends NodeAbstract implements VirtualNode { /** * @param StatementExitPoint[] $exitPoints + * @param ThrowPoint[] $throwPoints */ - public function __construct(private Expr $cond, private array $exitPoints) + public function __construct(private Expr $cond, private array $exitPoints, private array $throwPoints) { parent::__construct($cond->getAttributes()); } @@ -31,6 +33,14 @@ public function getExitPoints(): array return $this->exitPoints; } + /** + * @return ThrowPoint[] + */ + public function getThrowPoints(): array + { + return $this->throwPoints; + } + #[Override] public function getType(): string { diff --git a/src/Rules/Comparison/DoWhileLoopConstantConditionRule.php b/src/Rules/Comparison/DoWhileLoopConstantConditionRule.php index 7087fbd006..d66cffa5e1 100644 --- a/src/Rules/Comparison/DoWhileLoopConstantConditionRule.php +++ b/src/Rules/Comparison/DoWhileLoopConstantConditionRule.php @@ -54,6 +54,11 @@ public function processNode(Node $node, Scope $scope): array return []; } } + foreach ($node->getThrowPoints() as $throwPoint) { + if ($throwPoint->isExplicit()) { + return []; + } + } } else { foreach ($node->getExitPoints() as $exitPoint) { $statement = $exitPoint->getStatement(); diff --git a/tests/PHPStan/Rules/Comparison/DoWhileLoopConstantConditionRuleTest.php b/tests/PHPStan/Rules/Comparison/DoWhileLoopConstantConditionRuleTest.php index 519413d54e..3d05ccb85a 100644 --- a/tests/PHPStan/Rules/Comparison/DoWhileLoopConstantConditionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/DoWhileLoopConstantConditionRuleTest.php @@ -28,6 +28,11 @@ protected function getRule(): Rule ); } + public function testBug5865(): void + { + $this->analyse([__DIR__ . '/data/bug-5865.php'], []); + } + public function testRule(): void { $this->analyse([__DIR__ . '/data/do-while-loop.php'], [ diff --git a/tests/PHPStan/Rules/Comparison/data/bug-5865.php b/tests/PHPStan/Rules/Comparison/data/bug-5865.php new file mode 100644 index 0000000000..0f562bd0d8 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-5865.php @@ -0,0 +1,25 @@ +foo(); + } while (true); + } catch (\RuntimeException $e) { + // ok + } + } + + /** + * @throws \RuntimeException + */ + public function foo(): void + { + throw new \RuntimeException(); + } +}