Psalm: Input param not traced when covariance changes

Created on 7 Dec 2020  路  9Comments  路  Source: vimeo/psalm

Example: https://psalm.dev/r/0f0ce4be1e

Explanation

Just by changing return type of CovariantUserBuilder::create, psalm doesn't trace $data param correctly.

bug

All 9 comments

I found these snippets:


https://psalm.dev/r/0f0ce4be1e

<?php
declare(strict_types=1);

class User{}

class CovariantUser extends User{}

/**
 * @psalm-template T
 */
interface BuilderInterface 
{
    /**
     * @psalm-param T $data
     */
    public function create($data): User;
}

/**
 * @implements BuilderInterface<array{first_name: string}>
 */
class UserBuilder implements BuilderInterface
{
    /** @psalm-suppress InvalidReturnType */
    public function create($data): User
    {
        /** @psalm-trace $data */
    }
}

/**
 * @implements BuilderInterface<array{first_name: string}>
 */
class CovariantUserBuilder implements BuilderInterface
{
    /** @psalm-suppress InvalidReturnType */
    public function create($data): CovariantUser
    {
        /** @psalm-trace $data */
    }
}
Psalm output (using commit f5dd6e7):

INFO: Trace - 27:0 - $data: array{first_name: string}

INFO: Trace - 39:0 - $data: T:BuilderInterface as mixed

Simplified a bit: https://psalm.dev/r/fc69cedcb9

I found these snippets:


https://psalm.dev/r/fc69cedcb9

<?php declare(strict_types=1);

/** @psalm-template T */
interface BuilderInterface {
    /** @psalm-param T $data */
    public function create($data): Exception;
}

/** @implements BuilderInterface<int> */
class CovariantUserBuilder implements BuilderInterface {
    /** @psalm-suppress InvalidReturnType */
    public function create($data): RuntimeException {
        /** @psalm-trace $data */; // expected: int
    }
}
Psalm output (using commit 8a76a43):

INFO: Trace - 13:34 - $data: T:BuilderInterface as mixed

This is a very buggy bug

If you need some more example, I believe there is a similar issue in phpDocumentor codebase with last psalm release. I could try to dig it out.

The fix, for now, is to use the same param types as would be evaluated when calling the method externally.

Because you're defining a different return type from the one you inherited, that means $data is now typed to mixed.

This follows the rule introduced in 4.2.0: a function signature (parameter and return types) can only come from one method. If you redefine either a parameter type or a return type in a child method, that method must also provide local param types.

that means $data is now typed to mixed

I set reportMixedIssues="true"; does this mean the fix will report problems?

You get a MissingParamType issue instead

Actually this code will be allowed just fine, sorry, false alarm.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Pierstoval picture Pierstoval  路  3Comments

vudaltsov picture vudaltsov  路  3Comments

albe picture albe  路  3Comments

Ocramius picture Ocramius  路  3Comments

tux-rampage picture tux-rampage  路  3Comments