phpstan icon indicating copy to clipboard operation
phpstan copied to clipboard

Template type is overriden by the native typehint of the method

Open VincentLanglet opened this issue 1 year ago • 6 comments

Bug report

Code snippet that reproduces the problem

https://phpstan.org/r/97f95150-498a-4722-a0fb-cbe13af32bf3 is working fine but https://phpstan.org/r/24aaa27a-e6a5-478b-a85f-b25af078d5af is throwing a lot of errors:

  • Cannot access property $foo on array|SimpleXMLElement.
  • PHPStan is asking to specify in iterable type array|SimpleXMLElement.

In the same way, https://phpstan.org/r/adeaffc9-e53c-48ee-93aa-748069f6308b is working fine https://phpstan.org/r/f5f77c8d-2329-4ec4-ac9c-4fde412fd663 is throwing an error:

  • Offset 'count' does not exist on array{count: int}|string.

Expected output

No errors

VincentLanglet avatar Aug 12 '22 16:08 VincentLanglet

I saw you marked this as an easy fixes @ondrejmirtes. I'd like to work on it ; any advice what should I do/where should I look ?

VincentLanglet avatar Aug 24 '22 14:08 VincentLanglet

IMHO It's caused by this commit: https://github.com/phpstan/phpstan-src/commit/c3d09898e8195346c66d66fa7908fa65e27e451a

This shows the current behaviour: https://phpstan.org/r/0c1858ce-f4bc-45cc-8742-3a161b0f0022

We probably need to rethink it for generics.

ondrejmirtes avatar Aug 24 '22 14:08 ondrejmirtes

Didn't know this was an expected behavior for non-generic. I'm surprised by the behavior

	/**
	 * @param array<int, string> $foo
	 */
	public function doBaz(array|false $foo): void
	{
		assertType('array<int, string>|false', $foo);
		assertNativeType('array|false', $foo);

		assertType('array<int, string>|false', $this->doLorem());
	}

It's different for Psalm https://psalm.dev/r/ac26cd17e4

But, yeah, it should at least respect generics.

Edit: It's really weird for psalm cf https://psalm.dev/r/98340f0135 and https://psalm.dev/r/3a3b729ab0

VincentLanglet avatar Aug 24 '22 14:08 VincentLanglet

I tried to debug the getType method, but the issue I have is that when I dump the $phpDocType I get an PHPStan\Type\ArrayType described as array<int, int>.

Is there a way to know the type is coming from the generic ?

VincentLanglet avatar Aug 24 '22 15:08 VincentLanglet

Is there a way to know the type is coming from the generic ?

I saw you contributed a lot @staabm ; maybe do you know how/if this is possible ?

VincentLanglet avatar Sep 06 '22 16:09 VincentLanglet

I tried to debug the getType method, but the issue I have is that when I dump the $phpDocType I get an PHPStan\Type\ArrayType described as array<int, int>.

You should use a step-debugger, like xdebug. Otherwise its a time consuming try & error.

maybe do you know how/if this is possible ?

I am not that much into generics.

staabm avatar Sep 06 '22 17:09 staabm

I tried to solve

/**
 * @phpstan-template T of array|false
 */
interface Templated
{
	/**
	 * @param T $foo
	 */
	public function doBaz(array|false $foo): void;
}

/**
 * @phpstan-implements Templated<array<int, string>>
 */
class BarTemplated implements Templated
{
	public function doBaz(array|false $foo): void
	{
		assertType('array<int, string>', $foo);
		assertNativeType('array', $foo);
	}
}

first, but doesn't seems to be an easy fix to me.

I still don't understand how/where the template are resolved. If I could do the intersection of the decideType value and the resolved template type, this could be the solution...

VincentLanglet avatar Oct 23 '22 21:10 VincentLanglet