psalm icon indicating copy to clipboard operation
psalm copied to clipboard

Pushing to a list element changes the type of the list to `non-empty-array`

Open bkdotcom opened this issue 1 year ago • 7 comments

How to iterate over a list and modify each entry without psalm thinking we're modifying the list to a non-empty-array<int, mixed> ?

https://psalm.dev/r/d86080eff8

class Foo
{
    /** @var list<array> */
    public $list = array();
 
    public function someMethod() : void
    {
		/** @var mixed $v */
        foreach ($this->list as $i => $v) {
            self::modifyItem($i, $v);
        }
    }

    public function modifyItem(int $i, mixed $v) : void
    {
        if (!array_key_exists($i, $this->list)) {
            throw new InvalidArgumentException('unknown key');
        }
        $this->list[$i][] = array();
    }
}

bkdotcom avatar Jan 30 '24 16:01 bkdotcom

I found these snippets:

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

class Foo
{
    /** @var list<mixed> */
    public $list = array();
 
    public function someMethod() : void
    {
		/** @var mixed $v */
        foreach ($this->list as $i => $v) {
            self::modifyItem($i, $v);
        }
    }

    public function modifyItem(int $i, mixed $v) : void
    {
        $this->list[$i] = $v;
    }
}
Psalm output (using commit 9520223):

ERROR: PropertyTypeCoercion - 18:9 - $this->list expects 'list<mixed>',  parent type 'non-empty-array<int, mixed>' provided

psalm-github-bot[bot] avatar Jan 30 '24 16:01 psalm-github-bot[bot]

I'd just validate that the index exists: https://psalm.dev/r/9d49161cec

weirdan avatar Jan 30 '24 16:01 weirdan

I found these snippets:

https://psalm.dev/r/9d49161cec
<?php

class Foo
{
    /** @var list<mixed> */
    public $list = array();
 
    public function someMethod() : void
    {
		/** @var mixed $v */
        foreach ($this->list as $i => $v) {
            self::modifyItem($i, $v);
        }
    }

    public function modifyItem(int $i, mixed $v) : void
    {
        if (!array_key_exists($i, $this->list)) {
            throw new InvalidArgumentException('unknown key');
        }
        $this->list[$i] = $v;
    }
}
Psalm output (using commit 9520223):

No issues!

psalm-github-bot[bot] avatar Jan 30 '24 16:01 psalm-github-bot[bot]

@weirdan thanks / seems obvious
not immediately working in my real-world use-case... will spend a bit more time on it.

bkdotcom avatar Jan 30 '24 17:01 bkdotcom

@weirdan

https://psalm.dev/r/d86080eff8

    public function modifyItem(int $i, mixed $v) : void
    {
        if (!array_key_exists($i, $this->list)) {
            throw new InvalidArgumentException('unknown key');
        }
        $this->list[$i][] = array();   // $this->list expects 'list<array<array-key, mixed>>',  parent type 'non-empty-array<int, array<array-key, mixed>>' provided
    }

thoughts?

bkdotcom avatar Jan 30 '24 17:01 bkdotcom

I found these snippets:

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

class Foo
{
    /** @var list<array> */
    public $list = array();
 
    public function someMethod() : void
    {
		/** @var mixed $v */
        foreach ($this->list as $i => $v) {
            self::modifyItem($i, $v);
        }
    }

    public function modifyItem(int $i, mixed $v) : void
    {
        if (!array_key_exists($i, $this->list)) {
            throw new InvalidArgumentException('unknown key');
        }
        $this->list[$i][] = array();
    }
}
Psalm output (using commit 9520223):

ERROR: PropertyTypeCoercion - 21:9 - $this->list expects 'list<array<array-key, mixed>>',  parent type 'non-empty-array<int, array<array-key, mixed>>' provided

psalm-github-bot[bot] avatar Jan 30 '24 17:01 psalm-github-bot[bot]

possibly related #10449

bkdotcom avatar Feb 07 '24 01:02 bkdotcom