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 require-extends rules #2859
Conversation
'Learn more at https://phpstan.org/user-guide/discovering-symbols', | ||
], | ||
[ | ||
'PHPDoc tag @require-extends cannot contain generic type.', |
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 error messge is a bit misleading. my idea was that in @phpstan-require-extends SomeClass
we only name the class by its name, without generics params (generic classes can still be used at the using-class).
that way we don't need to validate the generics and therefore safe phpstan from validating them twice.
.. I think we just need a better error message.. wdyt?
Before I look at the source code, here's what I'd expect these rules to do: A rule to check the definition of
|
9309ac3
to
3a87cc0
Compare
I have implemented 2 rules for one thing I noticed: since we use |
|
||
public function getNodeType(): string | ||
{ | ||
return ClassLike::class; |
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.
Ask for InClassNode instead, it contains reflection already. You won't have to get the reflection from ReflectionProvider, and it works for anonymous classes too.
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.
but it doesn't work for trait defintions. it would not error on
/**
* @phpstan-require-implements SomeTrait
*/
trait InvalidTrait1 {}
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.
Two separate rules then please.
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.
For traits there's InTraitNode.
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.
InTraitNode
will only error for traits when a class exists which uses them. are you fine with that?
|
||
$errors = []; | ||
foreach ($implementsTags as $implementsTag) { | ||
$type = $implementsTag->getType(); |
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 whole foreach is too complicated. Just check that the type is ObjectType
.
This pull request has been marked as ready for review. |
|
||
public function getNodeType(): string | ||
{ | ||
return InTraitNode::class; |
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.
You're right, this one is better served by Node\Stmt\Trait_
- even when the trait is used multiple times, we just want to report the declaration once.
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.
Hey, I just pushed some cosmetic changes.
What still needs to be done here:
- Support
@psalm-
prefixes in PhpDocNodeResolver along with some tests: https://psalm.dev/docs/annotating_code/supported_annotations/#psalm-require-extends - There is a lot of literal duplication between RequireExtendsDefinitionTraitRule and RequireExtendsDefinitionClassRule. We usually solve this by introducing a "check" class that gets injected into rule classes to reuse the common code.
Also please note that after we merge the current two PRs and release them, I'd like you to work on properly introducing generics support for the new tags. But I want that to happen later and to not complicate the current PRs. Thanks.
@@ -56,6 +56,18 @@ public function testRule(): void | |||
'Trait IncompatibleRequireImplements\InvalidTrait4 requires using class to implement IncompatibleRequireImplements\SomeClass<T>, but IncompatibleRequireImplements\InvalidTraitUse4 does not.', | |||
137, | |||
], | |||
[ | |||
'Trait IncompatibleRequireImplements\ValidPsalmTrait requires using class to implement IncompatibleRequireImplements\RequiredInterface2, but AnonymousClass4fea61018e98554a605d5c87523a32cc does not.', |
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 message is wrong, you need to use getDisplayName on ClassReflection
Please use it consistently everywhere on ClassReflection instead of getName when building user-facing messages, thanks. |
916d7f6
to
f7bda5c
Compare
sorry missed one occurence |
$classReflection = $node->getClassReflection(); | ||
$extendsTags = $classReflection->getRequireExtendsTags(); | ||
|
||
if (count($extendsTags) === 0) { |
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 think we should error when a class defines 2 or more @*-require-extends
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.
Not error, but support and check all of them.
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.
Oh sorry, I misunderstood you. Please add a test and fix the rule.
9d4cdf6
to
e31c497
Compare
@@ -30,6 +31,11 @@ public function __construct( | |||
public function checkExtendsTags(Node $node, array $extendsTags): array | |||
{ | |||
$errors = []; | |||
|
|||
if (count($extendsTags) > 1) { | |||
$errors[] = RuleErrorBuilder::message(sprintf('PHPDoc tag @phpstan-require-extends can only be used once.'))->build(); |
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.
Oh right, this makes sense because you can't have multiple parents :)
Thank you! |
No description provided.