vimeo / psalm

A static analysis tool for finding errors in PHP applications
https://psalm.dev
MIT License
5.54k stars 659 forks source link

Psalm seems to forget (nested) type information after evaluation #11041

Open tfrommen opened 2 months ago

tfrommen commented 2 months ago

Consider the following snippet:

/**
 * @psalm-type Foo = object{
 *     bar: object{
 *         baz: string|null,
 *     },
 * }
 */

/** @var Foo */
$foo = (object) [
    'bar' => (object) [
        'baz' => null,
    ],
];

if ($foo->bar->baz === null) {
    $foo->bar->baz = 'foobarbaz';
    return;
}

$foo->bar->baz = 'foobarbaz';

As you can see, in lines 19 and 23, the exact same thing happens: We're setting a nested object property. Up until line 19, everything is fine, and Psalm happily fetches the nested property value in line 18 and also allows to set the value in line 19. However, in line 23, it no longer seems to know the type of the bar property; it inferred mixed.

Now, there are a lot of different things you can do to break it even more, or partially unbreak it.

For example, moving line 23 before the if will not trigger anything.

$foo->bar->baz = 'foobarbaz';

if ($foo->bar->baz === null) {
    $foo->bar->baz = 'foobarbaz';
    return;
}

Or storing the conditional in a variable will make the issue go away too:

$isNull = $foo->bar->baz === null;
if ($isNull) {
    $foo->bar->baz = 'foobarbaz';
    return;
}

$foo->bar->baz = 'foobarbaz';

Funnily, using is_null instead of manually comparing with null will work, too, even when inline:

if (is_null($foo->bar->baz)) {
    $foo->bar->baz = 'foobarbaz';
    return;
}

$foo->bar->baz = 'foobarbaz';

Or empty() or !isset() etc.


Is there anything I am doing wrong? Is Psalm?

Does that look like a bug to you too?

Happy to provide additional information, but it looks like Psalm discards (partial) information after a manual comparison inside a control structure (or something like that), and thus falls back to mixed, which then leads to various issues like MixedPropertyAssignment, MixedPropertyFetch, MixedArrayAssignment, MixedOperand etc.

psalm-github-bot[bot] commented 2 months ago

I found these snippets:

https://psalm.dev/r/e128b3793a ```php (object) [ 'baz' => null, ], ]; if ($foo->bar->baz === null) { $foo->bar->baz = 'foobarbaz'; return; } $foo->bar->baz = 'foobarbaz'; ``` ``` Psalm output (using commit 16b24bd): INFO: MixedPropertyAssignment - 23:1 - $foo->bar of type mixed cannot be assigned to ```