Skip to content
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

Missing key not reported after generic function #8166

Closed
iliubinskii opened this issue Oct 14, 2022 · 4 comments
Closed

Missing key not reported after generic function #8166

iliubinskii opened this issue Oct 14, 2022 · 4 comments

Comments

@iliubinskii
Copy link

Bug report

Missing key not reported after generic function, but reported for directly defined variable.

See it in playground:
https://phpstan.org/r/bf679482-96aa-49b2-8ac9-6f77a3a24fb1

Code snippet that reproduces the problem

<?php 

declare(strict_types = 1);

/**
 * @template T of int|string
 *
 * @param array<T,mixed> $arr
 *
 * @return array<T,string>
 */
function strings(array $arr): array
{
  // @phpstan-ignore-next-line
  return $arr;
}

$x = ['a' => 1];

$y = strings($x);

var_dump($x['b']);
var_dump($y['b']);

Expected output

22 | Offset 'b' does not exist on array{a: 1}.
23 | Offset 'b' does not exist on array{a: string}.

Did PHPStan help you today? Did it make you happy in any way?

Yes, great tool.

@TemirkhanN
Copy link

I don't think that it ever intended to work like that. Generics assert types - not values.

Would you expect the following declaration to check
a. return instanceof $arg
b. return === $arg

?

/**
* @param InputType $arg
* 
* @return InputType
function cloneIt(object $arg): object {
      return clone $arg;
}

@iliubinskii
Copy link
Author

I don't think that it ever intended to work like that. Generics assert types - not values.

$x = ['a' => 1];
var_dump($x['b']);

PHPStan reports this.
So, it understands that $x's type is array<'a',int>, and that key type is 'a' (this is enum type).

$y = strings($x);

Here, $x's type is <'a',int>.

@param array<T,mixed> $arr

In this param definition, T is key type.
Key type of $x is 'a' (enum).
So, we assume that T = 'a' (enum)

@return array<T,string>

By substituting T, we get return type array<'a',string> (array with enum key).

var_dump($y['b']);

Should be reported because $y type is <'a',string>


I understand that implementing this may be a lot of work.
But it may also be a small fix.
I can't know this.
So I am just showing what I would expect after working with typescript.

@ondrejmirtes ondrejmirtes added this to the Generics milestone Oct 26, 2022
@ondrejmirtes
Copy link
Member

Last night I came up with the idea that we mostly shouldn't generalize the generic type variables, except when they're in object generics, like Foo<int>.

Here's the resulting PR: phpstan/phpstan-src#2818

We can't do this for objects, because I want new Collection([1, 2, 3]) to become Collection<int>. This could be improved in the future thanks to this suggestion: #6732 (comment) (but it's pretty complex to implement so for now it has to wait).

The new behaviour now only applies to bleeding edge (https://phpstan.org/blog/what-is-bleeding-edge) so definitely enable it to get the taste of the future 👍

Copy link

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 12, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants