psalm icon indicating copy to clipboard operation
psalm copied to clipboard

False-positive `PossiblyNullArgument` when using `??=` on `ArrayAccess` with nullable `offsetGet()`

Open Shira-3749 opened this issue 11 months ago • 3 comments

When using ??= on an implementation of ArrayAccess whose offsetGet() can return null for nonexistent offsets, I'm getting a false-positive PossiblyNullArgument:

https://psalm.dev/r/f82e852479

// Psalm infers $foo as Foo|null even though that's impossible
$foo = ($map['foo'] ??= new Foo());

The offsetGet() method is not called at all when ??= is used so it is impossible for it to influence the call to offsetSet() or the result of the ??= operator:

https://3v4l.org/vQqLV

Shira-3749 avatar Mar 25 '24 12:03 Shira-3749

I found these snippets:

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

/**
 * @template TKey of array-key
 * @template TValue
 * @implements ArrayAccess<TKey, TValue>
 */
abstract class Map implements ArrayAccess
{
    /**
     * @param TKey $offset
     */
    abstract function offsetExists(mixed $offset): bool;

    /**
     * @param TKey $offset
     * @return TValue|null
     */
    abstract function offsetGet(mixed $offset): mixed;

    /**
     * @param TKey $offset
     * @param TValue $value
     */
    abstract function offsetSet(mixed $offset, mixed $value): void;

    /**
     * @param TKey $offset
     */
    abstract function offsetUnset(mixed $offset): void;
}

class Foo
{}

/**
 * @param Map<string, Foo> $map
 * @psalm-suppress UnusedVariable
 */
function example(Map $map): void
{
    /** @psalm-trace $foo */
    $foo = ($map['foo'] ??= new Foo());
}
Psalm output (using commit ef3b018):

ERROR: PossiblyNullArgument - 43:13 - Argument 2 of Map::offsetSet cannot be null, possibly null value provided

INFO: Trace - 43:5 - $foo: Foo|null

psalm-github-bot[bot] avatar Mar 25 '24 12:03 psalm-github-bot[bot]

Duplicate of https://github.com/vimeo/psalm/issues/9960?

jacekkarczmarczyk avatar Apr 11 '24 01:04 jacekkarczmarczyk

It could be the same cause, just with ??= instead of ??. Also in this case offsetGet() does not get called at all.

Shira-3749 avatar Apr 11 '24 10:04 Shira-3749