10
10
use PHPStan \Rules \Rule ;
11
11
use PHPStan \Rules \RuleErrorBuilder ;
12
12
use PHPStan \ShouldNotHappenException ;
13
+ use PHPStan \Type \FileTypeMapper ;
13
14
use PHPStan \Type \Generic \TemplateType ;
15
+ use PHPStan \Type \ParserNodeTypeToPHPStanType ;
14
16
use PHPStan \Type \VerbosityLevel ;
15
17
use function array_merge ;
18
+ use function count ;
16
19
use function sprintf ;
17
20
18
21
/**
@@ -24,6 +27,7 @@ class IncompatiblePropertyPhpDocTypeRule implements Rule
24
27
public function __construct (
25
28
private GenericObjectTypeCheck $ genericObjectTypeCheck ,
26
29
private UnresolvableTypeHelper $ unresolvableTypeHelper ,
30
+ private FileTypeMapper $ fileTypeMapper ,
27
31
)
28
32
{
29
33
}
@@ -40,15 +44,33 @@ public function processNode(Node $node, Scope $scope): array
40
44
}
41
45
42
46
$ 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
+ }
44
67
45
- if (! $ propertyReflection -> hasPhpDocType () ) {
68
+ if ($ phpDocType === null ) {
46
69
return [];
47
70
}
48
71
49
- $ phpDocType = $ propertyReflection ->getPhpDocType ();
50
72
$ description = 'PHPDoc tag @var ' ;
51
- if ($ propertyReflection ->isPromoted ()) {
73
+ if ($ node ->isPromoted ()) {
52
74
$ description = 'PHPDoc type ' ;
53
75
}
54
76
@@ -59,18 +81,18 @@ public function processNode(Node $node, Scope $scope): array
59
81
$ messages [] = RuleErrorBuilder::message (sprintf (
60
82
'%s for property %s::$%s contains unresolvable type. ' ,
61
83
$ description ,
62
- $ propertyReflection -> getDeclaringClass ()->getName (),
84
+ $ scope -> getClassReflection ()->getDisplayName (),
63
85
$ propertyName ,
64
86
))->build ();
65
87
}
66
88
67
- $ nativeType = $ propertyReflection ->getNativeType ();
89
+ $ nativeType = ParserNodeTypeToPHPStanType:: resolve ( $ node ->getNativeType (), $ scope -> getClassReflection () );
68
90
$ isSuperType = $ nativeType ->isSuperTypeOf ($ phpDocType );
69
91
if ($ isSuperType ->no ()) {
70
92
$ messages [] = RuleErrorBuilder::message (sprintf (
71
93
'%s for property %s::$%s with type %s is incompatible with native type %s. ' ,
72
94
$ description ,
73
- $ propertyReflection -> getDeclaringClass ()->getDisplayName (),
95
+ $ scope -> getClassReflection ()->getDisplayName (),
74
96
$ propertyName ,
75
97
$ phpDocType ->describe (VerbosityLevel::typeOnly ()),
76
98
$ nativeType ->describe (VerbosityLevel::typeOnly ()),
@@ -80,7 +102,7 @@ public function processNode(Node $node, Scope $scope): array
80
102
$ errorBuilder = RuleErrorBuilder::message (sprintf (
81
103
'%s for property %s::$%s with type %s is not subtype of native type %s. ' ,
82
104
$ description ,
83
- $ propertyReflection -> getDeclaringClass ()->getDisplayName (),
105
+ $ scope -> getClassReflection ()->getDisplayName (),
84
106
$ propertyName ,
85
107
$ phpDocType ->describe (VerbosityLevel::typeOnly ()),
86
108
$ nativeType ->describe (VerbosityLevel::typeOnly ()),
@@ -93,7 +115,7 @@ public function processNode(Node $node, Scope $scope): array
93
115
$ messages [] = $ errorBuilder ->build ();
94
116
}
95
117
96
- $ className = SprintfHelper::escapeFormatString ($ propertyReflection -> getDeclaringClass ()->getDisplayName ());
118
+ $ className = SprintfHelper::escapeFormatString ($ scope -> getClassReflection ()->getDisplayName ());
97
119
$ escapedPropertyName = SprintfHelper::escapeFormatString ($ propertyName );
98
120
99
121
$ messages = array_merge ($ messages , $ this ->genericObjectTypeCheck ->check (
0 commit comments