44namespace Rector \DeadCode \PhpDoc ;
55
66use PhpParser \Node ;
7+ use PhpParser \Node \Expr ;
8+ use PhpParser \Node \Expr \Assign ;
79use PhpParser \Node \Stmt \ClassConst ;
10+ use PhpParser \Node \Stmt \Expression ;
811use PhpParser \Node \Stmt \Property ;
912use PHPStan \PhpDocParser \Ast \PhpDoc \VarTagValueNode ;
1013use PHPStan \PhpDocParser \Ast \Type \GenericTypeNode ;
1417use PHPStan \Type \UnionType ;
1518use Rector \DeadCode \PhpDoc \Guard \TemplateTypeRemovalGuard ;
1619use Rector \NodeTypeResolver \TypeComparator \TypeComparator ;
20+ use Rector \PHPStan \ScopeFetcher ;
1721use Rector \StaticTypeMapper \StaticTypeMapper ;
1822final class DeadVarTagValueNodeAnalyzer
1923{
@@ -36,22 +40,43 @@ public function __construct(TypeComparator $typeComparator, StaticTypeMapper $st
3640 $ this ->templateTypeRemovalGuard = $ templateTypeRemovalGuard ;
3741 }
3842 /**
39- * @param \PhpParser\Node\Stmt\Property|\PhpParser\Node\Stmt\ClassConst $property
43+ * @param \PhpParser\Node\Stmt\Property|\PhpParser\Node\Stmt\ClassConst|\PhpParser\Node\Stmt\Expression $node
4044 */
41- public function isDead (VarTagValueNode $ varTagValueNode , $ property ): bool
45+ public function isDead (VarTagValueNode $ varTagValueNode , $ node ): bool
4246 {
43- if (!$ property ->type instanceof Node) {
47+ if (!$ node instanceof Expression && ! $ node ->type instanceof Node) {
4448 return \false;
4549 }
4650 if ($ varTagValueNode ->description !== '' ) {
4751 return \false;
4852 }
53+ $ targetNode = null ;
54+ if ($ node instanceof Expression && $ node ->expr instanceof Assign) {
55+ $ targetNode = $ node ->expr ->expr ;
56+ } elseif ($ node instanceof Property || $ node instanceof ClassConst) {
57+ $ targetNode = $ node ->type ;
58+ }
59+ // allow Identifier, ComplexType, and Name on Property and ClassConst
60+ if (!$ targetNode instanceof Node) {
61+ return \false;
62+ }
4963 if ($ varTagValueNode ->type instanceof GenericTypeNode) {
5064 return \false;
5165 }
5266 // is strict type superior to doc type? keep strict type only
53- $ propertyType = $ this ->staticTypeMapper ->mapPhpParserNodePHPStanType ($ property ->type );
54- $ docType = $ this ->staticTypeMapper ->mapPHPStanPhpDocTypeNodeToPHPStanType ($ varTagValueNode ->type , $ property );
67+ $ propertyType = $ this ->staticTypeMapper ->mapPhpParserNodePHPStanType ($ targetNode );
68+ $ docType = $ this ->staticTypeMapper ->mapPHPStanPhpDocTypeNodeToPHPStanType ($ varTagValueNode ->type , $ node );
69+ if ($ node instanceof Expression) {
70+ $ scope = ScopeFetcher::fetch ($ node );
71+ // only allow Expr on assign expr
72+ if (!$ targetNode instanceof Expr) {
73+ return \false;
74+ }
75+ $ nativeType = $ scope ->getNativeType ($ targetNode );
76+ if (!$ docType ->equals ($ nativeType )) {
77+ return \false;
78+ }
79+ }
5580 if (!$ this ->templateTypeRemovalGuard ->isLegal ($ docType )) {
5681 return \false;
5782 }
@@ -62,7 +87,7 @@ public function isDead(VarTagValueNode $varTagValueNode, $property): bool
6287 // more specific type is already in the property
6388 return $ docType ->isSuperTypeOf ($ propertyType )->yes ();
6489 }
65- if ($ this ->typeComparator ->arePhpParserAndPhpStanPhpDocTypesEqual ($ property -> type , $ varTagValueNode ->type , $ property )) {
90+ if ($ this ->typeComparator ->arePhpParserAndPhpStanPhpDocTypesEqual ($ targetNode , $ varTagValueNode ->type , $ node )) {
6691 return \true;
6792 }
6893 return $ docType instanceof UnionType && $ this ->typeComparator ->areTypesEqual (TypeCombinator::removeNull ($ docType ), $ propertyType );
0 commit comments