1010use PHPStan \Rules \Rule ;
1111use PHPStan \Rules \RuleErrorBuilder ;
1212use PHPStan \ShouldNotHappenException ;
13+ use PHPStan \Type \FileTypeMapper ;
1314use PHPStan \Type \Generic \TemplateType ;
15+ use PHPStan \Type \ParserNodeTypeToPHPStanType ;
1416use PHPStan \Type \VerbosityLevel ;
1517use function array_merge ;
18+ use function count ;
1619use function sprintf ;
1720
1821/**
@@ -24,6 +27,7 @@ class IncompatiblePropertyPhpDocTypeRule implements Rule
2427 public function __construct (
2528 private GenericObjectTypeCheck $ genericObjectTypeCheck ,
2629 private UnresolvableTypeHelper $ unresolvableTypeHelper ,
30+ private FileTypeMapper $ fileTypeMapper ,
2731 )
2832 {
2933 }
@@ -40,15 +44,33 @@ public function processNode(Node $node, Scope $scope): array
4044 }
4145
4246 $ propertyName = $ node ->getName ();
43- $ propertyReflection = $ scope ->getClassReflection ()->getNativeProperty ($ propertyName );
47+ $ phpDoc = $ node ->getPhpDoc ();
48+ if ($ phpDoc === null ) {
49+ return [];
50+ }
51+
52+ $ resolvedPhpDoc = $ this ->fileTypeMapper ->getResolvedPhpDoc (
53+ $ scope ->getFile (),
54+ $ scope ->isInClass () ? $ scope ->getClassReflection ()->getName () : null ,
55+ $ scope ->isInTrait () ? $ scope ->getTraitReflection ()->getName () : null ,
56+ null ,
57+ $ phpDoc ,
58+ );
59+
60+ $ varTags = $ resolvedPhpDoc ->getVarTags ();
61+ $ phpDocType = null ;
62+ if (isset ($ varTags [0 ]) && count ($ varTags ) === 1 ) {
63+ $ phpDocType = $ varTags [0 ]->getType ();
64+ } elseif (isset ($ varTags [$ propertyName ])) {
65+ $ phpDocType = $ varTags [$ propertyName ]->getType ();
66+ }
4467
45- if (! $ propertyReflection -> hasPhpDocType () ) {
68+ if ($ phpDocType === null ) {
4669 return [];
4770 }
4871
49- $ phpDocType = $ propertyReflection ->getPhpDocType ();
5072 $ description = 'PHPDoc tag @var ' ;
51- if ($ propertyReflection ->isPromoted ()) {
73+ if ($ node ->isPromoted ()) {
5274 $ description = 'PHPDoc type ' ;
5375 }
5476
@@ -59,18 +81,18 @@ public function processNode(Node $node, Scope $scope): array
5981 $ messages [] = RuleErrorBuilder::message (sprintf (
6082 '%s for property %s::$%s contains unresolvable type. ' ,
6183 $ description ,
62- $ propertyReflection -> getDeclaringClass ()->getName (),
84+ $ scope -> getClassReflection ()->getDisplayName (),
6385 $ propertyName ,
6486 ))->build ();
6587 }
6688
67- $ nativeType = $ propertyReflection ->getNativeType ();
89+ $ nativeType = ParserNodeTypeToPHPStanType:: resolve ( $ node ->getNativeType (), $ scope -> getClassReflection () );
6890 $ isSuperType = $ nativeType ->isSuperTypeOf ($ phpDocType );
6991 if ($ isSuperType ->no ()) {
7092 $ messages [] = RuleErrorBuilder::message (sprintf (
7193 '%s for property %s::$%s with type %s is incompatible with native type %s. ' ,
7294 $ description ,
73- $ propertyReflection -> getDeclaringClass ()->getDisplayName (),
95+ $ scope -> getClassReflection ()->getDisplayName (),
7496 $ propertyName ,
7597 $ phpDocType ->describe (VerbosityLevel::typeOnly ()),
7698 $ nativeType ->describe (VerbosityLevel::typeOnly ()),
@@ -80,7 +102,7 @@ public function processNode(Node $node, Scope $scope): array
80102 $ errorBuilder = RuleErrorBuilder::message (sprintf (
81103 '%s for property %s::$%s with type %s is not subtype of native type %s. ' ,
82104 $ description ,
83- $ propertyReflection -> getDeclaringClass ()->getDisplayName (),
105+ $ scope -> getClassReflection ()->getDisplayName (),
84106 $ propertyName ,
85107 $ phpDocType ->describe (VerbosityLevel::typeOnly ()),
86108 $ nativeType ->describe (VerbosityLevel::typeOnly ()),
@@ -93,7 +115,7 @@ public function processNode(Node $node, Scope $scope): array
93115 $ messages [] = $ errorBuilder ->build ();
94116 }
95117
96- $ className = SprintfHelper::escapeFormatString ($ propertyReflection -> getDeclaringClass ()->getDisplayName ());
118+ $ className = SprintfHelper::escapeFormatString ($ scope -> getClassReflection ()->getDisplayName ());
97119 $ escapedPropertyName = SprintfHelper::escapeFormatString ($ propertyName );
98120
99121 $ messages = array_merge ($ messages , $ this ->genericObjectTypeCheck ->check (
0 commit comments