psalm icon indicating copy to clipboard operation
psalm copied to clipboard

Incorrect `PossiblyUndefinedVariable` issue reporting

Open boesing opened this issue 2 years ago • 6 comments

It seems that psalm somehow does not realize that due to the try-catch block, the variable $indicesByAlias is always set in case there are no errors.

Somehow, the catch is not parsed properly as we do use continue in there. Using continue should continue the loop and thus, there should be no codepath which actually leads to the $indicesByAlias being undefined.

https://psalm.dev/r/ef08112a04

boesing avatar Aug 03 '22 14:08 boesing

I found these snippets:

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


final class Foo
{
    /**
     * @template T of non-empty-string
     * @param non-empty-list<T> $aliases
     * @return non-empty-array<T,list<non-empty-string>>
     */
    private function detectIndicesForAliases(array $aliases): array
    {
        /** @var non-empty-array<T,list<non-empty-string>> $indices */
        $indices = array_combine(
            $aliases,
            array_fill(0, count($aliases), [])
        );

        foreach ($aliases as $alias) {
            try {
                /** @var list<non-empty-string> $indicesByAlias */
                $indicesByAlias = array_keys([]);
            } catch (Throwable $throwable) {
                if ($throwable->getCode() === 123) {
                    continue;
                }

                throw $throwable;
            }

            $indices[$alias] = $indicesByAlias;
        }

        return $indices;
    }
}
Psalm output (using commit 24f7920):

INFO: PossiblyUndefinedVariable - 31:32 - Possibly undefined variable $indicesByAlias defined in try block

psalm-github-bot[bot] avatar Aug 03 '22 14:08 psalm-github-bot[bot]

Will almost certainly be fixed by #7688, but I need to find some time to get back to it. The last I checked I had to also completely redo how we're handling variables defined in ifs to get it to work, and there were like 3-5 tests failing, but the failing cases were really annoying to figure out.

AndrolGenhald avatar Aug 03 '22 14:08 AndrolGenhald

I had a similar issue regarding a try-catch block: https://psalm.dev/r/b1102ead61. In this example, psalm does not correctly track the value of $string, which will always be a string unless an exception was caught (which is then re-thrown). Not sure if it's the same issue, I can open a new ticket for this if necessary.

alcaeus avatar Sep 16 '22 06:09 alcaeus

I found these snippets:

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

function returnsString(): string
{
    $exception = null;
    $string = null;
    
    try {
        $string = generatesStringOrThrows();
    } catch (Exception $exception) {
    }
    
    // Imagine we're doing other stuff here
    
    if ($exception !== null) {
        throw $exception;
    }
    
    return $string;
}

function generatesStringOrThrows(): string
{
    if (rand(0, 1) == 1) {
        return 'foo';
    }
    
    throw new Exception('nope');
}
Psalm output (using commit d957ff2):

ERROR: NullableReturnStatement - 19:12 - The declared return type 'string' for returnsString is not nullable, but the function returns 'null|string'

ERROR: InvalidNullableReturnType - 3:27 - The declared return type 'string' for returnsString is not nullable, but 'null|string' contains null

psalm-github-bot[bot] avatar Sep 16 '22 06:09 psalm-github-bot[bot]

@alcaeus That's a separate issue that comes up now and then, having types of variables be dependent on type checks of other variables is really complicated and not likely to be supported any time soon: https://psalm.dev/r/f6d94dc39e

AndrolGenhald avatar Sep 16 '22 14:09 AndrolGenhald

I found these snippets:

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

/** @var mixed */
$foo = null;
$isString = false;

if (is_string($foo)) {
    $isString = true;
}

if ($isString) {
    /** @psalm-trace $foo */; // Technically should be `string`
}
Psalm output (using commit d957ff2):

INFO: Trace - 12:29 - $foo: mixed

psalm-github-bot[bot] avatar Sep 16 '22 14:09 psalm-github-bot[bot]