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,29 +93,29 @@ 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+ $ isExprSubType = $ this ->isExprSubType ($ scope , $ expr , $ exprNativeType , $ varTagType );
98+ if (!$ isExprSubType ->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 ();
102- } else {
104+ ))->acceptsReasonsTip ( $ isExprSubType -> reasons )-> identifier ('varTag.nativeType ' )->build ();
105+ } elseif ( $ this -> checkTypeAgainstPhpDocType || $ containsPhpStanType ) {
103106 $ exprType = $ scope ->getScopeType ($ expr );
104- if (
105- $ this ->shouldVarTagTypeBeReported ($ scope , $ expr , $ exprType , $ varTagType )
106- && ($ this ->checkTypeAgainstPhpDocType || $ containsPhpStanType )
107- ) {
107+ $ isExprSubType = $ this ->isExprSubType ($ scope , $ expr , $ exprType , $ varTagType );
108+ if (!$ isExprSubType ->yes ()) {
108109 $ verbosity = VerbosityLevel::getRecommendedLevelByType ($ exprType , $ varTagType );
109110 $ errors [] = RuleErrorBuilder::message (sprintf (
110111 'PHPDoc tag @var with type %s is not subtype of type %s. ' ,
111112 $ varTagType ->describe ($ verbosity ),
112113 $ exprType ->describe ($ verbosity ),
113- ))->identifier ('varTag.type ' )->build ();
114+ ))->acceptsReasonsTip ( $ isExprSubType -> reasons )-> identifier ('varTag.type ' )->build ();
114115 }
115116 }
116117
117- if (count ($ errors ) === 0 && $ containsPhpStanType ) {
118+ if ($ containsPhpStanType && count ($ errors ) === 0 ) {
118119 $ exprType = $ scope ->getScopeType ($ expr );
119120 if (!$ exprType ->equals ($ varTagType )) {
120121 $ verbosity = VerbosityLevel::getRecommendedLevelByType ($ exprType , $ varTagType );
@@ -148,22 +149,22 @@ private function containsPhpStanType(Type $type): bool
148149 return false ;
149150 }
150151
151- private function shouldVarTagTypeBeReported (Scope $ scope , Node \Expr $ expr , Type $ type , Type $ varTagType ): bool
152+ private function isExprSubType (Scope $ scope , Node \Expr $ expr , Type $ type , Type $ varTagType ): IsSuperTypeOfResult
152153 {
153154 if ($ expr instanceof Expr \Array_) {
154155 if ($ expr ->items === []) {
155156 $ type = new ArrayType (new MixedType (), new MixedType ());
156157 }
157158
158- return ! $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
159+ return $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
159160 }
160161
161162 if ($ expr instanceof Expr \ConstFetch) {
162- return ! $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
163+ return $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
163164 }
164165
165166 if ($ expr instanceof Node \Scalar) {
166- return ! $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
167+ return $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
167168 }
168169
169170 if ($ expr instanceof Expr \New_) {
@@ -172,72 +173,78 @@ private function shouldVarTagTypeBeReported(Scope $scope, Node\Expr $expr, Type
172173 }
173174 }
174175
175- return $ this ->checkType ($ scope , $ type , $ varTagType );
176+ return $ this ->isTypeSubType ($ scope , $ type , $ varTagType );
176177 }
177178
178- private function checkType (Scope $ scope , Type $ type , Type $ varTagType , int $ depth = 0 ): bool
179+ private function isTypeSubType (Scope $ scope , Type $ type , Type $ varTagType , int $ depth = 0 ): IsSuperTypeOfResult
179180 {
180181 if ($ this ->strictWideningCheck ) {
181- return ! $ this ->isSuperTypeOfVarType ($ scope , $ type , $ varTagType );
182+ return $ this ->isSuperTypeOfVarType ($ scope , $ type , $ varTagType );
182183 }
183184
184185 if ($ type ->isConstantArray ()->yes ()) {
185186 if ($ type ->isIterableAtLeastOnce ()->no ()) {
186187 $ type = new ArrayType (new MixedType (), new MixedType ());
187- return ! $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
188+ return $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
188189 }
189190 }
190191
191192 if ($ type ->isIterable ()->yes () && $ varTagType ->isIterable ()->yes ()) {
192- if (!$ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType )) {
193- return true ;
193+ $ isAtLeastMaybeSuperTypeOf = $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
194+ if ($ isAtLeastMaybeSuperTypeOf ->no ()) {
195+ return $ isAtLeastMaybeSuperTypeOf ;
194196 }
195197
196198 $ innerType = $ type ->getIterableValueType ();
197199 $ innerVarTagType = $ varTagType ->getIterableValueType ();
198200
199201 if ($ type ->equals ($ innerType ) || $ varTagType ->equals ($ innerVarTagType )) {
200- return ! $ this ->isSuperTypeOfVarType ($ scope , $ innerType , $ innerVarTagType );
202+ return $ this ->isSuperTypeOfVarType ($ scope , $ innerType , $ innerVarTagType );
201203 }
202204
203- return $ this ->checkType ($ scope , $ innerType , $ innerVarTagType , $ depth + 1 );
205+ return $ this ->isTypeSubType ($ scope , $ innerType , $ innerVarTagType , $ depth + 1 );
204206 }
205207
206208 if ($ depth === 0 && $ type ->isConstantValue ()->yes ()) {
207- return ! $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
209+ return $ this ->isAtLeastMaybeSuperTypeOfVarType ($ scope , $ type , $ varTagType );
208210 }
209211
210- return ! $ this ->isSuperTypeOfVarType ($ scope , $ type , $ varTagType );
212+ return $ this ->isSuperTypeOfVarType ($ scope , $ type , $ varTagType );
211213 }
212214
213- private function isSuperTypeOfVarType (Scope $ scope , Type $ type , Type $ varTagType ): bool
215+ private function isSuperTypeOfVarType (Scope $ scope , Type $ type , Type $ varTagType ): IsSuperTypeOfResult
214216 {
215217 if ($ type ->isSuperTypeOf ($ varTagType )->yes ()) {
216- return true ;
218+ return IsSuperTypeOfResult:: createYes () ;
217219 }
218220
219221 try {
220222 $ type = $ this ->typeNodeResolver ->resolve ($ type ->toPhpDocNode (), $ this ->createNameScope ($ scope ));
221223 } catch (NameScopeAlreadyBeingCreatedException ) {
222- return true ;
224+ return IsSuperTypeOfResult:: createYes () ;
223225 }
224226
225- return $ type ->isSuperTypeOf ($ varTagType )-> yes () ;
227+ return $ type ->isSuperTypeOf ($ varTagType );
226228 }
227229
228- private function isAtLeastMaybeSuperTypeOfVarType (Scope $ scope , Type $ type , Type $ varTagType ): bool
230+ private function isAtLeastMaybeSuperTypeOfVarType (Scope $ scope , Type $ type , Type $ varTagType ): IsSuperTypeOfResult
229231 {
230232 if (!$ type ->isSuperTypeOf ($ varTagType )->no ()) {
231- return true ;
233+ return IsSuperTypeOfResult:: createYes () ;
232234 }
233235
234236 try {
235237 $ type = $ this ->typeNodeResolver ->resolve ($ type ->toPhpDocNode (), $ this ->createNameScope ($ scope ));
236238 } catch (NameScopeAlreadyBeingCreatedException ) {
237- return true ;
239+ return IsSuperTypeOfResult::createYes ();
240+ }
241+
242+ $ isSuperTypeOf = $ type ->isSuperTypeOf ($ varTagType );
243+ if (!$ isSuperTypeOf ->no ()) {
244+ return IsSuperTypeOfResult::createYes ();
238245 }
239246
240- return ! $ type -> isSuperTypeOf ( $ varTagType )-> no () ;
247+ return $ isSuperTypeOf ;
241248 }
242249
243250 /**
0 commit comments