1818use PHPStan \Type \ArrayType ;
1919use PHPStan \Type \FileTypeMapper ;
2020use PHPStan \Type \Generic \GenericObjectType ;
21+ use PHPStan \Type \IsSuperTypeOfResult ;
2122use PHPStan \Type \MixedType ;
2223use PHPStan \Type \ObjectType ;
2324use PHPStan \Type \Type ;
@@ -92,22 +93,25 @@ public function checkExprType(Scope $scope, Node\Expr $expr, Type $varTagType):
9293 $ errors = [];
9394 $ exprNativeType = $ scope ->getScopeNativeType ($ expr );
9495 $ containsPhpStanType = $ this ->containsPhpStanType ($ varTagType );
95- if ($ this ->shouldVarTagTypeBeReported ($ scope , $ expr , $ exprNativeType , $ varTagType )) {
96+
97+ $ isValidSuperTypeOfExpr = $ this ->isValidSuperTypeOfExpr ($ scope , $ expr , $ exprNativeType , $ varTagType );
98+ if (!$ isValidSuperTypeOfExpr ->yes ()) {
9699 $ verbosity = VerbosityLevel::getRecommendedLevelByType ($ exprNativeType , $ varTagType );
97100 $ errors [] = RuleErrorBuilder::message (sprintf (
98101 'PHPDoc tag @var with type %s is not subtype of native type %s. ' ,
99102 $ varTagType ->describe ($ verbosity ),
100103 $ exprNativeType ->describe ($ verbosity ),
101- ))->identifier ('varTag.nativeType ' )->build ();
104+ ))->acceptsReasonsTip ( $ isValidSuperTypeOfExpr -> reasons )-> identifier ('varTag.nativeType ' )->build ();
102105 } elseif ($ this ->checkTypeAgainstPhpDocType || $ containsPhpStanType ) {
103106 $ exprType = $ scope ->getScopeType ($ expr );
104- if ($ this ->shouldVarTagTypeBeReported ($ scope , $ expr , $ exprType , $ varTagType )) {
107+ $ isValidSuperTypeOfExpr = $ this ->isValidSuperTypeOfExpr ($ scope , $ expr , $ exprType , $ varTagType );
108+ if (!$ isValidSuperTypeOfExpr ->yes ()) {
105109 $ verbosity = VerbosityLevel::getRecommendedLevelByType ($ exprType , $ varTagType );
106110 $ errors [] = RuleErrorBuilder::message (sprintf (
107111 'PHPDoc tag @var with type %s is not subtype of type %s. ' ,
108112 $ varTagType ->describe ($ verbosity ),
109113 $ exprType ->describe ($ verbosity ),
110- ))->identifier ('varTag.type ' )->build ();
114+ ))->acceptsReasonsTip ( $ isValidSuperTypeOfExpr -> reasons )-> identifier ('varTag.type ' )->build ();
111115 }
112116 }
113117
@@ -145,22 +149,22 @@ private function containsPhpStanType(Type $type): bool
145149 return false ;
146150 }
147151
148- private function shouldVarTagTypeBeReported (Scope $ scope , Node \Expr $ expr , Type $ type , Type $ varTagType ): bool
152+ private function isValidSuperTypeOfExpr (Scope $ scope , Node \Expr $ expr , Type $ type , Type $ varTagType ): IsSuperTypeOfResult
149153 {
150154 if ($ expr instanceof Expr \Array_) {
151155 if ($ expr ->items === []) {
152156 $ type = new ArrayType (new MixedType (), new MixedType ());
153157 }
154158
155- return ! $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
159+ return $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
156160 }
157161
158162 if ($ expr instanceof Expr \ConstFetch) {
159- return ! $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
163+ return $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
160164 }
161165
162166 if ($ expr instanceof Node \Scalar) {
163- return ! $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
167+ return $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
164168 }
165169
166170 if ($ expr instanceof Expr \New_) {
@@ -169,72 +173,78 @@ private function shouldVarTagTypeBeReported(Scope $scope, Node\Expr $expr, Type
169173 }
170174 }
171175
172- return $ this ->checkType ($ scope , $ type , $ varTagType );
176+ return $ this ->isValidSuperType ($ scope , $ type , $ varTagType );
173177 }
174178
175- private function checkType (Scope $ scope , Type $ type , Type $ varTagType , int $ depth = 0 ): bool
179+ private function isValidSuperType (Scope $ scope , Type $ type , Type $ varTagType , int $ depth = 0 ): IsSuperTypeOfResult
176180 {
177181 if ($ this ->strictWideningCheck ) {
178- return ! $ this ->isSuperTypeOfVarType ($ scope , $ type , $ varTagType );
182+ return $ this ->isSuperTypeOfVarType ($ scope , $ type , $ varTagType );
179183 }
180184
181185 if ($ type ->isConstantArray ()->yes ()) {
182186 if ($ type ->isIterableAtLeastOnce ()->no ()) {
183187 $ type = new ArrayType (new MixedType (), new MixedType ());
184- return ! $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
188+ return $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
185189 }
186190 }
187191
188192 if ($ type ->isIterable ()->yes () && $ varTagType ->isIterable ()->yes ()) {
189- if (!$ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType )) {
190- return true ;
193+ $ isAtLeastMaybeSuperTypeOf = $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
194+ if ($ isAtLeastMaybeSuperTypeOf ->no ()) {
195+ return $ isAtLeastMaybeSuperTypeOf ;
191196 }
192197
193198 $ innerType = $ type ->getIterableValueType ();
194199 $ innerVarTagType = $ varTagType ->getIterableValueType ();
195200
196201 if ($ type ->equals ($ innerType ) || $ varTagType ->equals ($ innerVarTagType )) {
197- return ! $ this ->isSuperTypeOfVarType ($ scope , $ innerType , $ innerVarTagType );
202+ return $ this ->isSuperTypeOfVarType ($ scope , $ innerType , $ innerVarTagType );
198203 }
199204
200- return $ this ->checkType ($ scope , $ innerType , $ innerVarTagType , $ depth + 1 );
205+ return $ this ->isValidSuperType ($ scope , $ innerType , $ innerVarTagType , $ depth + 1 );
201206 }
202207
203208 if ($ depth === 0 && $ type ->isConstantValue ()->yes ()) {
204- return ! $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
209+ return $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
205210 }
206211
207- return ! $ this ->isSuperTypeOfVarType ($ scope , $ type , $ varTagType );
212+ return $ this ->isSuperTypeOfVarType ($ scope , $ type , $ varTagType );
208213 }
209214
210- private function isSuperTypeOfVarType (Scope $ scope , Type $ type , Type $ varTagType ): bool
215+ private function isSuperTypeOfVarType (Scope $ scope , Type $ type , Type $ varTagType ): IsSuperTypeOfResult
211216 {
212217 if ($ type ->isSuperTypeOf ($ varTagType )->yes ()) {
213- return true ;
218+ return IsSuperTypeOfResult:: createYes () ;
214219 }
215220
216221 try {
217222 $ type = $ this ->typeNodeResolver ->resolve ($ type ->toPhpDocNode (), $ this ->createNameScope ($ scope ));
218223 } catch (NameScopeAlreadyBeingCreatedException ) {
219- return true ;
224+ return IsSuperTypeOfResult:: createYes () ;
220225 }
221226
222- return $ type ->isSuperTypeOf ($ varTagType )-> yes () ;
227+ return $ type ->isSuperTypeOf ($ varTagType );
223228 }
224229
225- private function isAtLeastMaybeSuperTypeOfVarType (Scope $ scope , Type $ type , Type $ varTagType ): bool
230+ private function isAtLeastMaybeSuperTypeOfVarType (Scope $ scope , Type $ type , Type $ varTagType ): IsSuperTypeOfResult
226231 {
227232 if (!$ type ->isSuperTypeOf ($ varTagType )->no ()) {
228- return true ;
233+ return IsSuperTypeOfResult:: createYes () ;
229234 }
230235
231236 try {
232237 $ type = $ this ->typeNodeResolver ->resolve ($ type ->toPhpDocNode (), $ this ->createNameScope ($ scope ));
233238 } catch (NameScopeAlreadyBeingCreatedException ) {
234- return true ;
239+ return IsSuperTypeOfResult::createYes ();
240+ }
241+
242+ $ isSuperTypeOf = $ type ->isSuperTypeOf ($ varTagType );
243+ if (!$ isSuperTypeOf ->no ()) {
244+ return IsSuperTypeOfResult::createYes ();
235245 }
236246
237- return ! $ type -> isSuperTypeOf ( $ varTagType )-> no () ;
247+ return $ isSuperTypeOf ;
238248 }
239249
240250 /**
0 commit comments