Skip to content

Commit 13f95d5

Browse files
phpstan-botclaude
andcommitted
Add comment explaining why isSuperTypeOf matching is limited to integer ranges
The conditional expression system intersects results from ALL matching conditions. Using isSuperTypeOf broadly causes types like bool to match both 'false' and 'bool' conditions simultaneously, producing *NEVER* when their results conflict. Integer ranges (from count()) don't have this problem because their conditions are non-overlapping. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5c37e8a commit 13f95d5

File tree

1 file changed

+12
-0
lines changed

1 file changed

+12
-0
lines changed

src/Analyser/MutatingScope.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2155,6 +2155,18 @@ private function conditionalExpressionHolderMatches(ExpressionTypeHolder $specif
21552155
}
21562156

21572157
$conditionType = $condition->getType();
2158+
2159+
// Only use isSuperTypeOf for non-constant integer types (i.e. IntegerRangeType).
2160+
// This is needed for count()-in-variable patterns: when $count = count($a) creates
2161+
// a condition on int<1, max>, and $count is later narrowed to int<2, max> or 1,
2162+
// the subtype relationship correctly preserves the array narrowing.
2163+
//
2164+
// We cannot safely extend this to all types because filterBySpecifiedTypes()
2165+
// intersects results from ALL matching conditional expressions (line ~3251).
2166+
// For types like bool, isSuperTypeOf would match both a 'false' condition AND
2167+
// a 'bool' condition simultaneously, and intersecting their (potentially conflicting)
2168+
// results produces *NEVER*. Integer ranges don't have this problem because
2169+
// count() creates non-overlapping conditions (e.g. int<1, max> vs 0).
21582170
if (!$conditionType->isInteger()->yes() || $conditionType->isConstantScalarValue()->yes()) {
21592171
return false;
21602172
}

0 commit comments

Comments
 (0)