-
Notifications
You must be signed in to change notification settings - Fork 462
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
Conversation
There was a problem hiding this 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)
src/Type/KeyOfType.php
Outdated
@@ -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(); |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 :)
src/Type/ValueOfType.php
Outdated
@@ -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(); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
I can't really come up with sensible tests for a |
No problem, we don't need to inhibit the current PR because of that :) |
Thank you very much! |
/** | ||
* @template K of key-of<T> | ||
* @param K $key | ||
* @param T[K] $val |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
Awesome, thanks! |
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 :) |
Will do soon :) |
Documentation for phpstan/phpstan-src#1318
{ | ||
$attr = $this->getAttributes(); | ||
$attr[$key] = $val; | ||
$this->setAttributes($attr); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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 :) |
Jfyi I think this was still not documented, or I just failed finding it 😊 |
Implementation for phpstan/phpstan#7094