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

False-positive TypeDoesNotContainType while using conditional type for return #8286

Closed
MaSpeng opened this issue Jul 19, 2022 · 3 comments
Closed

Comments

@MaSpeng
Copy link

MaSpeng commented Jul 19, 2022

Description

While trying to use a conditional type for a method return value based on the cases of an UnitEnum/BackendEnum, a TypeDoesNotContainType issue arises indicating that the value of parameter with the enum type could never be one of the enum cases.

The issue exists for me while using UnitEnum, BackedEnum<int>, and BackendEnum<string>.

Psalm Snippets

UnitEnum: https://psalm.dev/r/3a92e4a91f
BackedEnum (int): https://psalm.dev/r/cd28e96568
BackedEnum (string): https://psalm.dev/r/4c57a77aa8

PHPStan Snipppets

UnitEnum: https://phpstan.org/r/667b3feb-6a7e-4444-8666-64b37759c0d4
BackedEnum (int): https://phpstan.org/r/01028541-f166-4c1c-9ad1-f6c937fccb7f
BackedEnum (string): https://phpstan.org/r/04041665-afd1-4ebe-9b9b-5ed4725895ff

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/3a92e4a91f
<?php

enum Enum {
    case First;
    
    case Second;
	
	case Third;
}

class FirstId {}

class SecondId {}

class Test {
    /**
     * @return (
     *     $enum is Enum::First
     *         ? \Generator<FirstId>
     *         : (
     *             $enum is Enum::Second
     *             ? \Generator<SecondId>
     *             : never
     *     )
     * )
     **/
    public static function method(Enum $enum): \Generator
    {
        match ($enum) {
            Enum::First => yield new FirstId(),
            Enum::Second => yield new SecondId(),
            default => throw new \LogicException(),
        };
    }
}

/** @psalm-trace $firstIdGenerator **/
$firstIdGenerator = Test::method(Enum::First); // expected type is "Generator<mixed, FirstId, mixed, mixed>" got "Generator<mixed, FirstId, mixed, mixed>"

/** @psalm-trace $secondIdGenerator **/
$secondIdGenerator = Test::method(Enum::Second); // expected type is "Generator<mixed, SecondId, mixed, mixed>" got "Generator<mixed, FirstId, mixed, mixed>"

/** @psalm-trace $never **/
$never = Test::method(Enum::Third); // expected type is "never" got "Generator<mixed, FirstId, mixed, mixed>"
Psalm output (using commit 33b553e):

INFO: Trace - 38:1 - $firstIdGenerator: Generator<mixed, FirstId, mixed, mixed>

INFO: Trace - 41:1 - $secondIdGenerator: Generator<mixed, FirstId, mixed, mixed>

INFO: Trace - 44:1 - $never: Generator<mixed, FirstId, mixed, mixed>

ERROR: TypeDoesNotContainType - 30:13 - Type TGeneratedFromParam0:fn-test::method as Enum for $enum is never =enum(Enum::First)

ERROR: TypeDoesNotContainType - 31:13 - Type TGeneratedFromParam0:fn-test::method as Enum for $enum is never =enum(Enum::Second)
https://psalm.dev/r/cd28e96568
<?php

enum Enum: int {
    case First = 1;
    
    case Second = 2;
	
	case Third = 3;
}

class FirstId {}

class SecondId {}

class Test {
    /**
     * @param Enum $enum
     *
     * @return (
     *     $enum is Enum::First
     *         ? \Generator<FirstId>
     *         : (
     *             $enum is Enum::Second
     *             ? \Generator<SecondId>
     *             : never
     *     )
     * )
     **/
    public static function method(Enum $enum): \Generator
    {
        match ($enum) {
            Enum::First => yield new FirstId(),
            Enum::Second => yield new SecondId(),
            default => throw new \LogicException(),
        };
    }
}

/** @psalm-trace $firstIdGenerator **/
$firstIdGenerator = Test::method(Enum::First); // expected type is "Generator<mixed, FirstId, mixed, mixed>" got "Generator<mixed, FirstId, mixed, mixed>"

/** @psalm-trace $secondIdGenerator **/
$secondIdGenerator = Test::method(Enum::Second); // expected type is "Generator<mixed, SecondId, mixed, mixed>" got "Generator<mixed, FirstId, mixed, mixed>"

/** @psalm-trace $never **/
$never = Test::method(Enum::Third); // expected type is "never" got "Generator<mixed, FirstId, mixed, mixed>"
Psalm output (using commit 33b553e):

INFO: Trace - 40:1 - $firstIdGenerator: Generator<mixed, FirstId, mixed, mixed>

INFO: Trace - 43:1 - $secondIdGenerator: Generator<mixed, FirstId, mixed, mixed>

INFO: Trace - 46:1 - $never: Generator<mixed, FirstId, mixed, mixed>

ERROR: TypeDoesNotContainType - 32:13 - Type TGeneratedFromParam0:fn-test::method as Enum for $enum is never =enum(Enum::First)

ERROR: TypeDoesNotContainType - 33:13 - Type TGeneratedFromParam0:fn-test::method as Enum for $enum is never =enum(Enum::Second)
https://psalm.dev/r/4c57a77aa8
<?php

enum Enum: string {
    case First = 'first';
    
    case Second = 'second';
	
	case Third = 'third';
}

class FirstId {}

class SecondId {}

class Test {
    /**
     * @param Enum $enum
     *
     * @return (
     *     $enum is Enum::First
     *         ? \Generator<FirstId>
     *         : (
     *             $enum is Enum::Second
     *             ? \Generator<SecondId>
     *             : never
     *     )
     * )
     **/
    public static function method(Enum $enum): \Generator
    {
        match ($enum) {
            Enum::First => yield new FirstId(),
            Enum::Second => yield new SecondId(),
            default => throw new \LogicException(),
        };
    }
}

/** @psalm-trace $firstIdGenerator **/
$firstIdGenerator = Test::method(Enum::First); // expected type is "Generator<mixed, FirstId, mixed, mixed>" got "Generator<mixed, FirstId, mixed, mixed>"

/** @psalm-trace $secondIdGenerator **/
$secondIdGenerator = Test::method(Enum::Second); // expected type is "Generator<mixed, SecondId, mixed, mixed>" got "Generator<mixed, FirstId, mixed, mixed>"

/** @psalm-trace $never **/
$never = Test::method(Enum::Third); // expected type is "never" got "Generator<mixed, FirstId, mixed, mixed>"
Psalm output (using commit 33b553e):

INFO: Trace - 40:1 - $firstIdGenerator: Generator<mixed, FirstId, mixed, mixed>

INFO: Trace - 43:1 - $secondIdGenerator: Generator<mixed, FirstId, mixed, mixed>

INFO: Trace - 46:1 - $never: Generator<mixed, FirstId, mixed, mixed>

ERROR: TypeDoesNotContainType - 32:13 - Type TGeneratedFromParam0:fn-test::method as Enum for $enum is never =enum(Enum::First)

ERROR: TypeDoesNotContainType - 33:13 - Type TGeneratedFromParam0:fn-test::method as Enum for $enum is never =enum(Enum::Second)

@weirdan
Copy link
Collaborator

weirdan commented Feb 14, 2023

This is partially fixed by #9286. All traces produce expected results, but TypeDoesNotContainType is still there.

@weirdan
Copy link
Collaborator

weirdan commented Aug 13, 2023

TypeDoesNotContainType is no longer reported, and results match those of PHPStan

@weirdan weirdan closed this as completed Aug 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants