Fix while/for loop exit scope for conditions with pre/post increment/decrement#5687
Open
phpstan-bot wants to merge 1 commit into
Open
Fix while/for loop exit scope for conditions with pre/post increment/decrement#5687phpstan-bot wants to merge 1 commit into
phpstan-bot wants to merge 1 commit into
Conversation
…ect inc/dec When a while or for loop condition is a comparison with a direct PreInc/PreDec/PostInc/PostDec operand (e.g. `while(++$counter < 100)`), the old approach of applying filterByFalseyValue on the body output scope produced NEVER for the counter variable. This happened because the body scope inherited the truthy-narrowed type from condition evaluation, and the falsey filter then contradicted it by subtracting the same range. The fix uses condResult->getFalseyScope() in this case, which correctly captures both the side effect (increment/decrement) and the falsey narrowing on the pre-body merged scope. For conditions without direct inc/dec operators, the old filterByFalseyValue approach is preserved to maintain body-output precision for other variables. Closes phpstan/phpstan#7230
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
while(++$counter < 100)reporting counter as*NEVER*after loop exit instead ofint<100, max>for(; ++$counter < 5;)patternRoot cause
When a loop condition like
++$counter < 100is processed:$countertoint<1, 99>for the loop bodyfilterByFalseyValue(++$counter < 100)tries to subtractint<min, 99>from the body'sint<1, 99>, producing*NEVER*The contradiction occurs because
filterByFalseyValuedoesn't re-evaluate the increment side effect -- it only narrows the existing (already-narrowed) type.Fix
When the condition is a direct comparison with an increment/decrement operand, use
condResult->getFalseyScope()instead. This scope correctly reflects both the side effect and the falsey narrowing, evaluated on the merged/generalized loop scope. For all other condition forms (compound&&/||, simple comparisons), the oldfilterByFalseyValueapproach is preserved to maintain precision from body-output types.Test plan
tests/PHPStan/Analyser/nsrt/bug-7230.phpcovering PreInc, PostInc, PreDec in both while and for loopswhile-loop-variables.phptest passes (compound&&condition with PostInc)for-loop-variables.phptest passes (standard for loop patterns)integer-range-types.phptest passes (in-loop assertions)Closes phpstan/phpstan#7230