New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wrong template infer inheritance #4524
Comments
Hey @thomasvargiu, can you reproduce the issue on https://psalm.dev ? |
The problem isn't just on stubs: Skipping the empty |
I found these snippets: https://psalm.dev/r/72dd0c2084<?php
interface Pointed
{
/**
* @param mixed $value
* @return mixed
*/
public static function of($value);
}
/**
* @template T
*/
interface Applicative extends Pointed
{
/**
* @template U
* @param U $value
* @return Applicative<U>
*/
public static function of($value);
}
/**
* @template T
* @template-extends Applicative<T>
*/
interface Monad extends Applicative
{
}
/**
* @template T
* @template-implements Monad<T>
*/
class FakeMonad implements Monad
{
/** @psalm-suppress InvalidReturnType */
public static function of($value)
{
}
}
/**
* @template T
* @template-implements Applicative<T>
*/
class FakeMonad2 implements Applicative
{
/** @psalm-suppress InvalidReturnType */
public static function of($value)
{
}
}
/** @psalm-trace $value */
$value = FakeMonad::of('foo');
/** @psalm-trace $value2 */
$value2 = FakeMonad2::of('foo');
|
Simplified: https://psalm.dev/r/0b06235633 |
I found these snippets: https://psalm.dev/r/0b06235633<?php
/**
* @template T
*/
interface A {
/**
* @template U
* @param callable(T): U $func
* @return U
*/
function test(callable $func);
}
/**
* @template T
* @template-extends A<T>
*/
interface B extends A
{
}
/**
* @template T
* @template-extends B<T>
*/
interface C extends B
{
}
/** @var B<mixed> $interface */
$c = $interface->test(fn (int $foo) => 5); // error is thrown correctly using B
/** @var C<mixed> $interface */
$c = $interface->test(fn (int $foo) => 5); // no errors using C
|
Even more concise version:
|
@muglug Isn't resolved yet. If It works:
|
I found these snippets: https://psalm.dev/r/40e629c932<?php
interface Pointed
{
/**
* @param mixed $value
* @return mixed
*/
public static function of($value);
}
/**
* @template T
*/
interface A extends Pointed
{
/**
* @template U
* @param U $value
* @return A<U>
*/
public static function of($value);
}
/**
* @template T
* @template-extends A<T>
*/
interface B extends A
{
}
/**
* @template T
* @template-implements B<T>
*/
class Foo implements B
{
/** @psalm-suppress InvalidReturnType */
public static function of($value)
{
}
}
/** @psalm-trace $foo */
$foo = Foo::of('foo');
https://psalm.dev/r/948622b9f6<?php
interface Pointed
{
public static function of($value);
}
/**
* @template T
*/
interface A extends Pointed
{
/**
* @template U
* @param U $value
* @return A<U>
*/
public static function of($value);
}
/**
* @template T
* @template-extends A<T>
*/
interface B extends A
{
}
/**
* @template T
* @template-implements B<T>
*/
class Foo implements B
{
/** @psalm-suppress InvalidReturnType */
public static function of($value)
{
}
}
/** @psalm-trace $foo */
$foo = Foo::of('foo');
https://psalm.dev/r/ff098b1e0a<?php
interface Pointed
{
/**
* @param mixed $value
* @return mixed
*/
public static function of($value);
}
/**
* @template T
*/
interface A extends Pointed
{
/**
* @template U
* @param U $value
* @return A<U>
*/
public static function of($value);
}
/**
* @template T
* @template-implements A<T>
*/
class Foo implements A
{
/** @psalm-suppress InvalidReturnType */
public static function of($value)
{
}
}
/** @psalm-trace $foo */
$foo = Foo::of('foo');
|
Yeah, two inherited interfaces each with their own docblock confuses Psalm – it's not immediately clear which docblock should take precedence. |
So, should you open this issue again? Or are you working on it? |
Sorry, I was being a bit lazy. |
@muglug I was about to open a PR. I was fixing it too (I was running tests), changing the following line inverting parameters to mantain an ordered list of
|
Problem is you can't always rely on that ordering – |
But thanks, I always appreciate PRs! |
@muglug I think I found another issue about infer and inheritance: https://psalm.dev/r/7f937bb1d0 Since |
I found these snippets: https://psalm.dev/r/7f937bb1d0<?php
/**
* @template T
*/
interface A
{
/**
* @template F
* @param F $value
* @return A<F>
*/
public function map($value): A;
}
/**
* @template T
* @template-extends A<T>
*/
interface B extends A
{
/**
* @template F
* @param F $value
* @return B<F>
*/
public function map($value): A;
}
/**
* @template T
* @template-implements B<T>
*/
class Foo implements B
{
/**
* @var T
*/
private $value;
/**
* @param T $value
*/
public function __construct($value)
{
$this->value = $value;
}
public function map($value): A
{
return new Foo($value);
}
}
$foo = new Foo('str');
/** @psalm-trace $value */
$value = $foo->map('string');
|
EDIT: see comment below, this isn't a problem with stubs, I can reproduce it on playground: https://psalm.dev/r/72dd0c2084
I'm creating a plugin with stubs, and I created a dedicated branch for this issue.
The problem is with method
Identity::map()
.It's the implementation from the interface Functor, which is another stub with the same signature.
Psalm doesn't recognize the return type as you can see from the CI (https://github.com/thomasvargiu/psalm-plugin-fantasy-land/runs/1376311199?check_suite_focus=true#step:8:50).
So, calling the following method with
fn (T): int
returns aFunctor<empty>
instead aFunctor<int>
.The weird thing is that if I remove the docblock from the the original interface file it works as expected.
The text was updated successfully, but these errors were encountered: