77use PHPStan \Rules \ClassCaseSensitivityCheck ;
88use PHPStan \Rules \ClassNameNodePair ;
99use PHPStan \Rules \RuleErrorBuilder ;
10- use PHPStan \Type \Generic \TemplateTypeScope ;
10+ use PHPStan \Type \Generic \GenericObjectType ;
11+ use PHPStan \Type \Generic \TemplateType ;
1112use PHPStan \Type \IntegerType ;
1213use PHPStan \Type \MixedType ;
1314use PHPStan \Type \ObjectType ;
@@ -27,6 +28,8 @@ class TemplateTypeCheck
2728
2829 private \PHPStan \Rules \ClassCaseSensitivityCheck $ classCaseSensitivityCheck ;
2930
31+ private GenericObjectTypeCheck $ genericObjectTypeCheck ;
32+
3033 /** @var array<string, string> */
3134 private array $ typeAliases ;
3235
@@ -35,31 +38,32 @@ class TemplateTypeCheck
3538 /**
3639 * @param ReflectionProvider $reflectionProvider
3740 * @param ClassCaseSensitivityCheck $classCaseSensitivityCheck
41+ * @param GenericObjectTypeCheck $genericObjectTypeCheck
3842 * @param array<string, string> $typeAliases
3943 * @param bool $checkClassCaseSensitivity
4044 */
4145 public function __construct (
4246 ReflectionProvider $ reflectionProvider ,
4347 ClassCaseSensitivityCheck $ classCaseSensitivityCheck ,
48+ GenericObjectTypeCheck $ genericObjectTypeCheck ,
4449 array $ typeAliases ,
4550 bool $ checkClassCaseSensitivity
4651 )
4752 {
4853 $ this ->reflectionProvider = $ reflectionProvider ;
4954 $ this ->classCaseSensitivityCheck = $ classCaseSensitivityCheck ;
55+ $ this ->genericObjectTypeCheck = $ genericObjectTypeCheck ;
5056 $ this ->typeAliases = $ typeAliases ;
5157 $ this ->checkClassCaseSensitivity = $ checkClassCaseSensitivity ;
5258 }
5359
5460 /**
5561 * @param \PhpParser\Node $node
56- * @param \PHPStan\Type\Generic\TemplateTypeScope $templateTypeScope
5762 * @param array<string, \PHPStan\PhpDoc\Tag\TemplateTag> $templateTags
5863 * @return \PHPStan\Rules\RuleError[]
5964 */
6065 public function check (
6166 Node $ node ,
62- TemplateTypeScope $ templateTypeScope ,
6367 array $ templateTags ,
6468 string $ sameTemplateTypeNameAsClassMessage ,
6569 string $ sameTemplateTypeNameAsTypeMessage ,
@@ -113,7 +117,9 @@ public function check(
113117 || $ boundClass === IntegerType::class
114118 || $ boundClass === ObjectWithoutClassType::class
115119 || $ boundClass === ObjectType::class
120+ || $ boundClass === GenericObjectType::class
116121 || $ type instanceof UnionType
122+ || $ type instanceof TemplateType
117123 ) {
118124 return $ traverse ($ type );
119125 }
@@ -122,6 +128,17 @@ public function check(
122128
123129 return $ type ;
124130 });
131+
132+ $ genericObjectErrors = $ this ->genericObjectTypeCheck ->check (
133+ $ boundType ,
134+ sprintf ('PHPDoc tag @template %s bound contains generic type %%s but class %%s is not generic. ' , $ templateTagName ),
135+ sprintf ('PHPDoc tag @template %s bound has type %%s which does not specify all template types of class %%s: %%s ' , $ templateTagName ),
136+ sprintf ('PHPDoc tag @template %s bound has type %%s which specifies %%d template types, but class %%s supports only %%d: %%s ' , $ templateTagName ),
137+ sprintf ('Type %%s in generic type %%s in PHPDoc tag @template %s is not subtype of template type %%s of class %%s. ' , $ templateTagName ),
138+ );
139+ foreach ($ genericObjectErrors as $ genericObjectError ) {
140+ $ messages [] = $ genericObjectError ;
141+ }
125142 }
126143
127144 return $ messages ;
0 commit comments