Skip to content

Commit c0ac89b

Browse files
phpstan-botclaude
andcommitted
Extend subtype matching to IntegerRangeType guards and restore bar() test
The guard type check was too narrow with just `instanceof UnionType` — it missed IntegerRangeType guards like `int<1, max>` used for count-based array narrowing via intermediate variables. Extending to also allow IntegerRangeType fixes the bar() case in bug-4090 where `$count = count($a)` followed by `$count === 1` should narrow the array to non-empty. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ef80918 commit c0ac89b

2 files changed

Lines changed: 19 additions & 7 deletions

File tree

src/Analyser/MutatingScope.php

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3228,17 +3228,16 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self
32283228
}
32293229
$guardType = $conditionalTypeHolder->getType();
32303230
// Allow subtype matching: when the specified type is a strict subtype
3231-
// of the guard, fire the conditional. Restricted to UnionType guards
3232-
// because unions represent explicit disjunctions of alternatives —
3233-
// narrowing to one alternative validly satisfies the guard condition.
3234-
// Non-union types (IntersectionType, BooleanType, etc.) can have
3235-
// coincidental subtype relationships that cause incorrect type
3236-
// narrowing when the conditional result is applied to the scope.
3231+
// of the guard, fire the conditional. Restricted to UnionType and
3232+
// IntegerRangeType guards — these represent explicit sets or ranges
3233+
// of values where narrowing to a member validly satisfies the guard.
3234+
// IntersectionType, MixedType, and other types can have coincidental
3235+
// subtype relationships that cause incorrect type narrowing.
32373236
if (
32383237
$conditionalExpression->getTypeHolder()->getCertainty()->yes()
32393238
&& $specifiedHolder->getCertainty()->yes()
32403239
&& $conditionalTypeHolder->getCertainty()->yes()
3241-
&& $guardType instanceof UnionType
3240+
&& ($guardType instanceof UnionType || $guardType instanceof IntegerRangeType)
32423241
&& $guardType->isSuperTypeOf($specifiedHolder->getType())->yes()
32433242
) {
32443243
$subtypeMatch = true;

tests/PHPStan/Analyser/nsrt/bug-4090.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@ function foo(array $a): void
1717
}
1818

1919

20+
/** @param string[] $a */
21+
function bar(array $a): void
22+
{
23+
$count = count($a);
24+
if ($count > 1) {
25+
echo implode(',', $a);
26+
} elseif ($count === 1) {
27+
assertType('string', current($a));
28+
echo trim(current($a));
29+
}
30+
}
31+
32+
2033
/** @param string[] $a */
2134
function qux(array $a): void
2235
{

0 commit comments

Comments
 (0)