Fix #5919: Only one possible (always entered) loop exit path is not detected#5072
Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Open
Fix #5919: Only one possible (always entered) loop exit path is not detected#5072phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Conversation
- In while(true), do...while(true), and for(;;) loops, when the only exit path is through break, the scope after the loop should come exclusively from break exit points, not from the unreachable "condition becomes false" path - Previously, the normal-flow scope (e.g. from a catch block where the variable was not assigned) was merged with break exit point scopes, degrading variable certainty from Yes to Maybe - New regression test in tests/PHPStan/Rules/Variables/data/bug-5919.php Closes phpstan/phpstan#5919
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When a variable is assigned inside a
tryblock before abreakin an always-iterating loop (while(true),do...while(true),for(;;)), PHPStan incorrectly reported "Variable $s might not be defined" after the loop. Since the loop condition is always true, the only way to exit the loop is throughbreak, and at that point the variable is always defined.Changes
whileloop handling insrc/Analyser/NodeScopeResolver.php(around line 1522): When$alwaysIteratesis true and there are break exit points, build the final scope exclusively from break exit point scopes instead of merging them with the unreachable "condition becomes false" scope.do...whileloop handling (around line 1637).forloop handling (around line 1757), also skipping the post-loop scope merge with the pre-loop scope when$alwaysIterates.tests/PHPStan/Rules/Variables/data/bug-5919.phpcovering all three loop types.testBug5919intests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php.Root cause
When processing a
while(true)loop containingtry { $s = queryApi(); break; } catch (\Exception $e) { ... }, the scope after the loop was computed by merging two sources:$finalScopeResult->getScope()->filterByFalseyValue($stmt->cond)— representing the unreachable path where the loop condition becomes false. This scope came from the catch block where$swas NOT defined.$sWAS defined.Merging these two scopes degraded
$sfrom definitely-defined (Yes) to maybe-defined (Maybe), triggering the false positive. The fix recognizes that when the loop always iterates, the "condition becomes false" path is unreachable, so only break exit point scopes should contribute to the final scope after the loop.Test
Added
tests/PHPStan/Rules/Variables/data/bug-5919.phpwith three test functions coveringwhile(true),do { } while(true), andfor(;;)loops, each containing atry/catchwhere a variable is assigned and then the loop is broken out of. The test expects zero errors (no false positives).Fixes phpstan/phpstan#5919