Skip to content

Commit 756af18

Browse files
committed
TemplateTypeCheck - look for nested unsupported types
1 parent ea7e0ac commit 756af18

File tree

4 files changed

+34
-14
lines changed

4 files changed

+34
-14
lines changed

src/Rules/Generics/TemplateTypeCheck.php

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
use PHPStan\Type\MixedType;
1212
use PHPStan\Type\ObjectType;
1313
use PHPStan\Type\ObjectWithoutClassType;
14+
use PHPStan\Type\Type;
15+
use PHPStan\Type\TypeTraverser;
1416
use PHPStan\Type\UnionType;
1517
use PHPStan\Type\VerbosityLevel;
1618
use function array_key_exists;
@@ -101,18 +103,21 @@ public function check(
101103
$messages = array_merge($messages, $this->classCaseSensitivityCheck->checkClassNames($classNameNodePairs));
102104
}
103105

104-
$bound = $templateTag->getBound();
105-
$boundClass = get_class($bound);
106-
if (
107-
$boundClass === MixedType::class
108-
|| $boundClass === ObjectWithoutClassType::class
109-
|| $bound instanceof ObjectType
110-
|| $bound instanceof UnionType
111-
) {
112-
continue;
113-
}
106+
TypeTraverser::map($templateTag->getBound(), static function (Type $type, callable $traverse) use (&$messages, $notSupportedBoundMessage, $templateTagName): Type {
107+
$boundClass = get_class($type);
108+
if (
109+
$boundClass === MixedType::class
110+
|| $boundClass === ObjectWithoutClassType::class
111+
|| $boundClass === ObjectType::class
112+
|| $type instanceof UnionType
113+
) {
114+
return $traverse($type);
115+
}
116+
117+
$messages[] = RuleErrorBuilder::message(sprintf($notSupportedBoundMessage, $templateTagName, $type->describe(VerbosityLevel::typeOnly())))->build();
114118

115-
$messages[] = RuleErrorBuilder::message(sprintf($notSupportedBoundMessage, $templateTagName, $boundType->describe(VerbosityLevel::typeOnly())))->build();
119+
return $type;
120+
});
116121
}
117122

118123
return $messages;

src/Type/Generic/TemplateTypeFactory.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,10 @@ public static function create(TemplateTypeScope $scope, string $name, ?Type $bou
2121
return new TemplateMixedType($scope, $strategy, $variance, $name);
2222
}
2323

24-
if ($bound instanceof ObjectType) {
24+
$boundClass = get_class($bound);
25+
if ($boundClass === ObjectType::class) {
2526
return new TemplateObjectType($scope, $strategy, $variance, $name, $bound->getClassName());
2627
}
27-
28-
$boundClass = get_class($bound);
2928
if ($boundClass === ObjectWithoutClassType::class) {
3029
return new TemplateObjectWithoutClassType($scope, $strategy, $variance, $name);
3130
}

tests/PHPStan/Rules/Generics/InterfaceTemplateTypeRuleTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ public function testRule(): void
4141
'PHPDoc tag @template for interface InterfaceTemplateType\Lorem cannot have existing type alias TypeAlias as its name.',
4242
32,
4343
],
44+
[
45+
'PHPDoc tag @template T for interface InterfaceTemplateType\UnionBound with bound type InterfaceTemplateType\NormalT<stdClass> is not supported.',
46+
44,
47+
],
4448
]);
4549
}
4650

tests/PHPStan/Rules/Generics/data/interface-template.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,15 @@ interface Lorem
3333
{
3434

3535
}
36+
37+
/** @template T */
38+
interface NormalT
39+
{
40+
41+
}
42+
43+
/** @template T of NormalT<\stdClass>|\stdClass */
44+
interface UnionBound
45+
{
46+
47+
}

0 commit comments

Comments
 (0)