Skip to content

Commit b655779

Browse files
committed
Merge branch 2.1.x into 2.2.x
2 parents 2e0ce94 + 320298a commit b655779

File tree

4 files changed

+55
-1
lines changed

4 files changed

+55
-1
lines changed

src/Rules/PhpDoc/VarTagTypeRuleHelper.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@
1818
use PHPStan\Type\ArrayType;
1919
use PHPStan\Type\FileTypeMapper;
2020
use PHPStan\Type\Generic\GenericObjectType;
21+
use PHPStan\Type\Generic\TemplateTypeVariance;
2122
use PHPStan\Type\IsSuperTypeOfResult;
2223
use PHPStan\Type\MixedType;
2324
use PHPStan\Type\ObjectType;
2425
use PHPStan\Type\Type;
26+
use PHPStan\Type\TypeTraverser;
2527
use PHPStan\Type\TypeUtils;
2628
use PHPStan\Type\VerbosityLevel;
2729
use function array_key_exists;
30+
use function array_map;
2831
use function count;
2932
use function is_string;
3033
use function sprintf;
@@ -182,6 +185,17 @@ private function isValidSuperType(Scope $scope, Type $type, Type $varTagType, in
182185
return $this->isSuperTypeOfVarType($scope, $type, $varTagType);
183186
}
184187

188+
$type = TypeTraverser::map($type, static function (Type $type, callable $traverse): Type {
189+
if ($type instanceof GenericObjectType) {
190+
$type = $type->changeVariances(array_map(
191+
static fn (TemplateTypeVariance $variance) => $variance->invariant() ? TemplateTypeVariance::createCovariant() : $variance,
192+
$type->getVariances(),
193+
));
194+
}
195+
196+
return $traverse($type);
197+
});
198+
185199
if ($type->isConstantArray()->yes()) {
186200
if ($type->isIterableAtLeastOnce()->no()) {
187201
$type = new ArrayType(new MixedType(), new MixedType());

src/Type/Generic/GenericObjectType.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,14 @@ protected function recreate(string $className, array $types, ?Type $subtractedTy
403403
);
404404
}
405405

406+
/**
407+
* @param TemplateTypeVariance[] $variances
408+
*/
409+
public function changeVariances(array $variances): self
410+
{
411+
return $this->recreate($this->getClassName(), $this->getTypes(), $this->getSubtractedType(), $variances);
412+
}
413+
406414
public function changeSubtractedType(?Type $subtractedType): Type
407415
{
408416
$result = parent::changeSubtractedType($subtractedType);

tests/PHPStan/Rules/PhpDoc/WrongVariableNameInVarTagRuleTest.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ public function testBug12457(): void
586586
]);
587587
}
588588

589-
public function testGenericSubtype(): void
589+
public function testGenericSubtypeWithStrictCheck(): void
590590
{
591591
$this->checkTypeAgainstPhpDocType = true;
592592
$this->strictWideningCheck = true;
@@ -601,6 +601,22 @@ public function testGenericSubtype(): void
601601
131,
602602
'Template type E on class GenericSubtype\IRepository is not covariant. Learn more: <fg=cyan>https://phpstan.org/blog/whats-up-with-template-covariant</>',
603603
],
604+
[
605+
'PHPDoc tag @var with type GenericSubtype\Collection<int> is not subtype of type GenericSubtype\Collection<string>.',
606+
162,
607+
],
608+
]);
609+
}
610+
611+
public function testGenericSubtypeWithoutStrictCheck(): void
612+
{
613+
$this->checkTypeAgainstPhpDocType = true;
614+
$this->strictWideningCheck = false;
615+
$this->analyse([__DIR__ . '/data/generic-subtype.php'], [
616+
[
617+
'PHPDoc tag @var with type GenericSubtype\Collection<int> is not subtype of type GenericSubtype\Collection<string>.',
618+
162,
619+
],
604620
]);
605621
}
606622

tests/PHPStan/Rules/PhpDoc/data/generic-subtype.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,19 @@ protected function getTargetRepository(): IRepository
146146
*/
147147
protected function test($repository): void {}
148148
}
149+
150+
/** @template T */
151+
class Collection
152+
{
153+
}
154+
155+
abstract class AlwaysFail
156+
{
157+
/** @return Collection<string> */
158+
abstract public function getCollection(): Collection;
159+
160+
public function test(): void {
161+
/** @var Collection<int> $collection */
162+
$collection = $this->getCollection();
163+
}
164+
}

0 commit comments

Comments
 (0)