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

Native return type conflicting with parent declaration, while docbloc type with same definition accepted #8660

Closed
Ocramius opened this issue Nov 4, 2022 · 7 comments

Comments

@Ocramius
Copy link
Contributor

Ocramius commented Nov 4, 2022

Given following example ( https://psalm.dev/r/e51bdf311a ), psalm seems to infer the incorrect types in EnumField::fromMixedToValue()

<?php

/** @template-covariant TValue of scalar|DateTimeImmutable */
abstract class Field
{
    /** @psalm-return TValue|null */
    abstract public function from(mixed $value): mixed;
}

/** @extends Field<string> */
class EnumField extends Field
{
    public function from(mixed $value): string|null
    {
        if ($value === null) {
            return null;
        }

        return (string) $value;
    }
}
Psalm output (using commit 51838a5): 

ERROR: [InvalidReturnStatement](https://psalm.dev/128) - 19:16 - The inferred type 'string' does not match the declared return type 'null' for EnumField::from

ERROR: [InvalidReturnType](https://psalm.dev/011) - 13:41 - The declared return type 'null' for EnumField::from is incorrect, got 'null|string'
 
Psalm detected 1 [fixable issue(s)](https://psalm.dev/docs/manipulating_code/fixing/)

Adding an /** @return string|null */ solves the issue:

<?php

/** @template-covariant TValue of scalar|DateTimeImmutable */
abstract class Field
{
    /** @psalm-return TValue|null */
    abstract public function from(mixed $value): mixed;
}

/** @extends Field<string> */
class EnumField extends Field
{
    /** @return string|null */
    public function from(mixed $value): string|null
    {
        if ($value === null) {
            return null;
        }

        return (string) $value;
    }
}
Psalm output (using commit 51838a5): 

No issues!

I wonder if type inference is overriding native types here? 🤔

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/e51bdf311a
<?php

/** @template-covariant TValue of scalar|DateTimeImmutable */
abstract class Field
{
    /** @psalm-return TValue|null */
    abstract public function from(mixed $value): mixed;
}

/** @extends Field<string> */
class EnumField extends Field
{
    public function from(mixed $value): string|null
    {
        if ($value === null) {
            return null;
        }

        return (string) $value;
    }
}
Psalm output (using commit 51838a5):

ERROR: InvalidReturnStatement - 19:16 - The inferred type 'string' does not match the declared return type 'null' for EnumField::from

ERROR: InvalidReturnType - 13:41 - The declared return type 'null' for EnumField::from is incorrect, got 'null|string'

@orklah
Copy link
Collaborator

orklah commented Nov 4, 2022

I think it's Psalm failing to correctly infer the type of the template when it's redefined in a child class.

The return type of from is (TValue as scalar|DateTimeImmutable)|null in Field. When EnumField inherits Field, from must take the most precise return type (which is still the parent docblock). It should become (TValue as string)|null because EnumFields extends Field<string> but I think it fails to do that and just infers something like never|null which become null.

I'm not sure where this should be fixed though

@Ocramius
Copy link
Contributor Author

Ocramius commented Nov 4, 2022

Is there any way this can be reduced further, perhaps? 🤔

We have suspicions, but we don't know what's going on precisely.

/cc @guidobonuzzi

@orklah
Copy link
Collaborator

orklah commented Nov 5, 2022

Well, this shows the same thing: https://psalm.dev/r/57e10b684b with every unnecessary thing stripped (and I replaced null with bool just in case)

I really think it's an issue with TValue just being dropped by Psalm's inference.

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/57e10b684b
<?php

/** @template TValue */
abstract class Field
{
    /** @psalm-return TValue|bool */
    abstract public function from(mixed $value): mixed;
}

/** @extends Field<string> */
class EnumField extends Field
{
    public function from(mixed $value): string|bool
    {
        return 'a';
    }
}
Psalm output (using commit d0be59e):

ERROR: InvalidReturnStatement - 15:16 - The inferred type ''a'' does not match the declared return type 'bool' for EnumField::from

ERROR: InvalidReturnType - 13:41 - The declared return type 'bool' for EnumField::from is incorrect, got ''a''

@orklah
Copy link
Collaborator

orklah commented Dec 18, 2022

I'm surprised but it seems to have been fixed :)

@orklah orklah closed this as completed Dec 18, 2022
@danog
Copy link
Collaborator

danog commented Dec 18, 2022

It was thanks to #8926, which fixed a bug I introduced earlier when I improved the intersection logic :)

Ocramius added a commit to Ocramius/event-sourcing-workshop that referenced this issue Dec 19, 2022
Ocramius added a commit to Ocramius/event-sourcing-workshop that referenced this issue Dec 19, 2022
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