Skip to content

Commit c36922b

Browse files
mhertondrejmirtes
andcommitted
fix(type-system): sealed class-string match exhaustiveness for ::class comparisons
GenericClassStringType::tryRemove() did not handle @phpstan-sealed hierarchies, so match expressions on $foo::class falsely reported unhandled remaining values even when all allowed subtypes were covered. Delegates to TypeCombinator::remove() which already handles sealed subtraction via ObjectType::changeSubtractedType(). Closes phpstan/phpstan#12241 Co-Authored-By: Ondrej Mirtes <ondrej@mirtes.cz>
1 parent 43c1a1b commit c36922b

File tree

3 files changed

+38
-0
lines changed

3 files changed

+38
-0
lines changed

src/Type/Generic/GenericClassStringType.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,18 @@ public function tryRemove(Type $typeToRemove): ?Type
219219
if ($classReflection->isFinal() && $genericObjectClassNames[0] === $typeToRemove->getValue()) {
220220
return new NeverType();
221221
}
222+
223+
if ($classReflection->getAllowedSubTypes() !== null) {
224+
$objectTypeToRemove = new ObjectType($typeToRemove->getValue());
225+
$remainingType = TypeCombinator::remove($generic, $objectTypeToRemove);
226+
if ($remainingType instanceof NeverType) {
227+
return new NeverType();
228+
}
229+
230+
if (!$remainingType->equals($generic)) {
231+
return new self($remainingType);
232+
}
233+
}
222234
}
223235
} elseif (count($genericObjectClassNames) > 1) {
224236
$objectTypeToRemove = new ObjectType($typeToRemove->getValue());

tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,12 @@ public function testBug9534(): void
447447
]);
448448
}
449449

450+
#[RequiresPhp('>= 8.0')]
451+
public function testBug12241(): void
452+
{
453+
$this->analyse([__DIR__ . '/data/bug-12241.php'], []);
454+
}
455+
450456
#[RequiresPhp('>= 8.0')]
451457
public function testBug13029(): void
452458
{
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug12241;
6+
7+
/**
8+
* @phpstan-sealed Bar|Baz
9+
*/
10+
abstract class Foo{}
11+
12+
final class Bar extends Foo{}
13+
final class Baz extends Foo{}
14+
15+
function (Foo $foo): string {
16+
return match ($foo::class) {
17+
Bar::class => 'Bar',
18+
Baz::class => 'Baz',
19+
};
20+
};

0 commit comments

Comments
 (0)