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

Implement offset/index access type #1318

Merged
merged 3 commits into from
May 16, 2022

Conversation

rvanvelzen
Copy link
Contributor

Implementation for phpstan/phpstan#7094

Copy link
Member

@ondrejmirtes ondrejmirtes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise seems pretty straightforward 👍 (but myself I see it's pretty hard to explain this feature to another person and also realize to apply it in the right cases)

@@ -41,7 +46,7 @@ public function describe(VerbosityLevel $level): string

public function isResolvable(): bool
{
return !TypeUtils::containsTemplateType($this->type);
return !TypeUtils::containsTemplateType($this->type) && $this->type->isIterable()->yes();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't necessary, right? For example ArrayAccess has "keys" but isn't iterable unless paired with Traversable.

use PHPStan\Type\Type;

/** @api */
final class TemplateKeyOfType extends KeyOfType implements TemplateType
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's weird to add support for key-of in generics but not for value-of at the same time :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did have it implemented, but I wasn't able to think of a good use-case for it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't either but it looks like an ommission not to have it :)

@@ -41,7 +41,7 @@ public function describe(VerbosityLevel $level): string

public function isResolvable(): bool
{
return !TypeUtils::containsTemplateType($this->type);
return !TypeUtils::containsTemplateType($this->type) && $this->type->isIterable()->yes();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dtto

$type = $this->resolve($typeNode->type, $nameScope);
$offset = $this->resolve($typeNode->offset, $nameScope);

return new OffsetAccessType($type, $offset);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeNodeResolver could detect when the type doesn't make sense. For example when $type isn't offset-accessible or when $type–>hasOffsetValueType($offset) is no(). It could return new ErrorType() in that case and the appropriate rules will pick this ErrorType and complain about an "unresolvable type" automatically.

@rvanvelzen rvanvelzen marked this pull request as ready for review May 16, 2022 13:22
@rvanvelzen
Copy link
Contributor Author

I can't really come up with sensible tests for a value-of template type. I'd like to look at that separately

@ondrejmirtes
Copy link
Member

No problem, we don't need to inhibit the current PR because of that :)

@ondrejmirtes ondrejmirtes merged commit 9e73151 into phpstan:1.7.x May 16, 2022
@ondrejmirtes
Copy link
Member

Thank you very much!

/**
* @template K of key-of<T>
* @param K $key
* @param T[K] $val
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a difference between T[K] and value-of<T> for this line?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

T[K] will return only the types for offset K, where value-of<T> will return the types for all offset.

@Seldaek
Copy link
Contributor

Seldaek commented May 16, 2022

Awesome, thanks!

@rvanvelzen rvanvelzen deleted the index-access-type branch May 17, 2022 09:50
@ondrejmirtes
Copy link
Member

Hi @rvanvelzen, it would be great if you found a moment to document this for https://phpstan.org/writing-php-code/phpdoc-types, thanks a lot :)

@rvanvelzen
Copy link
Contributor Author

Will do soon :)

rvanvelzen added a commit to rvanvelzen/phpstan that referenced this pull request May 27, 2022
{
$attr = $this->getAttributes();
$attr[$key] = $val;
$this->setAttributes($attr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I can bug you with a question @rvanvelzen - I am trying to implement this pretty much got the same setup as in this test file, but I get a weird result here:

I have this in setAttribute:

        $attr = $this->getAttributes() ?? [];
        \PHPStan\dumpType($attr);
        \PHPStan\dumpType($key);
        $attr[$key] = $val;
        \PHPStan\dumpType($attr);
        $this->setAttributes($attr);

Producing this output:

  28     Dumped type: array{language?: string, timezone?: string}
  29     Dumped type: string
  31     Dumped type: non-empty-array<string, array<int, string>|string>
  32     Parameter #1 $attributes of method User::setAttributes() expects array{language?: string, timezone?: string}, non-empty-array<string, array<int, string>|string> given.

You can see that $key got resolved to string which is too wide and then it widens the $attr type and breaks the setAttributes call.

I verified though with a dumpType of $key in unsetAttribute the direct key-of<T> works fine:

  52     Dumped type: 'language'|'timezone'

So somehow the indirection with the following seems to break it?

     * @template K of key-of<T>
     * @param K $key

Any idea what could be the cause? Why is this not breaking in the test suite? If I switch to use @param key-of<T> $key then as you described above it cannot resolve $val correctly as K is not set anymore and it ends up failing as well because it assumes every array key can be a union of any value present in the array.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have time to look into the root cause right now, but I believe using K&key-of<T> as the parameter type should already improve the situation.

I didn't actually spend much time looking at the parameter types inside the methods, that definitely can use some polishing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that but could not make it work 100%, continuing this in phpstan/phpstan#7380 so it does not get lost.

@ondrejmirtes
Copy link
Member

FYI @rvanvelzen I realized this still isn't documented, can you please send an update to this page? https://phpstan.org/writing-php-code/phpdoc-types Thank you :)

@herndlm
Copy link
Contributor

herndlm commented Jul 15, 2024

Jfyi I think this was still not documented, or I just failed finding it 😊

@ondrejmirtes
Copy link
Member

It's already documented. Just use the search feature on the website :)

Screenshot 2024-07-15 at 21 41 11

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants