Skip to content

Match on ::class reports unhandled values for generic sealed class hierarchy #14412

@mhert

Description

@mhert

Bug report

Match expressions on $foo::class against a @phpstan-sealed hierarchy report "Match expression does not handle remaining values" even when all allowed subtypes are covered — but only when the sealed class has generic type parameters (@template). The non-generic case was fixed in phpstan/phpstan-src#5305.

Code snippet that reproduces the problem

<?php // lint >= 8.0

declare(strict_types = 1);

namespace MatchGenericSealedClassString;

/**
 * @template-covariant T
 * @phpstan-sealed BarCov|BazCov
 */
abstract class FooCov {}

/** @template-covariant T @extends FooCov<T> */
final class BarCov extends FooCov {}

/** @template-covariant T @extends FooCov<T> */
final class BazCov extends FooCov {}

/** @param FooCov<string> $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<T> */
final class BarInv extends FooInv {}

/** @template T @extends FooInv<T> */
final class BazInv extends FooInv {}

/** @param FooInv<covariant string> $foo */
function testCovariantParam(FooInv $foo): string {
	return match ($foo::class) {
		BarInv::class => 'bar',
		BazInv::class => 'baz',
	};
}

A fix is proposed here: phpstan/phpstan-src#5367

Code snippet that reproduces the problem

https://phpstan.org/r/3b28cc01-3b28-4ffd-993b-6ec56dd720ab

Expected output

No errors — all allowed subtypes of the sealed hierarchy are covered. Removing @template-covariant T from the classes makes the error disappear, so the issue is specific to generic sealed hierarchies.

Did PHPStan help you today? Did it make you happy in any way?

PHPStan helps me almost every day. It's the number one tool to mainatain code quality and prevent bugs. Thanks for your awesome work!

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions