Skip to content

Skip class name case check for type hints using explicit use ... as aliases#5671

Open
phpstan-bot wants to merge 2 commits into
phpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-rsul8dt
Open

Skip class name case check for type hints using explicit use ... as aliases#5671
phpstan-bot wants to merge 2 commits into
phpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-rsul8dt

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

When a class is imported with an explicit alias via use Foo\MyClass as myclass, PHPStan incorrectly reported "Class Foo\MyClass referenced with incorrect case: Foo\myclass" on type hints using the alias. The alias name is intentionally chosen by the user and should not trigger a case sensitivity error.

Changes

  • Added src/Parser/UseAliasVisitor.php — a new parser visitor that:
    • Tracks explicit use ... as aliases (both regular and group use statements)
    • Marks resolved Name nodes with an isExplicitUseAlias attribute when the original name matches an explicit alias case-sensitively
    • Resets alias tracking on namespace boundaries
  • Modified src/Rules/FunctionDefinitionCheck.php to check the isExplicitUseAlias attribute in getOriginalClassNamePairsFromTypeNode() and skip the case sensitivity check for explicit aliases

Root cause

FunctionDefinitionCheck::getOriginalClassNamePairsFromTypeNode() reconstructs the "original case" class name by combining the namespace prefix from the resolved name with the original name parts. For use Foo\MyClass as myclass, the original name myclass was combined with prefix Foo to produce Foo\myclass. Since strtolower('Foo\myclass') === strtolower('Foo\MyClass'), the existing alias detection (which only catches aliases with entirely different names) did not fire, and the name was incorrectly flagged as a case mismatch.

The fix distinguishes explicit use ... as aliases from wrong-case references by tracking which aliases were explicitly defined via the as keyword in use statements.

Analogous cases probed

The following contexts were tested and confirmed to NOT be affected (they pass the fully-resolved class name to ClassCaseSensitivityCheck, bypassing the problematic reconstruction logic):

  • new expressions (InstantiationRule)
  • instanceof expressions (ExistingClassInInstanceOfRule)
  • catch clauses (CaughtExceptionExistenceRule)
  • Class constant access (ClassConstantRule)
  • Static property access (AccessStaticPropertiesCheck)
  • Static method calls (StaticMethodCallCheck)
  • Class extends/implements/trait use declarations
  • Property type declarations (ExistingClassesInPropertiesRule)
  • PHPDoc types (@param, @return)

Test

Added tests/PHPStan/Rules/Methods/data/bug-14617.php with a regression test covering:

  • Function return type with explicit alias
  • Class method parameter type with explicit alias
  • Class method return type with explicit alias
  • new expression with explicit alias (via InstantiationRule, verified no error)

Fixes phpstan/phpstan#14617

… aliases

- Add `UseAliasVisitor` parser visitor that tracks explicit `use ... as` aliases
  and sets an `isExplicitUseAlias` attribute on resolved Name nodes whose
  original name matches an explicit alias exactly (case-sensitively)
- In `FunctionDefinitionCheck::getOriginalClassNamePairsFromTypeNode()`, skip
  the case sensitivity check when the Name node is marked as an explicit alias
- The false positive occurred because the method reconstructed the original-case
  class name by combining the resolved namespace prefix with the original alias
  name, producing e.g. `Foo\myclass` when the alias was `myclass` for `Foo\MyClass`,
  which was then incorrectly flagged as a case mismatch
- Verified that analogous contexts (new, instanceof, catch, extends, implements,
  trait use, static access, PHPDoc types) are not affected because they pass the
  fully-resolved class name directly to `ClassCaseSensitivityCheck`
Copy link
Copy Markdown
Contributor

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

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

The condition

if (strtolower($originalCaseClassName) !== strtolower($resolvedName)) {
				// use alias, not just a case difference
				return [];
			}

should not be needed anymore

The UseAliasVisitor now explicitly tracks `use ... as` aliases,
making the old heuristic that compared lowercase class names
unnecessary. The old check was a broad approximation; the new
attribute-based approach is precise.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@phpstan-bot
Copy link
Copy Markdown
Collaborator Author

Done. Removed the redundant strtolower alias detection heuristic in FunctionDefinitionCheck.php. The UseAliasVisitor now handles all explicit use ... as alias detection precisely via the isExplicitUseAlias attribute, making the old lowercase-comparison check unnecessary. All tests (12,075) and static analysis pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants