Skip to content

Commit 3b282b6

Browse files
Add hint in VarTagTypeRuleHelper
1 parent 6442485 commit 3b282b6

File tree

1 file changed

+38
-31
lines changed

1 file changed

+38
-31
lines changed

src/Rules/PhpDoc/VarTagTypeRuleHelper.php

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use PHPStan\Type\ArrayType;
1919
use PHPStan\Type\FileTypeMapper;
2020
use PHPStan\Type\Generic\GenericObjectType;
21+
use PHPStan\Type\IsSuperTypeOfResult;
2122
use PHPStan\Type\MixedType;
2223
use PHPStan\Type\ObjectType;
2324
use 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

Comments
 (0)