Scan PHPDoc type nodes for class constant references in UnusedPrivateConstantRule#5677
Scan PHPDoc type nodes for class constant references in UnusedPrivateConstantRule#5677phpstan-bot wants to merge 3 commits into
UnusedPrivateConstantRule#5677Conversation
…eConstantRule` - Add PHPDoc scanning to UnusedPrivateConstantRule to detect constant references in value-of<>, key-of<>, int-mask-of<>, and const type notation (self::CONST, self::CONST*) - Walk the raw PhpDocNode AST recursively to find ConstFetchNode instances that reference the class's own constants - Scan class-level, method-level, and property-level PHPDocs - Handle wildcard patterns (self::FLAG_*) with regex matching - Update testBug9039 which now correctly recognizes self::* in @template-extends as constant usage
VincentLanglet
left a comment
There was a problem hiding this comment.
Rather than checking this in UnusedPrivateConstantRule it would make more sens to add the constantFetch in ClassStatementsGatherer
Rather than checking PHPDoc constant references directly in UnusedPrivateConstantRule, move the scanning logic to ClassConstantsNode where it is lazily computed via getPhpDocFetches(). This keeps the rule simple and makes PHPDoc constant reference data available to any rule that processes ClassConstantsNode. Introduces PhpDocClassConstantReference value object to represent PHPDoc-based constant references (className + constantName). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Done. I've addressed the review by moving the PHPDoc constant reference scanning logic out of Key design decisions:
|
VincentLanglet
left a comment
There was a problem hiding this comment.
The correct way to access PHPDocs is through FileTypeMapper. Also you shouldn't have to do any regexes, but correctly resolve the class name with NameScope.
Of course the issue is that the thing returned by FileTypeMapper is ResolvedPhpDocBlock. Which is about accessing the useful PHPDoc things like ParamTag and many others.
One way to solve this would be to add getClassConstFetches on ResolvedPhpDocBlock. But the class is a bit tricky to work with - because it can be created multiple ways (create, createEmpty, merge), we need to be careful every time a new property is added.
…g NameScope Use NameScope::resolveStringName() to properly resolve class names in PHPDoc constant references instead of manual string comparison. This addresses the review feedback to access PHPDocs through the proper abstraction layer. - Add getClassConstantReferences() on ResolvedPhpDocBlock that walks PHPDoc AST nodes for ConstFetchNode and resolves class names using the block's NameScope (handles self/static, use aliases, namespaces) - In merge(), eagerly compute references from the child block (which has a valid NameScope) so merged blocks still carry the data - Simplify ClassConstantsNode::getPhpDocFetches() to delegate to ResolvedPhpDocBlock::getClassConstantReferences() - Remove isSameClass() from UnusedPrivateConstantRule - compare resolved class names directly Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
The changes are committed and pushed. Here's a summary of what was done to address the review: Changes made:
|
Summary
UnusedPrivateConstantRuleonly checked for constant usages via ASTClassConstFetchnodes (explicit PHP code references likeself::CONST). PHPDoc type references such asvalue-of<self::CONST>,key-of<self::CONST>,int-mask-of<self::FLAG_*>, and const type notation (self::CONST*) did not count as "usage," causing false-positive "unused constant" errors.Changes
src/Rules/DeadCode/UnusedPrivateConstantRule.php:removePhpDocReferencedConstants()method that collects resolved PHPDoc blocks from the class-level, all methods, and all properties declared in the classfindConstFetchReferences()method that recursively walks PHPDoc AST nodes (usingget_object_vars) to findConstFetchNodeinstancesisSameClass()helper to matchself,static, or the class nameFLAG_*) using regex matching, consistent with howTypeNodeResolverhandles themtests/PHPStan/Analyser/AnalyserIntegrationTest.php:testBug9039now expects no errors sinceself::*in@template-extendscorrectly references all matching constants including private onestests/PHPStan/Rules/DeadCode/data/bug-6415.phpwith test cases covering:value-of<self::CONST>in return typekey-of<self::CONST>in return typekey-of<self::CONST>in property@varint-mask-of<self::FLAG_*>with wildcardself::MODE_*const type notation in@param@phpstan-typealias withvalue-of<self::CONST>@param value-of<self::CONST>in parameters@phpstan-assert value-of<self::CONST>in assertionsRoot cause
The
UnusedPrivateConstantRulerelied exclusively onClassStatementsGathererwhich only collectsExpr\ClassConstFetchAST nodes from PHP code. PHPDoc types are resolved during type resolution inTypeNodeResolver::resolveConstTypeNode(), which eagerly resolves the constant reference to its value type — the constant reference is consumed and never surfaces as an AST node. The fix adds a second pass that scans the raw PHPDoc AST (before type resolution) forConstFetchNodeinstances.Test
Regression test
testBug6415covers 8 classes exercising all PHPDoc-based constant reference patterns:value-of,key-of,int-mask-of, const type wildcard notation, property types, parameter types, assertion types, and@phpstan-typealiases. TheMixedUsageclass verifies that truly unused constants are still reported alongside PHPDoc-referenced ones.Fixes phpstan/phpstan#6415