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 RequireExtendsPropertiesClassReflectionExtension #2856

Merged
merged 1 commit into from Jan 10, 2024

Conversation

staabm
Copy link
Contributor

@staabm staabm commented Jan 5, 2024

No description provided.

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.

@staabm
Copy link
Contributor Author

staabm commented Jan 5, 2024

(just pushed a intermediate state to allow me switching to a different computer over the weekend)

src/PhpDoc/ResolvedPhpDocBlock.php Outdated Show resolved Hide resolved
src/Reflection/ClassReflection.php Outdated Show resolved Hide resolved
src/Reflection/ClassReflection.php Outdated Show resolved Hide resolved
src/Reflection/ClassReflection.php Outdated Show resolved Hide resolved
src/Reflection/ClassReflection.php Outdated Show resolved Hide resolved
$this->inProcess[$typeDescription][$methodName] = true;

if (!$type->hasMethod($methodName)->yes()) {
unset($this->inProcess[$typeDescription][$methodName]);
Copy link
Member

Choose a reason for hiding this comment

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

Is this recursion guard tested?

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 tried thinking of a way this could get recursive, but wasn't able to come up with a example.

do you think its impossible and the recursion-guard should be removed?

Copy link
Member

Choose a reason for hiding this comment

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

I'd expect something like this to end up in infinite recursion https://phpstan.org/r/aa768c71-4596-4edf-a900-60e4db58582b

Copy link
Contributor Author

@staabm staabm Jan 6, 2024

Choose a reason for hiding this comment

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

thx for the inspiration. I tried similar things before and I can't force them to endless recurse.

I think this might not happen, because the new extension early exits for non-interfaces and the phpstan-require-extends requires a class as argument.

since I was still not able to force a recursion, I have removed the recursion guards for now

private function findMethod(ClassReflection $classReflection, string $methodName): ?MethodReflection
{
if (!$classReflection->isInterface()) {
return null;
Copy link
Member

Choose a reason for hiding this comment

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

This is a point for the rules - @...-require-extends can only be above an interface.

Copy link
Contributor Author

@staabm staabm Jan 6, 2024

Choose a reason for hiding this comment

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

so do you mean we should still try to infer the property across the type hierarchy, even though the require-extends is placed on a invalid location?

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, I don't get it. This comment was just a reminder for you to check whether require-extends is always above an interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

rules will be topic for a followup PR. thanks for noticing.

@staabm staabm marked this pull request as ready for review January 6, 2024 14:18
@phpstan-bot
Copy link
Collaborator

This pull request has been marked as ready for review.

@ondrejmirtes
Copy link
Member

I merged part of this pull request: 53a61dc

You should be able to rebase this PR cleanly (maybe after you squash all commits).

The reason why I did this is that you can start working on the rules PR in parallel to this PR.

I want to keep 1.10.x releasable and I don't want to merge this PR and then wait several releases for the rules PR to be finished, because I see them as an atomic feature.

You should be able to work on rules in a separate branch/PR without any conflicts.

@ondrejmirtes ondrejmirtes merged commit 1740286 into phpstan:1.10.x Jan 10, 2024
425 of 426 checks passed
@ondrejmirtes
Copy link
Member

Thank you!

@staabm staabm deleted the require-extends branch January 10, 2024 13:24
@ondrejmirtes
Copy link
Member

FYI this was a missing part: 9ac6fd1

@ondrejmirtes
Copy link
Member

About generics - I'd like to support things like:

/**
 * @template T
 * @phpstan-require-extends Model<T>
 */
  1. Which would inform about what T is in case the Model typehints it in the accessed property/called method. I think it might already work with the existing code.

  2. Also when doing class Foo implements Bar extends Model, it should enforce that the same type in @implements Bar<X> is also used in @extends Model<X>.

  3. Also if Model is generic but the user does not declare what T is in @phpstan-require-extends, PHPStan should complain.

  4. It should for example also complain when Model has two @template but the number of type variables isn't 2.

For 3) and 4) there are already existing checks which we should just call.

Do you feel up to the task? If you start working on this and hit a roadblock don't worry, open a draft PR and we'll figure out why it doesn't work. Thanks!

@staabm
Copy link
Contributor Author

staabm commented Jan 10, 2024

FYI this was a missing part: 9ac6fd1

great catch, thank you.

@staabm
Copy link
Contributor Author

staabm commented Jan 11, 2024

@ondrejmirtes should we add the require-extends and require-implements types to $dependenciesReflections similar how its done for MixinTypes?

foreach ($classReflection->getResolvedMixinTypes() as $mixinType) {
foreach ($mixinType->getReferencedClasses() as $referencedClass) {
if (!$this->reflectionProvider->hasClass($referencedClass)) {
continue;
}
$dependenciesReflections[] = $this->reflectionProvider->getClass($referencedClass);
}
}

@ondrejmirtes
Copy link
Member

Yes, definitely! Ideally try to simulate in e2e-tests.yml a situation where the result cache gets stale. There arr several examples already.

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