Skip to content

Commit 6aa6db5

Browse files
committed
Merge branch 2.1.x into 2.2.x
2 parents 05358ba + 7b8a821 commit 6aa6db5

File tree

2 files changed

+38
-26
lines changed

2 files changed

+38
-26
lines changed

src/Rules/PhpDoc/VarTagTypeRuleHelper.php

Lines changed: 36 additions & 26 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,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
/**

tests/PHPStan/Rules/PhpDoc/WrongVariableNameInVarTagRuleTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,10 +594,12 @@ public function testGenericSubtype(): void
594594
[
595595
'PHPDoc tag @var with type GenericSubtype\IRepository<E of GenericSubtype\IEntity> is not subtype of type GenericSubtype\IRepository<GenericSubtype\IEntity>.',
596596
78,
597+
'Template type E on class GenericSubtype\IRepository is not covariant. Learn more: <fg=cyan>https://phpstan.org/blog/whats-up-with-template-covariant</>',
597598
],
598599
[
599600
'PHPDoc tag @var with type GenericSubtype\IRepository<GenericSubtype\Foo> is not subtype of type GenericSubtype\IRepository<GenericSubtype\IEntity>.',
600601
131,
602+
'Template type E on class GenericSubtype\IRepository is not covariant. Learn more: <fg=cyan>https://phpstan.org/blog/whats-up-with-template-covariant</>',
601603
],
602604
]);
603605
}

0 commit comments

Comments
 (0)