Fix #11545: while-true-if-break: Variable might not be defined#4974
Open
phpstan-bot wants to merge 7 commits into2.1.xfrom
Open
Fix #11545: while-true-if-break: Variable might not be defined#4974phpstan-bot wants to merge 7 commits into2.1.xfrom
phpstan-bot wants to merge 7 commits into2.1.xfrom
Conversation
- When a loop always iterates (while(true), for(;;), do...while(true)), the only way to exit is through a break statement - Previously, break scopes were merged into an unreachable "condition became false" scope, causing variables defined before break to be reported as "might not be defined" - Now, when the loop always iterates and has break exit points, the post-loop scope is built purely from the break exit point scopes - Applied the fix consistently to while, for, and do-while loops - Added regression test for all three loop types Closes phpstan/phpstan#11545
Automated fix attempt 1 for CI failures.
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 loop with an always-true condition (
while(true),for(;;),do...while(true)) exits only through abreakinside anifblock, variables assigned before thebreakwere incorrectly reported as "might not be defined" after the loop.Changes
whileloop handling insrc/Analyser/NodeScopeResolver.php(around line 1519): when$alwaysIteratesis true and there are break exit points, build$finalScopepurely from break scopes instead of merging them into the unreachable body-end scopedo-whileloop handling insrc/Analyser/NodeScopeResolver.php(around line 1636): same fix appliedforloop handling insrc/Analyser/NodeScopeResolver.php(around line 1758): same fix for break scope construction, plus skip merging with pre-loop scope when$alwaysIteratesis truetests/PHPStan/Rules/Variables/data/bug-11545.phpcoveringwhile(true),for(;;), anddo...while(true)variantstestBug11545intests/PHPStan/Rules/Variables/DefinedVariableRuleTest.phpRoot cause
When processing loops, PHPStan constructs a
$finalScoperepresenting the state after the loop exits. Forwhile(true), this starts with$finalScopeResult->getScope()->filterByFalseyValue($stmt->cond)— the scope where the conditiontrueis falsey, which is effectively unreachable. Break exit point scopes were then merged into this unreachable scope usingmergeWith(). ThemergeWith()method treats a variable existing in one scope but not the other as "maybe defined" (TrinaryLogic::Maybe). Since the unreachable scope doesn't have the variable$result, merging produces "maybe" certainty instead of "yes".The fix recognizes that when a loop always iterates, the only way to reach post-loop code is through a
breakstatement. Therefore, the post-loop scope should be constructed entirely from the break exit point scopes, not merged with the unreachable body-end scope.Test
Added
tests/PHPStan/Rules/Variables/data/bug-11545.phpwith three functions testingwhile(true),for(;;), anddo...while(true)loops where the only exit is abreakinside anifblock that also assigns a variable. The test expects zero errors, confirming the variable is considered definitely defined after the loop.Fixes phpstan/phpstan#11545
Closes phpstan/phpstan#10245
Closes phpstan/phpstan#11984
Closes phpstan/phpstan#5477
Closes phpstan/phpstan#5919
Closes phpstan/phpstan#9023