-
Notifications
You must be signed in to change notification settings - Fork 574
Skip class name case check for type hints using explicit use ... as aliases
#5671
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
Open
phpstan-bot
wants to merge
9
commits into
phpstan:2.1.x
Choose a base branch
from
phpstan-bot:create-pull-request/patch-rsul8dt
base: 2.1.x
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
90c68c1
Skip class name case check for type hints using explicit `use ... as`…
phpstan-bot b73e21a
Remove redundant strtolower alias detection heuristic
phpstan-bot 66fd083
Move explicit use alias check to ClassCaseSensitivityCheck
phpstan-bot 797f756
Add test cases for explicit use alias in extends, implements, instanc…
phpstan-bot 1fa40f1
Add test coverage for explicit use aliases in closures and complex types
phpstan-bot 1786263
Add explicit test for class_alias() case sensitivity skip
phpstan-bot c4d76b9
Skip ExistingClassesInTypehintsRuleTest::testBug14617 for PHP < 8.0
phpstan-bot 1b08883
Remove class_alias case sensitivity test to not lock in current behavior
phpstan-bot bbeaf3e
Add back class_alias case sensitivity test with correct-case usage only
phpstan-bot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| <?php declare(strict_types = 1); | ||
|
|
||
| namespace PHPStan\Parser; | ||
|
|
||
| use Override; | ||
| use PhpParser\Node; | ||
| use PhpParser\Node\Name; | ||
| use PhpParser\Node\Stmt\GroupUse; | ||
| use PhpParser\Node\Stmt\Use_; | ||
| use PhpParser\NodeVisitorAbstract; | ||
| use PHPStan\DependencyInjection\AutowiredService; | ||
| use function count; | ||
| use function strtolower; | ||
|
|
||
| #[AutowiredService] | ||
| final class UseAliasVisitor extends NodeVisitorAbstract | ||
| { | ||
|
|
||
| public const ATTRIBUTE_NAME = 'isExplicitUseAlias'; | ||
|
|
||
| /** @var array<string, string> alias name (original case) keyed by lowercase alias name */ | ||
| private array $explicitAliases = []; | ||
|
|
||
| #[Override] | ||
| public function enterNode(Node $node): ?Node | ||
| { | ||
| if ($node instanceof Node\Stmt\Namespace_) { | ||
| $this->explicitAliases = []; | ||
| } | ||
|
|
||
| if ($node instanceof Use_ && $node->type === Use_::TYPE_NORMAL) { | ||
| foreach ($node->uses as $use) { | ||
| if ($use->alias === null) { | ||
| continue; | ||
| } | ||
|
|
||
| $this->explicitAliases[strtolower($use->alias->name)] = $use->alias->name; | ||
| } | ||
| } | ||
|
|
||
| if ($node instanceof GroupUse) { | ||
| foreach ($node->uses as $use) { | ||
| if ($use->type !== Use_::TYPE_NORMAL && $node->type !== Use_::TYPE_NORMAL) { | ||
| continue; | ||
| } | ||
| if ($use->alias === null) { | ||
| continue; | ||
| } | ||
|
|
||
| $this->explicitAliases[strtolower($use->alias->name)] = $use->alias->name; | ||
| } | ||
| } | ||
|
|
||
| if ($node instanceof Name) { | ||
| $originalName = $node->getAttribute('originalName'); | ||
| if ($originalName instanceof Name) { | ||
| $originalParts = $originalName->getParts(); | ||
| if (count($originalParts) === 1) { | ||
| $lowerOriginal = strtolower($originalParts[0]); | ||
| if ( | ||
| isset($this->explicitAliases[$lowerOriginal]) | ||
| && $this->explicitAliases[$lowerOriginal] === $originalParts[0] | ||
| ) { | ||
| $node->setAttribute(self::ATTRIBUTE_NAME, true); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <?php declare(strict_types = 1); | ||
|
|
||
| namespace Bug14617Classes; | ||
|
|
||
| class MyClass {} | ||
|
|
||
| interface MyInterface {} | ||
|
|
||
| namespace Bug14617Classes\Consumer; | ||
|
|
||
| use Bug14617Classes\MyClass as myclass; | ||
| use Bug14617Classes\MyInterface as myinterface; | ||
|
|
||
| class Foo extends myclass implements myinterface { | ||
| public myclass $prop; | ||
| } | ||
|
|
||
| function test(mixed $x): void { | ||
| if ($x instanceof myclass) { | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| <?php declare(strict_types = 1); | ||
|
|
||
| namespace Bug14617Closure; | ||
|
|
||
| class MyClass {} | ||
|
|
||
| namespace Bug14617Closure\Consumer; | ||
|
|
||
| use Bug14617Closure\MyClass as myclass; | ||
|
|
||
| $callback = function (myclass $a): myclass { | ||
| return $a; | ||
| }; |
7 changes: 7 additions & 0 deletions
7
tests/PHPStan/Rules/Functions/data/class-alias-case-sensitivity.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| <?php declare(strict_types = 1); | ||
|
|
||
| namespace ClassAliasCaseSensitivity; | ||
|
|
||
| $callback = function (\ReturnTypes\FooAlias $a): \ReturnTypes\FooAlias { | ||
| return $a; | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| <?php declare(strict_types = 1); | ||
|
|
||
| namespace Bug14617; | ||
|
|
||
| class MyClass {} | ||
|
|
||
| namespace Bug14617\Consumer; | ||
|
|
||
| use Bug14617\MyClass as myclass; | ||
|
|
||
| function test(): myclass { | ||
| return new myclass(); | ||
| } | ||
|
|
||
| class Foo { | ||
| public function bar(myclass $a): myclass { | ||
| return $a; | ||
| } | ||
|
|
||
| public function nullable(?myclass $a): ?myclass { | ||
| return $a; | ||
| } | ||
|
|
||
| public function union(myclass|string $a): myclass|int { | ||
| return $a; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Should/Could this if be removed 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.
Here's a summary of what was done to address the review:
The
strtolowercheck cannot be removed. I verified this by temporarily removing it and running the full test suite —ExistingClassesInClosureTypehintsRuleTest::testExistingClassInTypehintfails because line 35 ofclosure-typehints.phpuses\ReturnTypes\FooAliaS, which is aclass_alias()forReturnTypes\Foo. Without thestrtolowercheck, PHPStan incorrectly reports "Class ReturnTypes\Foo referenced with incorrect case: ReturnTypes\FooAliaS" — a false positive becauseFooAliaSis a completely different name (aclass_alias), not a case mismatch.The
UseAliasVisitoronly tracksuse ... asaliases in the parser. Thestrtolowercheck handlesclass_alias()scenarios where the reflection-resolved class name is entirely different from the written name.Changes made:
strtolowercheck to explain it handlesclass_alias()specificallybug-14617-closure.php+ test inExistingClassesInClosureTypehintsRuleTest)bug-14617.phpAll 12,076 tests pass and
make phpstanreports no errors.