Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Remove UnionType guard from Pass 2 condition matching
The UnionType guard was a workaround for two issues that are now fixed
at their root causes:

1. pr-5379 regression (non-falsy-string matching 'filter'): Fixed by
   skipping trivially-always-true conditions in
   processBooleanSureConditionalTypes — when TypeCombinator::remove has
   no effect, the condition is not created.

2. bug-14249 regression (mixed~false producing *NEVER*): Fixed by
   marking the getMixed() test helper as @phpstan-impure, preventing
   conflicting conditional expressions from assignment handlers.

With these root causes addressed, Pass 2 can safely use isSuperTypeOf
for all condition types without restriction.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
  • Loading branch information
2 people authored and staabm committed Apr 13, 2026
commit 9c66e81d0c79ccd89e3ddc4179041726d2a580e7
26 changes: 7 additions & 19 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -3229,22 +3229,13 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self
$specifiedExpressions[$conditionalExprString] = $conditionalExpression->getTypeHolder();
}

// Pass 2: for union condition types, use isSuperTypeOf
// This handles cases where the condition type is a union
// (e.g. 'value1'|'value2' or int|string) that won't match a narrowed
// type (e.g. 'value1' or int) via equals(), but should match via
// isSuperTypeOf. Only attempted when pass 1 found no matches, to avoid
// conflicts with broader conditions that have lower certainty from
// scope merging.
//
// The UnionType guard is necessary because using isSuperTypeOf for all
// condition types causes regressions: non-union types like
// non-falsy-string (from TypeSpecifier boolean processing) or
// mixed~false (from assignment handlers) are too broad — e.g.
// non-falsy-string would incorrectly match 'filter', and mixed~false
// matching false causes conflicting conditional expressions to both
// activate, producing *NEVER* types. Union types are safe because they
// explicitly enumerate alternatives where subtype matching is correct.
// Pass 2: use isSuperTypeOf for condition matching
// This handles cases where the condition type is broader than the
// narrowed type — e.g. 'value1'|'value2' (condition) won't match
// 'value1' (narrowed) via equals(), but should match via
// isSuperTypeOf. Only attempted when pass 1 found no matches, to
// avoid conflicts with broader conditions that have lower certainty
// from scope merging.
if (!array_key_exists($conditionalExprString, $conditions)) {
foreach ($conditionalExpressions as $conditionalExpression) {
foreach ($conditionalExpression->getConditionExpressionTypeHolders() as $holderExprString => $conditionalTypeHolder) {
Expand All @@ -3255,9 +3246,6 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self
if (!$specifiedHolder->getCertainty()->equals($conditionalTypeHolder->getCertainty())) {
continue 2;
}
if (!$conditionalTypeHolder->getType() instanceof UnionType) {
continue 2;
}
if (!$conditionalTypeHolder->getType()->isSuperTypeOf($specifiedHolder->getType())->yes()) {
continue 2;
}
Expand Down