12
12
use PHPStan \Rules \IdentifierRuleError ;
13
13
use PHPStan \Rules \RuleErrorBuilder ;
14
14
use PHPStan \Type \ArrayType ;
15
+ use PHPStan \Type \FileTypeMapper ;
15
16
use PHPStan \Type \Generic \GenericObjectType ;
16
17
use PHPStan \Type \MixedType ;
17
18
use PHPStan \Type \ObjectType ;
@@ -28,6 +29,7 @@ final class VarTagTypeRuleHelper
28
29
29
30
public function __construct (
30
31
private TypeNodeResolver $ typeNodeResolver ,
32
+ private FileTypeMapper $ fileTypeMapper ,
31
33
private bool $ checkTypeAgainstPhpDocType ,
32
34
private bool $ strictWideningCheck ,
33
35
)
@@ -82,7 +84,7 @@ public function checkExprType(Scope $scope, Node\Expr $expr, Type $varTagType):
82
84
$ errors = [];
83
85
$ exprNativeType = $ scope ->getNativeType ($ expr );
84
86
$ containsPhpStanType = $ this ->containsPhpStanType ($ varTagType );
85
- if ($ this ->shouldVarTagTypeBeReported ($ expr , $ exprNativeType , $ varTagType )) {
87
+ if ($ this ->shouldVarTagTypeBeReported ($ scope , $ expr , $ exprNativeType , $ varTagType )) {
86
88
$ verbosity = VerbosityLevel::getRecommendedLevelByType ($ exprNativeType , $ varTagType );
87
89
$ errors [] = RuleErrorBuilder::message (sprintf (
88
90
'PHPDoc tag @var with type %s is not subtype of native type %s. ' ,
@@ -92,7 +94,7 @@ public function checkExprType(Scope $scope, Node\Expr $expr, Type $varTagType):
92
94
} else {
93
95
$ exprType = $ scope ->getType ($ expr );
94
96
if (
95
- $ this ->shouldVarTagTypeBeReported ($ expr , $ exprType , $ varTagType )
97
+ $ this ->shouldVarTagTypeBeReported ($ scope , $ expr , $ exprType , $ varTagType )
96
98
&& ($ this ->checkTypeAgainstPhpDocType || $ containsPhpStanType )
97
99
) {
98
100
$ verbosity = VerbosityLevel::getRecommendedLevelByType ($ exprType , $ varTagType );
@@ -133,22 +135,22 @@ private function containsPhpStanType(Type $type): bool
133
135
return false ;
134
136
}
135
137
136
- private function shouldVarTagTypeBeReported (Node \Expr $ expr , Type $ type , Type $ varTagType ): bool
138
+ private function shouldVarTagTypeBeReported (Scope $ scope , Node \Expr $ expr , Type $ type , Type $ varTagType ): bool
137
139
{
138
140
if ($ expr instanceof Expr \Array_) {
139
141
if ($ expr ->items === []) {
140
142
$ type = new ArrayType (new MixedType (), new MixedType ());
141
143
}
142
144
143
- return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ type , $ varTagType );
145
+ return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
144
146
}
145
147
146
148
if ($ expr instanceof Expr \ConstFetch) {
147
- return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ type , $ varTagType );
149
+ return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
148
150
}
149
151
150
152
if ($ expr instanceof Node \Scalar) {
151
- return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ type , $ varTagType );
153
+ return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
152
154
}
153
155
154
156
if ($ expr instanceof Expr \New_) {
@@ -157,64 +159,76 @@ private function shouldVarTagTypeBeReported(Node\Expr $expr, Type $type, Type $v
157
159
}
158
160
}
159
161
160
- return $ this ->checkType ($ type , $ varTagType );
162
+ return $ this ->checkType ($ scope , $ type , $ varTagType );
161
163
}
162
164
163
- private function checkType (Type $ type , Type $ varTagType , int $ depth = 0 ): bool
165
+ private function checkType (Scope $ scope , Type $ type , Type $ varTagType , int $ depth = 0 ): bool
164
166
{
165
167
if ($ this ->strictWideningCheck ) {
166
- return !$ this ->isSuperTypeOfVarType ($ type , $ varTagType );
168
+ return !$ this ->isSuperTypeOfVarType ($ scope , $ type , $ varTagType );
167
169
}
168
170
169
171
if ($ type ->isConstantArray ()->yes ()) {
170
172
if ($ type ->isIterableAtLeastOnce ()->no ()) {
171
173
$ type = new ArrayType (new MixedType (), new MixedType ());
172
- return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ type , $ varTagType );
174
+ return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
173
175
}
174
176
}
175
177
176
178
if ($ type ->isIterable ()->yes () && $ varTagType ->isIterable ()->yes ()) {
177
- if (!$ this ->isAtLeastMaybeSuperTypeOfVarType ($ type , $ varTagType )) {
179
+ if (!$ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType )) {
178
180
return true ;
179
181
}
180
182
181
183
$ innerType = $ type ->getIterableValueType ();
182
184
$ innerVarTagType = $ varTagType ->getIterableValueType ();
183
185
184
186
if ($ type ->equals ($ innerType ) || $ varTagType ->equals ($ innerVarTagType )) {
185
- return !$ this ->isSuperTypeOfVarType ($ innerType , $ innerVarTagType );
187
+ return !$ this ->isSuperTypeOfVarType ($ scope , $ innerType , $ innerVarTagType );
186
188
}
187
189
188
- return $ this ->checkType ($ innerType , $ innerVarTagType , $ depth + 1 );
190
+ return $ this ->checkType ($ scope , $ innerType , $ innerVarTagType , $ depth + 1 );
189
191
}
190
192
191
193
if ($ depth === 0 && $ type ->isConstantValue ()->yes ()) {
192
- return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ type , $ varTagType );
194
+ return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
193
195
}
194
196
195
- return !$ this ->isSuperTypeOfVarType ($ type , $ varTagType );
197
+ return !$ this ->isSuperTypeOfVarType ($ scope , $ type , $ varTagType );
196
198
}
197
199
198
- private function isSuperTypeOfVarType (Type $ type , Type $ varTagType ): bool
200
+ private function isSuperTypeOfVarType (Scope $ scope , Type $ type , Type $ varTagType ): bool
199
201
{
200
202
if ($ type ->isSuperTypeOf ($ varTagType )->yes ()) {
201
203
return true ;
202
204
}
203
205
204
- $ type = $ this ->typeNodeResolver ->resolve ($ type ->toPhpDocNode (), new NameScope ( null , [] ));
206
+ $ type = $ this ->typeNodeResolver ->resolve ($ type ->toPhpDocNode (), $ this -> createNameScope ( $ scope ));
205
207
206
208
return $ type ->isSuperTypeOf ($ varTagType )->yes ();
207
209
}
208
210
209
- private function isAtLeastMaybeSuperTypeOfVarType (Type $ type , Type $ varTagType ): bool
211
+ private function isAtLeastMaybeSuperTypeOfVarType (Scope $ scope , Type $ type , Type $ varTagType ): bool
210
212
{
211
213
if (!$ type ->isSuperTypeOf ($ varTagType )->no ()) {
212
214
return true ;
213
215
}
214
216
215
- $ type = $ this ->typeNodeResolver ->resolve ($ type ->toPhpDocNode (), new NameScope ( null , [] ));
217
+ $ type = $ this ->typeNodeResolver ->resolve ($ type ->toPhpDocNode (), $ this -> createNameScope ( $ scope ));
216
218
217
219
return !$ type ->isSuperTypeOf ($ varTagType )->no ();
218
220
}
219
221
222
+ private function createNameScope (Scope $ scope ): NameScope
223
+ {
224
+ $ function = $ scope ->getFunction ();
225
+
226
+ return $ this ->fileTypeMapper ->getNameScope (
227
+ $ scope ->getFile (),
228
+ $ scope ->isInClass () ? $ scope ->getClassReflection ()->getName () : null ,
229
+ $ scope ->isInTrait () ? $ scope ->getTraitReflection ()->getName () : null ,
230
+ $ function !== null ? $ function ->getName () : null ,
231
+ );
232
+ }
233
+
220
234
}
0 commit comments