Skip to content

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

@tfrommen

Description

@tfrommen

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions