psalm icon indicating copy to clipboard operation
psalm copied to clipboard

Creating variables in `match` (`PHP 8`) can lead to `PossiblyUndefinedVariable` and `MixedArgument` false-positive warnings

Open AlexMinaev19 opened this issue 2 years ago • 3 comments

If you will try to create a variable in a new match expression you can get PossiblyUndefinedVariable and MixedArgument false-positive warnings.

I did some examples: https://psalm.dev/r/3fcba079df

With the switch statement it works fine and non-false-positive warnings are not appearing.

AlexMinaev19 avatar Jul 26 '22 12:07 AlexMinaev19

I found these snippets:

https://psalm.dev/r/3fcba079df
<?php

class A 
{
	public function some(): void 
    { 	   
    }
};
class B extends A {};

function fooMatch(string $case): void
{
    match($case) {
    	'a' => $object = new A(),
        'b' => $object = new B(),
        default => throw new RuntimeException("Undefined case: $case")
    };
    
    bar($object);
    
    bar(new A());
    bar(new B());
}

function fooMatch2(string $case): void
{    
    match($case) {
    	'a' => $object = new A(),
        'b' => $object = new B(),
        default => $object = new B()
    };
    
    bar($object);
    
    bar(new A());
    bar(new B());
}

function fooSwitch(string $case): void
{    
    switch($case) {
        case 'a': 
            $object = new A();
            break;
        case 'b':
            $object = new B();
            break;
        default:
            throw new RuntimeException("Undefined case: $case");
    }
    
    bar($object);
    
    bar(new A());
    bar(new B());
}

function fooSwitch2(string $case): void
{    
    switch($case) {
        case 'a': 
            $object = new A();
            break;
        case 'b':
            $object = new B();
            break;
        default:
            $object = new A();
    }
    
    bar($object);
    
    bar(new A());
    bar(new B());
}

function bar(A $object): void
{
    $object->some();
}
Psalm output (using commit 4b2935f):

INFO: PossiblyUndefinedVariable - 19:9 - Possibly undefined variable $object, first seen on line 14

INFO: MixedArgument - 19:9 - Argument 1 of bar cannot be mixed, expecting A

INFO: PossiblyUndefinedVariable - 33:9 - Possibly undefined variable $object, first seen on line 28

INFO: MixedArgument - 33:9 - Argument 1 of bar cannot be mixed, expecting A

psalm-github-bot[bot] avatar Jul 26 '22 12:07 psalm-github-bot[bot]

In Rust and Scala (where match is taken from), assigning the value of the match expression itself is more idiomatic anyway:

$object = match($case) {
    'a' => new A(),
    'b' => new B(),
    default => throw new RuntimeException("Undefined case: $case")
};

https://psalm.dev/r/e01ceb02bf

AndrolGenhald avatar Jul 26 '22 17:07 AndrolGenhald

I found these snippets:

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

class A 
{
	public function some(): void 
    { 	   
    }
};
class B extends A {};

function fooMatch(string $case): void
{
    $object = match($case) {
    	'a' => new A(),
        'b' => new B(),
        default => throw new RuntimeException("Undefined case: $case")
    };
    
    bar($object);
    
    bar(new A());
    bar(new B());
}

function fooMatch2(string $case): void
{    
    $object = match($case) {
    	'a' => new A(),
        'b' => new B(),
        default => $object = new B()
    };
    
    bar($object);
    
    bar(new A());
    bar(new B());
}

function fooSwitch(string $case): void
{    
    switch($case) {
        case 'a': 
            $object = new A();
            break;
        case 'b':
            $object = new B();
            break;
        default:
            throw new RuntimeException("Undefined case: $case");
    }
    
    bar($object);
    
    bar(new A());
    bar(new B());
}

function fooSwitch2(string $case): void
{    
    switch($case) {
        case 'a': 
            $object = new A();
            break;
        case 'b':
            $object = new B();
            break;
        default:
            $object = new A();
    }
    
    bar($object);
    
    bar(new A());
    bar(new B());
}

function bar(A $object): void
{
    $object->some();
}
Psalm output (using commit 4b2935f):

No issues!

psalm-github-bot[bot] avatar Jul 26 '22 17:07 psalm-github-bot[bot]