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
Recognize constants as constant types #1163
Conversation
abstract class Test | ||
{ | ||
/** | ||
* @return (PHP_MAJOR_VERSION is 8 ? true : 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.
Obviously this breaks on PHP < 8 💥
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 should run this test only on php8, or/and add an php7 alternative test-case with a different expectation.
src/PhpDoc/TypeNodeResolver.php
Outdated
return new ObjectType($stringName); | ||
} | ||
|
||
private function mightBeConstant(string $name): bool | ||
{ | ||
return preg_match('(^[A-Z_][A-Z0-9_]*$)', $name) > 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.
since A
and B
or similar short-names might be considered a constant in this case, I think tryResolveConstant
should at first try whether there is a class defined with this name. Only if not, try whether its actually a constant.
what do you think?
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.
Agreed, constant can be tried only when it's UPPER_CASE and class of that name does not exist.
I love it. this kills 2 birds with one stone. it implements phpstan/phpstan#6160 and fixes one of the psalm-special-cases within the conditional-type language phpstan/phpstan#3853 (comment) |
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 hate to bring the bad news but:
- I like to resolve constants here to solve Support global constants in union types phpstan#6160
- But I don't think it makes sense to support this in conditional types. The code here reports
int<5, max>
: https://phpstan.org/r/853028b5-3d52-4f51-ae64-1655458ef879 So typehintingPHP_MAJOR_VERSION
in a PHPDoc also has to resolve toint<5, max>
thanks todynamicConstantNames
(https://phpstan.org/config-reference#constants).
Which makes it useless for conditional types. But it's fine for me - having PHP_MAJOR_VERSION
conditional in a stub is useful only for built-in PHP functions, and these can be solved by writing code in an extension. I don't want to have a small cosmetic advantage at a cost of doing some horrible hacks to make this work in stubs :)
Also wildcard support would be a great addition, some people expect that to work (phpstan/phpstan#6972). Needs modification in phpdoc-parser too. |
See phpstan#1163 for discussion. `TypeNodeResolver` needs to be able to resolve constants the same way as `MutatingScope` does
See #1163 for discussion. `TypeNodeResolver` needs to be able to resolve constants the same way as `MutatingScope` does
5b11ca8
to
db4d492
Compare
If you don't mind I'd like to tackle that separately. This PR has become quite large enough as-is 🥲 |
I agree, we can leave the wildcards for a later 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.
Perfect! Can you please check that NameScope doesn't contain use function
uses as they are useless for it?
Checking the code they probably aren't there thanks to |
src/PhpDoc/TypeNodeResolver.php
Outdated
@@ -361,9 +366,36 @@ private function resolveIdentifierTypeNode(IdentifierTypeNode $typeNode, NameSco | |||
return new ErrorType(); | |||
} | |||
|
|||
if (!$this->mightBeConstant($typeNode->name) || $this->getReflectionProvider()->hasClass($stringName)) { |
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 this should just be if ($this->getReflectionProvider()->hasClass($stringName)) {
.
Because if something looks like a constant FOO_BAR
but there's a class FOO_BAR
, I want ObjectType
of the class, not the constant.
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 should still always result in the class taking precedence, but does a slightly cheaper heuristic check.
It's covered in the test as well: https://github.com/phpstan/phpstan-src/pull/1163/files#diff-f60365c259ff216f4d228e0c46729817e679144be2a7bf2e7395ace7fd344284R47
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 think it hits that condition:
private function mightBeConstant(string $name): bool
{
return preg_match('((?:^|\\\\)[A-Z_][A-Z0-9_]*$)', $name) > 0;
}
This just matches UPPER_CASE names, right? But the constant in question has CamelCaps namespace.
Because when I read that code in case of FOO_BAR
the first return new ObjectType($stringName);
is skipped because mightBeConstant() === true
. And then tryResolveConstant
is successful because the constant exists. So constant takes precedence over 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.
The namespace isn't considered, only the last part of $name
(so BAR
in that case). If the constant took precedence the type in the test would be 10
, instead of the class name (ConstantPhpdocType\BAR
).
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.
Now it makes sense to me :) I'm ready to merge this, thanks.
It shouldn't because only |
Closes phpstan/phpstan#6160. This was just a wild idea. Probably not the best way forward, but at least it shows the potential
d07a223
to
97f7026
Compare
Thank you! Please send an update to the documentation: https://phpstan.org/writing-php-code/phpdoc-types (add a new section "Global constants"). |
Documentation for phpstan/phpstan-src#1163
Did you consider introducing a new |
@vudaltsov You're commenting on a two year old PR. You should open a new feature request and describe your use case. |
Closes phpstan/phpstan#6160. This was just a wild idea. Probably not the best way forward, but at least it shows the potential