Skip to content

[NodeAnalyzer] Treat infinite loop without break as always terminated - #8348

Merged
TomasVotruba merged 1 commit into
mainfrom
fix-infinite-loop-terminated-append-return
Aug 13, 2026
Merged

[NodeAnalyzer] Treat infinite loop without break as always terminated#8348
TomasVotruba merged 1 commit into
mainfrom
fix-infinite-loop-terminated-append-return

Conversation

@TomasVotruba

Copy link
Copy Markdown
Member

Fixes rectorphp/rector#9852

TerminatedNodeAnalyzer::isAlwaysTerminated() did not recognize an infinite loop (while (true), do {} while (true), for (;;)) with no break as a terminating statement. Anything following such a loop is unreachable, but the analyzer reported the loop as fall-through.

Consequence downstream: ConsoleExecuteReturnIntRector appended a redundant return Command::SUCCESS; after a while (true) body whose exits all return, producing an unreachable statement that fails PHPStan at level 7+ (reported in the issue). RemoveUnreachableStatementRector (the core consumer) also failed to strip such dead code.

Now an infinite loop with no escaping break/goto is treated as always terminated:

protected function execute(...): int
{
    while (true) {
        if ($this->shouldStop()) {
            return Command::SUCCESS;
        }
        $this->tick();
    }
    // was appended before, now correctly omitted:
    // return Command::SUCCESS; ← unreachable
}

Behavior with RemoveUnreachableStatementRector:

 while (true) {
     if ($a) {
         return 'A';
     }
     $a = doSomething();
 }
-echo 'never executed';

Conservative on purpose: a loop containing any break/goto (which may exit it) is left as non-terminating, so no code that could still be reached is dropped. Only literal always-true conditions (true, non-zero int literal, empty for(;;)) qualify — dynamic conditions like while ($x) are untouched.

Fixtures: always_terminated_infinite_while.php.inc (removed dead code) and skip_while_true_with_break.php.inc (kept, has a break).

@TomasVotruba
TomasVotruba merged commit 975c46e into main Aug 13, 2026
52 checks passed
@TomasVotruba
TomasVotruba deleted the fix-infinite-loop-terminated-append-return branch August 13, 2026 20:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

CommandConstantReturnCodeRector appends unreachable return after a break-less while(true) loop

1 participant