Skip to content

Commit 2d5f0f6

Browse files
phpstan-botclaude
andcommitted
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 <noreply@anthropic.com>
1 parent 24f8a2f commit 2d5f0f6

File tree

1 file changed

+7
-19
lines changed

1 file changed

+7
-19
lines changed

src/Analyser/MutatingScope.php

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3229,22 +3229,13 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self
32293229
$specifiedExpressions[$conditionalExprString] = $conditionalExpression->getTypeHolder();
32303230
}
32313231

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

0 commit comments

Comments
 (0)