Skip to content

Commit df5ee10

Browse files
phpstan-botclaude
andcommitted
Move generic type arg check from TemplateTypeVariance to VarTagTypeRuleHelper
Revert the TemplateTypeVariance hack that treated template types as equal to their bounds for invariant checks - this was conceptually wrong since invariant means exact type equality. Instead, fix VarTagTypeRuleHelper::checkType() to compare generic object type arguments individually (covariantly) when both types are the same generic class. This correctly allows @var tags like `@var IRepository<E>` or `@var IRepository<Foo>` when the expression type is `IRepository<IEntity>` and E/Foo are subtypes of IEntity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3e1b2d6 commit df5ee10

File tree

3 files changed

+18
-4
lines changed

3 files changed

+18
-4
lines changed

phpstan-baseline.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,7 @@ parameters:
726726
-
727727
rawMessage: Doing instanceof PHPStan\Type\Generic\GenericObjectType is error-prone and deprecated.
728728
identifier: phpstanApi.instanceofType
729-
count: 1
729+
count: 3
730730
path: src/Rules/PhpDoc/VarTagTypeRuleHelper.php
731731

732732
-

src/Rules/PhpDoc/VarTagTypeRuleHelper.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,23 @@ private function shouldVarTagTypeBeReported(Scope $scope, Node\Expr $expr, Type
177177

178178
private function checkType(Scope $scope, Type $type, Type $varTagType, int $depth = 0): bool
179179
{
180+
if (
181+
$type instanceof GenericObjectType
182+
&& $varTagType instanceof GenericObjectType
183+
&& $type->getClassName() === $varTagType->getClassName()
184+
) {
185+
$typeArgs = $type->getTypes();
186+
$varTagArgs = $varTagType->getTypes();
187+
if (count($typeArgs) === count($varTagArgs)) {
188+
foreach ($typeArgs as $i => $typeArg) {
189+
if ($this->checkType($scope, $typeArg, $varTagArgs[$i], $depth + 1)) {
190+
return true;
191+
}
192+
}
193+
return false;
194+
}
195+
}
196+
180197
if ($this->strictWideningCheck) {
181198
return !$this->isSuperTypeOfVarType($scope, $type, $varTagType);
182199
}

src/Type/Generic/TemplateTypeVariance.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,6 @@ public function isValidVariance(TemplateType $templateType, Type $a, Type $b): I
177177

178178
if ($this->invariant()) {
179179
$result = $a->equals($b);
180-
if (!$result && $b instanceof TemplateType && $a->equals($b->getBound())) {
181-
$result = true;
182-
}
183180
$reasons = [];
184181
if (!$result) {
185182
if (

0 commit comments

Comments
 (0)