Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support exhaustiveness checks for sealed interfaces implemented by an enum #2

Open
stof opened this issue Sep 29, 2023 · 5 comments
Open

Comments

@stof
Copy link

stof commented Sep 29, 2023

#[Sealed(permits: [ColorFormatEnum::class, SpanColorFormat::class])]
interface ColorFormat
{
}


enum ColorFormatEnum implements ColorFormat
{
    /**
     * A color defined using the `rgb()` or `rgba()` functions.
     */
    case rgbFunction;
    /**
     * A color defined using the `hsl()` or `hsla()` functions.
     */
    case hslFunction;
}

final class SpanColorFormat implements ColorFormat
{
    private readonly string $original;

    public function __construct(string $original)
    {
        $this->original = $original;
    }

    public function getOriginal(): string
    {
        return $this->original;
    }
}

interface SassColor {
    public function getFormat(): ColorFormat;
}

function writeColorWithMatch(SassColor $value): void
{
        $format = $value->getFormat();
            match ($format) {
                ColorFormatEnum::rgbFunction => writeRgb($value),
                ColorFormatEnum::hslFunction => writeHsl($value),
                default => write($format->getOriginal()),
            };
}

function writeColorWithIf(SassColor $value): void
{
        $format = $value->getFormat();

            if ($format === ColorFormatEnum::rgbFunction) {
                writeRgb($value);
            } elseif ($format === ColorFormatEnum::hslFunction) {
                writeHsl($value);
            } else {
                write($format->getOriginal());
            }
}

function writeRgb(SassColor $color): void {
// Omitted
}
function writeHsl(SassColor $color): void {
// Omitted
}

function write(string $text): void {
// Omitted
}

In the code snippet above, as ColorFormat allows only ColorFormatEnum and SpanColorFormat and I'm checking explicitly all cases of the enum, I would expect the default branch of writeColorWithMatch and the else branch of writeColorWithIf to know that $format must be a SpanColorFormat there (and so calling getOriginal is OK.

@jiripudil
Copy link
Owner

Hi, at first glance, this might be a limitation on the side of PHPStan's type subtraction logic. I'll try and look into it, see if that's the case and if I can find a way around it

@stof
Copy link
Author

stof commented Oct 3, 2023

Or maybe this is something that should be fixed in the phpstan side indeed. I haven't debugged this.

@stof
Copy link
Author

stof commented Oct 31, 2023

@jiripudil did you manage to look at this ?

@stof
Copy link
Author

stof commented Nov 17, 2023

for reference, replacing the sealed interface with a union SpanColorFormat|ColorFormatEnum in the SassColor::getFormat return type removes the error reported by phpstan. So it looks like phpstan already supports the exhaustiveness check when eliminating enum cases one by one in case an actual union is used.

@stof
Copy link
Author

stof commented Nov 17, 2023

I opened a phpstan issue about that: phpstan/phpstan#10148

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants