Fix phpstan/phpstan#6830: Variable inside loop might not be defined.#5185
Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Open
Fix phpstan/phpstan#6830: Variable inside loop might not be defined.#5185phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Conversation
- Fixed intersectConditionalExpressions in MutatingScope to merge conditional expressions with matching conditions but different result types, instead of dropping them entirely - Added regression test case in tests/PHPStan/Rules/Variables/data/bug-6830.php - Root cause: during loop scope merging, conditional expression keys include the result type, so different types across iterations caused the intersection to drop the conditional linking variable definedness to the guarding condition Closes phpstan/phpstan#6830
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 conditionally assigned before a
foreachloop (e.g.,if ($do) { $x = 9999; }) and then used inside the loop under the same condition (if ($do) { if ($x) { ... } }), PHPStan incorrectly reported "Variable $x might not be defined" even though the guarding condition guarantees the variable is defined. Additionally, PHPStan contradictorily reported "If condition is always true" on the same line.Changes
intersectConditionalExpressionsinsrc/Analyser/MutatingScope.phpto merge conditional expressions that have matching condition expressions but different result types, instead of dropping them entirelyconditionExpressionHoldersMatchto compare condition expression type holderstests/PHPStan/Rules/Variables/data/bug-6830.phpwith the exact reproducing code from the issueRoot cause
During loop scope iteration in
NodeScopeResolver, the body scope is merged with the original scope usingmergeWith(). This callsintersectConditionalExpressions()which only keeps conditional expressions present in both scopes, matched by key. The key format includes the result type (e.g.,$do=true => 9999 (Yes)vs$do=true => 123 (Yes)). After the first loop iteration, the variable's type changes (from9999to123due to reassignment), causing the keys to differ. The intersection then dropped the conditional expression entirely, losing the information that$xis definitely defined when$doistrue.The fix detects when both scopes have conditional expressions for the same variable with the same condition expressions (matching by condition holder equality) but different result types, and merges the result type holders using
ExpressionTypeHolder::and()(which unions the types and ANDs the certainties).Test
Extended
tests/PHPStan/Rules/Variables/data/bug-6830.phpwith thetest2function that reproduces the exact issue: a variable assigned before a foreach loop under a condition, and used inside the loop under the same condition. The test expects no errors.Fixes phpstan/phpstan#6830