🌐 US-Proxy
class="logged-out env-production page-responsive" style="word-wrap: break-word;" >
Skip to content

Resolve class-string expressions to object types when detecting unused private methods, constants and static properties - #5953

Merged
staabm merged 4 commits into
phpstan:2.2.xfrom
phpstan-bot:create-pull-request/patch-l5gz1uo
Jun 29, 2026
Merged

Resolve class-string expressions to object types when detecting unused private methods, constants and static properties#5953
staabm merged 4 commits into
phpstan:2.2.xfrom
phpstan-bot:create-pull-request/patch-l5gz1uo

Conversation

@phpstan-bot

Copy link
Copy Markdown
Collaborator

Summary

PHPStan reported a private method as unused when it was called via a class-string expression such as self::class::sampleClass(). The dead-code rules resolved the call's class expression to a string type and then failed to find the member on it, so the member was treated as never used. This fixes the false positive and the analogous static-property and class-constant cases.

Changes

  • src/Rules/DeadCode/UnusedPrivateMethodRule.php — when a static call's class is an expression (not a Node\Name), resolve the type with getObjectTypeOrClassStringObjectType() before looking up the method.
  • src/Rules/DeadCode/UnusedPrivatePropertyRule.php — same conversion for static property fetches whose class is an expression.
  • src/Rules/DeadCode/UnusedPrivateConstantRule.php — same conversion for class-constant fetches whose class is an expression.

Root cause

A static member access like self::class::sampleClass() parses with the class part being an expression (self::class), not a Name. The rules took the else branch and used $scope->getType($class), which yields a string/class-string type. getMethodReflection() and getStaticPropertyReflection() return null for a string type, and the class-string is not a subtype of the analysed class' ObjectType, so the "is it used on this class?" guard failed and the member was kept in the unused set.

The pattern affected all three dead-code member rules (method ↔ static property ↔ class constant). getObjectTypeOrClassStringObjectType() is the unifying fix: it returns object types as-is and converts class-string types to the corresponding object type, so the existing member lookup just works.

Note: UnusedPrivateConstantRule happened to already handle a constant class-string (ConstantStringType delegates hasConstant()/getConstant() to its object type), but it was still broken when the class expression had a non-constant class-string type such as class-string<self> (GenericClassStringType does not delegate those queries). The same conversion fixes that case too, keeping the three rules consistent.

Test

  • tests/PHPStan/Rules/DeadCode/data/bug-14880.php + UnusedPrivateMethodRuleTest::testBug14880self::class::method(), a $class = self::class; $class::method() variant, and a class-string<self>-typed variant; expects no method.unused error.
  • tests/PHPStan/Rules/DeadCode/data/bug-14880-property.php + UnusedPrivatePropertyRuleTest::testBug14880 — the same three variants for a private static property read/written via the class-string expression.
  • tests/PHPStan/Rules/DeadCode/data/bug-14880-constant.php + UnusedPrivateConstantRuleTest::testBug14880 — the same three variants for a private class constant.

All three method/property tests fail without the fix; the constant test fails without the fix for the class-string<self> variant. make phpstan and the full DeadCode rule suite pass.

Fixes phpstan/phpstan#14880

…d private methods, constants and static properties

- In UnusedPrivateMethodRule, UnusedPrivatePropertyRule and UnusedPrivateConstantRule, call getObjectTypeOrClassStringObjectType() on the type of a static call/fetch whose class is an expression (e.g. self::class::method(), $class::CONST, $class::$prop) instead of using the raw string type.
- A class-string expression like self::class resolves to a string type, on which getMethodReflection()/getStaticPropertyReflection() return null, so the member was wrongly reported as unused. Converting to the corresponding object type lets the member lookup succeed and the usage be recognized.
- Fixes the reported unused-method false positive for self::class::method(), plus the analogous static-property "never read" and class-constant cases, and the same family for variables typed as class-string<self> (GenericClassStringType, which unlike ConstantStringType does not delegate hasConstant()/hasMethod() to its object type).
@staabm
staabm merged commit 9da3744 into phpstan:2.2.x Jun 29, 2026
671 of 672 checks passed
@staabm
staabm deleted the create-pull-request/patch-l5gz1uo branch June 29, 2026 21:50
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.

Unused method false positive

3 participants