-
Notifications
You must be signed in to change notification settings - Fork 501
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
Native type does not know anything about purity #3797
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.
I disagree with this on a conceptual level. With a closure like fn () => sleep(5)
, only native types are involved and we still know it's impure.
With one-off fixes like that, we'd be chaising our own tail for a long time (finding more stuff to fix all the time, which is frustrating). I propose a different fix. In https://github.com/phpstan/phpstan-src/blob/2.1.x/src/Rules/PhpDoc/VarTagTypeRuleHelper.php there are about 9 isSuperTypeOf calls.
- Instead of calling them directly on a type, introduce a private method that accepts
$type, $varTagType
and calls isSuperTypeOf inside. That way we only need to change a single place. - After calling this
isSuperTypeOf
, in case an error would be reported, run one more check. Take the$type
(the native type of the expression), doType::toPhpDocNode()
and then create aType
again withTypeNodeResolver
. This will "clean up" everything that's not expressible with PHPDocs. - Call
isSuperTypeOf
again with the new$type
that's the result of operation from 2).
|
I think null is fine? Or an empty one. We won't be resolving any relative names, I hope. |
Anyway, this is just a guess what could work, maybe it's still going to break for some cases. |
25b2572
to
5afb299
Compare
5afb299
to
45824e4
Compare
|
||
private function isSuperTypeOfVarType(Type $type, Type $varTagType): TrinaryLogic | ||
{ | ||
$type = $this->typeNodeResolver->resolve($type->toPhpDocNode(), new NameScope(null, [])); |
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.
After calling this isSuperTypeOf, in case an error would be reported, run one more check.
Seems like if I do this every time it works. I would say it's avoid two calls to isSuperTypeOf
(?)
Also since sometimes checks are
!$this->isSuperTypeOfVarType($type, $varTagType)->yes()
and sometimes it's
$this->isSuperTypeOfVarType($type, $varTagType)->no()
It was unclear to me if I should have call toPhpDocNode
for maybe
results. So I did it every 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.
Please do what I suggested. I don't want to break any existing use-cases, that's why I want to do the "new" thing right before when the original error message would appear. To decrease the number of reported errors, not to potentially incraese 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.
Sure, but I don't understand how I'll know "an error would be reported" easily since sometime the usage will be
!$this->isSuperTypeOfVarType($type, $varTagType)->yes()
and sometimes it will be
$this->isSuperTypeOfVarType($type, $varTagType)->no()
.
The following implementation
private function isSuperTypeOfVarType(Type $type, Type $varTagType): TrinaryLogic
{
$result = $type->isSuperTypeOf($varTagType);
if ($result->yes()) {
return $result;
}
$type = $this->typeNodeResolver->resolve($type->toPhpDocNode(), new NameScope(null, []));
return $type->isSuperTypeOf($varTagType);
}
is not satisfying since for case where the usage is
$this->isSuperTypeOfVarType($type, $varTagType)->no()
I use the strategy toPhpDocNode
for nothing since the first isSuperTypeOf
wouldn't have report any error.
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 have the implementation
d8b9c48
But I dunno if it's satisfying enough
|
||
private function isSuperTypeOfVarType(Type $type, Type $varTagType): TrinaryLogic | ||
{ | ||
$type = $this->typeNodeResolver->resolve($type->toPhpDocNode(), new NameScope(null, [])); |
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.
Please do what I suggested. I don't want to break any existing use-cases, that's why I want to do the "new" thing right before when the original error message would appear. To decrease the number of reported errors, not to potentially incraese it.
We don't need to be too smart. We csn have two different methods - one for !yes(), one for no (). |
c4acd25
to
aa6ae1d
Compare
I created |
aa6ae1d
to
1b4409b
Compare
Friendly ping @ondrejmirtes ; this PR is ready to be re-reviewed :) |
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 worry the empty NameScope
might cause problems for generics. Can you please test it with @template T
above a method and then using the T
in inline @var
PHPDoc tag?
With a failing test I'll come up with a solution to the problem.
We need a test that executes the line with if ($type->isSuperTypeOf($varTagType)->yes()) {
return true;
}
$type = $this->typeNodeResolver->resolve($type->toPhpDocNode(), new NameScope(null, []));
return $type->isSuperTypeOf($varTagType)->yes(); Please debug what |
I fixed the test, it's failing now.
gives |
Now instead of $function = $scope->getFunction();
$nameScope = $this->fileTypeMapper->getNameScope(
$scope->getFile(),
$scope->isInClass() ? $scope->getClassReflection()->getName() : null,
$scope->isInTrait() ? $scope->getTraitReflection()->getName() : null,
$function !== null ? $function->getName() : null,
); It's a new method I just added in 1.12.x. |
dd605c7
to
f0d2c7a
Compare
f0d2c7a
to
58be765
Compare
Thanks ! It works well. |
try { | ||
$type = $this->typeNodeResolver->resolve($type->toPhpDocNode(), $this->createNameScope($scope)); | ||
} catch (NameScopeAlreadyBeingCreatedException) { | ||
return false; |
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.
Does this mean it's going to be reported, or it's not going to be reported? I'd rather opt for failing silently (to not report an error).
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 didn't know exactly what this exception means.
Before my PR we were doing just $type->isSuperTypeOf($varTagType)->no()
.
Does this mean it's going to be reported,
Yes. But the error was also reported before this PR. This won't introduce new error.
That's why I didn't chose the failing silently solution.
Now, we doing
- If
$type->isSuperTypeOf($varTagType)->no()
was OK before, early return - Try to create a namescope
- If we cannot create a namescope => Report an error (But this error is currently already reported by PHPStan because the check
$type->isSuperTypeOf($varTagType)->no()
was not ok). - If we can create a namescope => use it to remove some false-positive.
- If we cannot create a namescope => Report an error (But this error is currently already reported by PHPStan because the check
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.
Do you still prefer, I return true
?
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.
Yes. This would be an error the user cannot do with much. So I don't want to report 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.
Done
Perfect, thank you! From the method naming to the logic, this is top-notch! |
Can you please submit these regression tests? Thanks. |
Sure, I opened #3841 |
Closes phpstan/phpstan#12458