vimeo / psalm

A static analysis tool for finding errors in PHP applications
https://psalm.dev
MIT License
5.57k stars 659 forks source link

Covariance of class strings for templates #11096

Open egst opened 2 months ago

egst commented 2 months ago

A: https://psalm.dev/r/4b9945a061

B: https://psalm.dev/r/7698fe63cc

When overriding the createFoo method, Psalm correctly recognizes IntFoo (in A) or Foo<int> (in B) to be covariant with Foo<T> since T is int.

However, when overriding the getFooClass method, Psalm fails to recognize class-string<IntFoo> (in A) or class-string<Foo<int>> (in B) to be covariant with class-string<Foo<T>>.

Am I missing something, or this a bug?

psalm-github-bot[bot] commented 2 months ago

I found these snippets:

https://psalm.dev/r/4b9945a061 ```php */ class IntFoo extends Foo {} /** @template T */ abstract class Bar { /** @return Foo */ abstract function createFoo (): Foo; /** @return class-string> */ abstract function getFooClass (): string; } /** @extends Bar */ class Baz extends Bar { function createFoo (): IntFoo { return new IntFoo; } // OK /** @return class-string */ function getFooClass (): string { return IntFoo::class; } // Class string not covariant } ``` ``` Psalm output (using commit 16b24bd): ERROR: ImplementedReturnTypeMismatch - 20:17 - The inherited return type 'class-string>' for Bar::getFooClass is different to the implemented return type for Baz::getfooclass 'class-string' ```
https://psalm.dev/r/7698fe63cc ```php */ class IntFoo extends Foo {} /** @template T */ abstract class Bar { /** @return Foo */ abstract function createFoo (): Foo; /** @return class-string> */ abstract function getFooClass (): string; } /** @extends Bar */ class Baz extends Bar { /** @return Foo */ function createFoo (): Foo { return new IntFoo; } // OK /** @return class-string> */ function getFooClass (): string { return IntFoo::class; } // Class string not covariant } ``` ``` Psalm output (using commit 16b24bd): ERROR: ImplementedReturnTypeMismatch - 21:17 - The inherited return type 'class-string>' for Bar::getFooClass is different to the implemented return type for Baz::getfooclass 'class-string>' ```