Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork9.7k
[PHPDoc] Fix various PHPDoc syntax errors#62317
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:7.4
Are you sure you want to change the base?
Conversation
hi@nicolas-grekas this PR target 7.4 instead of this one#62316 |
| * @param U|null $backingType | ||
| * | ||
| * @return ($className is class-string<\BackedEnum> ? ($backingType is U ? BackedEnumType<T, U> : BackedEnumType<T, BuiltinType<TypeIdentifier::INT>|BuiltinType<TypeIdentifier::STRING>>) : EnumType<T>)) | ||
| * @return ($className is class-string<\BackedEnum> ? ($backingType is U ? BackedEnumType<class-string<\BackedEnum>, U> : BackedEnumType<class-string<\BackedEnum>, BuiltinType<TypeIdentifier::INT>|BuiltinType<TypeIdentifier::STRING>>) : EnumType<T>) |
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.
why this change that stops usingT ?
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.
Hi@stof, this change fixes several static analysis errors reported by Psalm.
The root cause is anInvalidTemplateParam error: the@template T is defined asUnitEnum|BackedEnum, but thetrue branch of the conditional return was passing this genericT toBackedEnumType, which strictly expectsclass-string<BackedEnum>.
This PR replaces the genericT with the more specificclass-string<\BackedEnum> in that branch, which satisfies the template constraint and resolves the subsequentInvalidReturnType errors.
Here is the full output from Psalm for reference:
https://github.com/symfony/symfony/actions/runs/19110924189
Psalm Error Log
InvalidReturnType: src/Symfony/Component/TypeInfo/TypeFactoryTrait.php:239:16: InvalidReturnType: The declared return type 'Symfony\Component\TypeInfo\Type\BackedEnumType, Symfony\Component\TypeInfo\Type\BuiltinType|Symfony\Component\TypeInfo\Type\BuiltinType>|Symfony\Component\TypeInfo\Type\BackedEnumType, U:fn-symfony\component\typeinfo\typefactorytrait::enum as Symfony\Component\TypeInfo\Type\BuiltinType|Symfony\Component\TypeInfo\Type\BuiltinType>|(Symfony\Component\TypeInfo\Type\EnumType>)' for Symfony\Component\TypeInfo\TypeFactoryTrait::enum is incorrect, got 'Symfony\Component\TypeInfo\Type\BackedEnumType, TGeneratedFromParam1:fn-symfony\component\typeinfo\typefactorytrait::enum as U:fn-symfony\component\typeinfo\typefactorytrait::enum as Symfony\Component\TypeInfo\Type\BuiltinType|Symfony\Component\TypeInfo\Type\BuiltinType|null>|(Symfony\Component\TypeInfo\Type\EnumType>)' (see https://psalm.dev/011)InvalidTemplateParam: src/Symfony/Component/TypeInfo/TypeFactoryTrait.php:239:16: InvalidTemplateParam: Extended template param T of Symfony\Component\TypeInfo\Type\BackedEnumType<T:fn-symfony\component\typeinfo\typefactorytrait::enum as class-string, U:fn-symfony\component\typeinfo\typefactorytrait::enum as Symfony\Component\TypeInfo\Type\BuiltinType<enum(Symfony\Component\TypeInfo\TypeIdentifier::INT)>|Symfony\Component\TypeInfo\Type\BuiltinType<enum(Symfony\Component\TypeInfo\TypeIdentifier::STRING)>> expects type class-string, type T:fn-symfony\component\typeinfo\typefactorytrait::enum as class-string given (seehttps://psalm.dev/183)
InvalidTemplateParam: src/Symfony/Component/TypeInfo/TypeFactoryTrait.php:239:16: InvalidTemplateParam: Extended template param T of Symfony\Component\TypeInfo\Type\BackedEnumType<T:fn-symfony\component\typeinfo\typefactorytrait::enum as class-string, Symfony\Component\TypeInfo\Type\BuiltinType<enum(Symfony\Component\TypeInfo\TypeIdentifier::INT)>|Symfony\Component\TypeInfo\Type\BuiltinType<enum(Symfony\Component\TypeInfo\TypeIdentifier::STRING)>> expects type class-string, type T:fn-symfony\component\typeinfo\typefactorytrait::enum as class-string given (seehttps://psalm.dev/183)
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 this leads to a less precise type thanT.
Maybe the conditional type should use a check onT instead of$className for the analysis to work.
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.
Hi@stof, that's a much more elegant solution. You're right, checkingT directly instead of$className should allow the static analysis to work while preserving the type precision.
I've pushed the change. Thanks 🙏🏼
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.
Hi@stof, I tried checkingT directly in the conditional, but unfortunately, Psalm still reports the exact sameInvalidTemplateParam errors.
It seems the analyzer is unable to "narrow" the type ofT (fromUnitEnum) based on that conditional.
I've reverted to my original fix (using theclass-string<\BackedEnum>) and pushed that.
Let me know if that's okay, or if you'd prefer a different approach. Thanks!
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.
@yoeunes are you looking at what Psalm is able to do when analyzing the code of that method, or when analyzing codecalling that method ?
The second case is more important for the value it brings to the ecosystem (and it might make sense to also check how phpstan analyzes such caller code, as projects using Symfony might use any tool).
I know that psalm is not always smart enough to analyze code of a method that combines both conditional types and generics.
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.
Hi@stof, that's a great point, and you're right, the "caller" analysis is the most important one.
I've tested both scenarios, and this is where it gets tricky:
Internal Analysis (CI): Your suggestion to check
(T is ...)is the most elegant, but as the CI run showed,Psalm still fails. It reports the sameInvalidTemplateParamerror, as it seems unable to "narrow" the genericT(fromUnitEnum) inside the conditional.Caller Analysis (User-land): To fix that Psalm error, I first tried the "
less precise" workaround (BackedEnumType<class-string<\BackedEnum>, ...>). This fixes Psalm, but it then causesPHPStan to fail forcallers with anUnable to resolve the template type Uerror. This happens when a user calls::enum()with only one argument (ornull), as PHPStan can't inferU.
I've finally pushed a solution that makesboth analyzers happy. It's the one in the PR now, which combines the two fixes:
- It uses
BackedEnumType<class-string<\BackedEnum>, ...to satisfyPsalm's internal analysis. - It adds aninner conditional check for
($backingType is null ? ...)to satisfyPHPStan's caller analysis (by giving it a concrete type path whenUcannot be inferred).
This final version now passes all tests, both inside Symfony's CI and when analyzing user-land code.
Here is the simple user-land script I used to reproduce and fix the PHPStan (caller) error:
<?phprequire__DIR__.'/vendor/autoload.php';useSymfony\Component\TypeInfo\TypeFactoryTrait;enum MyEnum:string {case A ='a';}class TestEnumType{use TypeFactoryTrait;}// This call (with 1 arg) triggers "Unable to resolve U"// unless the PHPDoc has the inner ($backingType is null) check.TestEnumType::enum(MyEnum::class);
Let me know if this solution looks good. Thanks!
51e77aa to4af7f20Compare4af7f20 to827ad60Compare| * $className is class-string<\BackedEnum> | ||
| * ? ($backingType is null | ||
| * ? BackedEnumType<class-string<\BackedEnum>, BuiltinType<TypeIdentifier::INT>|BuiltinType<TypeIdentifier::STRING>> | ||
| * : BackedEnumType<class-string<\BackedEnum>, U>) |
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 still looses the preciseT type for theBackedEnumType AFAICT.
This PR fixes several minor PHPDoc syntax errors