1212use PHPStan \Rules \IdentifierRuleError ;
1313use PHPStan \Rules \RuleErrorBuilder ;
1414use PHPStan \Type \ArrayType ;
15+ use PHPStan \Type \FileTypeMapper ;
1516use PHPStan \Type \Generic \GenericObjectType ;
1617use PHPStan \Type \MixedType ;
1718use PHPStan \Type \ObjectType ;
@@ -28,6 +29,7 @@ final class VarTagTypeRuleHelper
2829
2930 public function __construct (
3031 private TypeNodeResolver $ typeNodeResolver ,
32+ private FileTypeMapper $ fileTypeMapper ,
3133 private bool $ checkTypeAgainstPhpDocType ,
3234 private bool $ strictWideningCheck ,
3335 )
@@ -82,7 +84,7 @@ public function checkExprType(Scope $scope, Node\Expr $expr, Type $varTagType):
8284 $ errors = [];
8385 $ exprNativeType = $ scope ->getNativeType ($ expr );
8486 $ containsPhpStanType = $ this ->containsPhpStanType ($ varTagType );
85- if ($ this ->shouldVarTagTypeBeReported ($ expr , $ exprNativeType , $ varTagType )) {
87+ if ($ this ->shouldVarTagTypeBeReported ($ scope , $ expr , $ exprNativeType , $ varTagType )) {
8688 $ verbosity = VerbosityLevel::getRecommendedLevelByType ($ exprNativeType , $ varTagType );
8789 $ errors [] = RuleErrorBuilder::message (sprintf (
8890 '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):
9294 } else {
9395 $ exprType = $ scope ->getType ($ expr );
9496 if (
95- $ this ->shouldVarTagTypeBeReported ($ expr , $ exprType , $ varTagType )
97+ $ this ->shouldVarTagTypeBeReported ($ scope , $ expr , $ exprType , $ varTagType )
9698 && ($ this ->checkTypeAgainstPhpDocType || $ containsPhpStanType )
9799 ) {
98100 $ verbosity = VerbosityLevel::getRecommendedLevelByType ($ exprType , $ varTagType );
@@ -133,22 +135,22 @@ private function containsPhpStanType(Type $type): bool
133135 return false ;
134136 }
135137
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
137139 {
138140 if ($ expr instanceof Expr \Array_) {
139141 if ($ expr ->items === []) {
140142 $ type = new ArrayType (new MixedType (), new MixedType ());
141143 }
142144
143- return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ type , $ varTagType );
145+ return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
144146 }
145147
146148 if ($ expr instanceof Expr \ConstFetch) {
147- return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ type , $ varTagType );
149+ return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
148150 }
149151
150152 if ($ expr instanceof Node \Scalar) {
151- return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ type , $ varTagType );
153+ return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
152154 }
153155
154156 if ($ expr instanceof Expr \New_) {
@@ -157,64 +159,76 @@ private function shouldVarTagTypeBeReported(Node\Expr $expr, Type $type, Type $v
157159 }
158160 }
159161
160- return $ this ->checkType ($ type , $ varTagType );
162+ return $ this ->checkType ($ scope , $ type , $ varTagType );
161163 }
162164
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
164166 {
165167 if ($ this ->strictWideningCheck ) {
166- return !$ this ->isSuperTypeOfVarType ($ type , $ varTagType );
168+ return !$ this ->isSuperTypeOfVarType ($ scope , $ type , $ varTagType );
167169 }
168170
169171 if ($ type ->isConstantArray ()->yes ()) {
170172 if ($ type ->isIterableAtLeastOnce ()->no ()) {
171173 $ type = new ArrayType (new MixedType (), new MixedType ());
172- return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ type , $ varTagType );
174+ return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
173175 }
174176 }
175177
176178 if ($ type ->isIterable ()->yes () && $ varTagType ->isIterable ()->yes ()) {
177- if (!$ this ->isAtLeastMaybeSuperTypeOfVarType ($ type , $ varTagType )) {
179+ if (!$ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType )) {
178180 return true ;
179181 }
180182
181183 $ innerType = $ type ->getIterableValueType ();
182184 $ innerVarTagType = $ varTagType ->getIterableValueType ();
183185
184186 if ($ type ->equals ($ innerType ) || $ varTagType ->equals ($ innerVarTagType )) {
185- return !$ this ->isSuperTypeOfVarType ($ innerType , $ innerVarTagType );
187+ return !$ this ->isSuperTypeOfVarType ($ scope , $ innerType , $ innerVarTagType );
186188 }
187189
188- return $ this ->checkType ($ innerType , $ innerVarTagType , $ depth + 1 );
190+ return $ this ->checkType ($ scope , $ innerType , $ innerVarTagType , $ depth + 1 );
189191 }
190192
191193 if ($ depth === 0 && $ type ->isConstantValue ()->yes ()) {
192- return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ type , $ varTagType );
194+ return !$ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
193195 }
194196
195- return !$ this ->isSuperTypeOfVarType ($ type , $ varTagType );
197+ return !$ this ->isSuperTypeOfVarType ($ scope , $ type , $ varTagType );
196198 }
197199
198- private function isSuperTypeOfVarType (Type $ type , Type $ varTagType ): bool
200+ private function isSuperTypeOfVarType (Scope $ scope , Type $ type , Type $ varTagType ): bool
199201 {
200202 if ($ type ->isSuperTypeOf ($ varTagType )->yes ()) {
201203 return true ;
202204 }
203205
204- $ type = $ this ->typeNodeResolver ->resolve ($ type ->toPhpDocNode (), new NameScope ( null , [] ));
206+ $ type = $ this ->typeNodeResolver ->resolve ($ type ->toPhpDocNode (), $ this -> createNameScope ( $ scope ));
205207
206208 return $ type ->isSuperTypeOf ($ varTagType )->yes ();
207209 }
208210
209- private function isAtLeastMaybeSuperTypeOfVarType (Type $ type , Type $ varTagType ): bool
211+ private function isAtLeastMaybeSuperTypeOfVarType (Scope $ scope , Type $ type , Type $ varTagType ): bool
210212 {
211213 if (!$ type ->isSuperTypeOf ($ varTagType )->no ()) {
212214 return true ;
213215 }
214216
215- $ type = $ this ->typeNodeResolver ->resolve ($ type ->toPhpDocNode (), new NameScope ( null , [] ));
217+ $ type = $ this ->typeNodeResolver ->resolve ($ type ->toPhpDocNode (), $ this -> createNameScope ( $ scope ));
216218
217219 return !$ type ->isSuperTypeOf ($ varTagType )->no ();
218220 }
219221
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+
220234}
0 commit comments