Skip to content

Commit e3832a7

Browse files
github-actions[bot]phpstan-bot
authored andcommitted
Fix do-while(true) false positive when loop body has explicit throw points
- DoWhileLoopConstantConditionRule now considers explicit throw points as valid loop exit mechanisms, suppressing the "always true" warning - Added throw points to DoWhileLoopConditionNode alongside exit points - Updated NodeScopeResolver to pass throw points when creating the node - New regression test in tests/PHPStan/Rules/Comparison/data/bug-5865.php Closes phpstan/phpstan#5865
1 parent 2681e50 commit e3832a7

File tree

5 files changed

+48
-2
lines changed

5 files changed

+48
-2
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1607,7 +1607,8 @@ private function processStmtNode(
16071607
$alwaysIterates = $condBooleanType->isTrue()->yes();
16081608
}
16091609

1610-
$this->callNodeCallback($nodeCallback, new DoWhileLoopConditionNode($stmt->cond, $bodyScopeResult->toPublic()->getExitPoints()), $bodyScope, $storage);
1610+
$publicResult = $bodyScopeResult->toPublic();
1611+
$this->callNodeCallback($nodeCallback, new DoWhileLoopConditionNode($stmt->cond, $publicResult->getExitPoints(), $publicResult->getThrowPoints()), $bodyScope, $storage);
16111612

16121613
if ($alwaysIterates) {
16131614
$alwaysTerminating = count($bodyScopeResult->getExitPointsByType(Break_::class)) === 0;

src/Node/DoWhileLoopConditionNode.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@
66
use PhpParser\Node\Expr;
77
use PhpParser\NodeAbstract;
88
use PHPStan\Analyser\StatementExitPoint;
9+
use PHPStan\Analyser\ThrowPoint;
910

1011
final class DoWhileLoopConditionNode extends NodeAbstract implements VirtualNode
1112
{
1213

1314
/**
1415
* @param StatementExitPoint[] $exitPoints
16+
* @param ThrowPoint[] $throwPoints
1517
*/
16-
public function __construct(private Expr $cond, private array $exitPoints)
18+
public function __construct(private Expr $cond, private array $exitPoints, private array $throwPoints)
1719
{
1820
parent::__construct($cond->getAttributes());
1921
}
@@ -31,6 +33,14 @@ public function getExitPoints(): array
3133
return $this->exitPoints;
3234
}
3335

36+
/**
37+
* @return ThrowPoint[]
38+
*/
39+
public function getThrowPoints(): array
40+
{
41+
return $this->throwPoints;
42+
}
43+
3444
#[Override]
3545
public function getType(): string
3646
{

src/Rules/Comparison/DoWhileLoopConstantConditionRule.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ public function processNode(Node $node, Scope $scope): array
5454
return [];
5555
}
5656
}
57+
foreach ($node->getThrowPoints() as $throwPoint) {
58+
if ($throwPoint->isExplicit()) {
59+
return [];
60+
}
61+
}
5762
} else {
5863
foreach ($node->getExitPoints() as $exitPoint) {
5964
$statement = $exitPoint->getStatement();

tests/PHPStan/Rules/Comparison/DoWhileLoopConstantConditionRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ protected function getRule(): Rule
2828
);
2929
}
3030

31+
public function testBug5865(): void
32+
{
33+
$this->analyse([__DIR__ . '/data/bug-5865.php'], []);
34+
}
35+
3136
public function testRule(): void
3237
{
3338
$this->analyse([__DIR__ . '/data/do-while-loop.php'], [
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug5865;
4+
5+
class HelloWorld
6+
{
7+
public function sayHello(): void
8+
{
9+
try {
10+
do {
11+
$this->foo();
12+
} while (true);
13+
} catch (\RuntimeException $e) {
14+
// ok
15+
}
16+
}
17+
18+
/**
19+
* @throws \RuntimeException
20+
*/
21+
public function foo(): void
22+
{
23+
throw new \RuntimeException();
24+
}
25+
}

0 commit comments

Comments
 (0)