Fix #14178: Regression about complicated conditions#5025
Merged
ondrejmirtes merged 1 commit into2.1.xfrom Feb 22, 2026
Merged
Conversation
- Iterating conditional expression holders in filterBySpecifiedTypes now loops until convergence, making it order-independent - The previous commit (3daa312, Fix #13303) changed mergeConditionalExpressions to put existing holders before new ones, breaking chains where a new holder must fire before an existing one - Updated dependent-variable-certainty and dependent-expression-certainty tests to reflect improved certainty resolution (Maybe -> Yes) - New regression test in tests/PHPStan/Analyser/nsrt/bug-14178.php Fixes phpstan/phpstan#14178
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
Fixes a regression introduced in 3daa312 (Fix #13303) where PHPStan failed to narrow nullable types through chained conditional expression holders. When boolean variables like
$exists = $obj !== nullwere used in compound conditions, the type of$objwas not properly narrowed inside subsequentifblocks.Changes
MutatingScope::filterBySpecifiedTypes()insrc/Analyser/MutatingScope.phpto iterate over conditional expression holders in a loop until no new specified expressions are discovered (convergence loop)tests/PHPStan/Analyser/nsrt/dependent-variable-certainty.phpandtests/PHPStan/Analyser/nsrt/dependent-expression-certainty.phpto reflect improved certainty (the comment "could be Yes" confirmed this was a known limitation)tests/PHPStan/Analyser/nsrt/bug-14178.phpRoot cause
The
mergeConditionalExpressions()method introduced in 3daa312 changed the iteration order of conditional expression holders compared to the oldarray_merge()approach. Previously, new holders (from boolean conditions like!$a && !$b) came before existing holders (from assignments like$exists = $obj !== null). The new method reversed this order by starting with existing holders.In
filterBySpecifiedTypes(), conditional expression holders are processed in a single pass: when one holder fires, it adds tospecifiedExpressions, which may enable subsequent holders. With the reversed order, a holder like "when$newVersionExistsis true,$newVersionis non-null" was evaluated before the holder "when$previousVersionExistsis false,$newVersionExistsis true" had fired, so$newVersionExistswasn't yet inspecifiedExpressions.The fix makes
filterBySpecifiedTypesorder-independent by looping until convergence — it keeps iterating as long as new expressions are being resolved, allowing chains of any depth to propagate correctly.Test
The regression test
tests/PHPStan/Analyser/nsrt/bug-14178.phpreproduces the exact scenario from the issue: two nullable parameters with boolean tracking variables, compound conditions that eliminate impossible combinations, and a subsequentifblock where the type should be narrowed to non-null.Fixes phpstan/phpstan#14178