From d7fe7893597489aad37525063ad75dc0a83c497a Mon Sep 17 00:00:00 2001 From: phpstan-bot <79867460+phpstan-bot@users.noreply.github.com> Date: Fri, 27 Feb 2026 13:40:41 +0000 Subject: [PATCH 1/2] Fix false positive class.nameCase when use alias matches another class - In getOriginalClassNamePairsFromTypeNode(), detect when original name parts are a use alias (not just a case variant) by comparing them case-insensitively with the resolved name's suffix parts - When parts differ (not just in case), skip creating a pair since the alias name is unrelated to the actual class name - New regression test in tests/PHPStan/Rules/Methods/data/bug-14205.php Closes https://github.com/phpstan/phpstan/issues/14205 --- src/Rules/FunctionDefinitionCheck.php | 7 +++++++ .../ExistingClassesInTypehintsRuleTest.php | 5 +++++ tests/PHPStan/Rules/Methods/data/bug-14205.php | 15 +++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 tests/PHPStan/Rules/Methods/data/bug-14205.php diff --git a/src/Rules/FunctionDefinitionCheck.php b/src/Rules/FunctionDefinitionCheck.php index c888ed886f..ca78471312 100644 --- a/src/Rules/FunctionDefinitionCheck.php +++ b/src/Rules/FunctionDefinitionCheck.php @@ -860,6 +860,13 @@ private function getOriginalClassNamePairsFromTypeNode(Identifier|Name|ComplexTy $resolvedPartsCount = count($resolvedParts); if ($originalPartsCount <= $resolvedPartsCount) { + $suffixParts = array_slice($resolvedParts, $resolvedPartsCount - $originalPartsCount); + for ($i = 0; $i < $originalPartsCount; $i++) { + if (strtolower($originalParts[$i]) !== strtolower($suffixParts[$i])) { + return []; // use alias, not just a case difference + } + } + $prefixParts = array_slice($resolvedParts, 0, $resolvedPartsCount - $originalPartsCount); $originalCaseClassName = implode('\\', array_merge($prefixParts, $originalParts)); } else { diff --git a/tests/PHPStan/Rules/Methods/ExistingClassesInTypehintsRuleTest.php b/tests/PHPStan/Rules/Methods/ExistingClassesInTypehintsRuleTest.php index 929b3bdfd4..dd9f891c4a 100644 --- a/tests/PHPStan/Rules/Methods/ExistingClassesInTypehintsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/ExistingClassesInTypehintsRuleTest.php @@ -657,4 +657,9 @@ public function testBug11470(): void ]); } + public function testBug14205(): void + { + $this->analyse([__DIR__ . '/data/bug-14205.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Methods/data/bug-14205.php b/tests/PHPStan/Rules/Methods/data/bug-14205.php new file mode 100644 index 0000000000..35ab5741a0 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/bug-14205.php @@ -0,0 +1,15 @@ + Date: Wed, 4 Mar 2026 20:20:23 +0100 Subject: [PATCH 2/2] Rework --- src/Rules/FunctionDefinitionCheck.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Rules/FunctionDefinitionCheck.php b/src/Rules/FunctionDefinitionCheck.php index ca78471312..6ef8f74b15 100644 --- a/src/Rules/FunctionDefinitionCheck.php +++ b/src/Rules/FunctionDefinitionCheck.php @@ -860,19 +860,17 @@ private function getOriginalClassNamePairsFromTypeNode(Identifier|Name|ComplexTy $resolvedPartsCount = count($resolvedParts); if ($originalPartsCount <= $resolvedPartsCount) { - $suffixParts = array_slice($resolvedParts, $resolvedPartsCount - $originalPartsCount); - for ($i = 0; $i < $originalPartsCount; $i++) { - if (strtolower($originalParts[$i]) !== strtolower($suffixParts[$i])) { - return []; // use alias, not just a case difference - } - } - $prefixParts = array_slice($resolvedParts, 0, $resolvedPartsCount - $originalPartsCount); $originalCaseClassName = implode('\\', array_merge($prefixParts, $originalParts)); } else { $originalCaseClassName = $originalName->toString(); } + if (strtolower($originalCaseClassName) !== strtolower($resolvedName)) { + // use alias, not just a case difference + return []; + } + if ($originalCaseClassName === $resolvedName) { return []; }