diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e925137b24..67dabae8fe 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1134,7 +1134,7 @@ parameters: - rawMessage: 'Doing instanceof PHPStan\Type\ObjectType is error-prone and deprecated. Use Type::isObject() or Type::getObjectClassNames() instead.' identifier: phpstanApi.instanceofType - count: 1 + count: 2 path: src/Type/Generic/GenericObjectType.php - diff --git a/src/Type/Generic/GenericObjectType.php b/src/Type/Generic/GenericObjectType.php index 10b516355d..63a10119e8 100644 --- a/src/Type/Generic/GenericObjectType.php +++ b/src/Type/Generic/GenericObjectType.php @@ -405,6 +405,14 @@ protected function recreate(string $className, array $types, ?Type $subtractedTy public function changeSubtractedType(?Type $subtractedType): Type { + $result = parent::changeSubtractedType($subtractedType); + + // Parent handles sealed type exhaustiveness (returning NeverType when all + // allowed subtypes are subtracted, or a single remaining subtype). + if (!$result instanceof ObjectType || $result->getClassName() !== $this->getClassName()) { + return $result; + } + return new self($this->getClassName(), $this->types, $subtractedType, null, $this->variances); } diff --git a/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php b/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php index 85b8364a32..1f7880fc2a 100644 --- a/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php @@ -453,6 +453,12 @@ public function testBug12241(): void $this->analyse([__DIR__ . '/data/bug-12241.php'], []); } + #[RequiresPhp('>= 8.0')] + public function testBug14412(): void + { + $this->analyse([__DIR__ . '/data/bug-14412.php'], []); + } + #[RequiresPhp('>= 8.0')] public function testBug13029(): void { diff --git a/tests/PHPStan/Rules/Comparison/data/bug-14412.php b/tests/PHPStan/Rules/Comparison/data/bug-14412.php new file mode 100644 index 0000000000..35266e75ce --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-14412.php @@ -0,0 +1,57 @@ += 8.0 + +declare(strict_types = 1); + +namespace Bug14412; + +/** + * @template-covariant T + * @phpstan-sealed BarCov|BazCov + */ +abstract class FooCov {} + +/** + * @template-covariant T + * @extends FooCov + */ +final class BarCov extends FooCov {} + +/** + * @template-covariant T + * @extends FooCov + */ +final class BazCov extends FooCov {} + +/** @param FooCov $foo */ +function testTemplateCovariant(FooCov $foo): string { + return match ($foo::class) { + BarCov::class => 'bar', + BazCov::class => 'baz', + }; +} + +/** + * @template T + * @phpstan-sealed BarInv|BazInv + */ +abstract class FooInv {} + +/** + * @template T + * @extends FooInv + */ +final class BarInv extends FooInv {} + +/** + * @template T + * @extends FooInv + */ +final class BazInv extends FooInv {} + +/** @param FooInv $foo */ +function testCovariantParam(FooInv $foo): string { + return match ($foo::class) { + BarInv::class => 'bar', + BazInv::class => 'baz', + }; +}