Skip to content

Commit 86c307b

Browse files
committed
report required template type after optional
1 parent 0ee1c3a commit 86c307b

18 files changed

+89
-0
lines changed

src/Rules/Generics/ClassTemplateTypeRule.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public function processNode(Node $node, Scope $scope): array
5151
sprintf('PHPDoc tag @template %%s for %s with bound type %%s is not supported.', $displayName),
5252
sprintf('PHPDoc tag @template %%s for %s has invalid default type %%s.', $displayName),
5353
sprintf('Default type %%s in PHPDoc tag @template %%s for %s is not subtype of bound type %%s.', $displayName),
54+
sprintf('PHPDoc tag @template %%s for %s does not have a default type but follows an optional @template %%s.', $displayName),
5455
);
5556
}
5657

src/Rules/Generics/FunctionTemplateTypeRule.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public function processNode(Node $node, Scope $scope): array
6262
sprintf('PHPDoc tag @template %%s for function %s() with bound type %%s is not supported.', $escapedFunctionName),
6363
sprintf('PHPDoc tag @template %%s for function %s() has invalid default type %%s.', $escapedFunctionName),
6464
sprintf('Default type %%s in PHPDoc tag @template %%s for function %s() is not subtype of bound type %%s.', $escapedFunctionName),
65+
sprintf('PHPDoc tag @template %%s for function %s() does not have a default type but follows an optional @template %%s.', $escapedFunctionName),
6566
);
6667
}
6768

src/Rules/Generics/InterfaceTemplateTypeRule.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public function processNode(Node $node, Scope $scope): array
4848
sprintf('PHPDoc tag @template %%s for interface %s with bound type %%s is not supported.', $escapadInterfaceName),
4949
sprintf('PHPDoc tag @template %%s for interface %s has invalid default type %%s.', $escapadInterfaceName),
5050
sprintf('Default type %%s in PHPDoc tag @template %%s for interface %s is not subtype of bound type %%s.', $escapadInterfaceName),
51+
sprintf('PHPDoc tag @template %%s for interface %s does not have a default type but follows an optional @template %%s.', $escapadInterfaceName),
5152
);
5253
}
5354

src/Rules/Generics/MethodTagTemplateTypeCheck.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public function check(
6363
sprintf('PHPDoc tag @method template %%s for method %s::%s() with bound type %%s is not supported.', $escapedClassName, $escapedMethodName),
6464
sprintf('PHPDoc tag @method template %%s for method %s::%s() has invalid default type %%s', $escapedClassName, $escapedMethodName),
6565
sprintf('Default type %%s in PHPDoc tag @method template %%s for method %s::%s() is not subtype of bound type %%s', $escapedClassName, $escapedMethodName),
66+
sprintf('PHPDoc tag @template %%s for method %s::%s() does not have a default type but follows an optional @template %%s.', $escapedClassName, $escapedMethodName),
6667
));
6768

6869
foreach (array_keys($methodTemplateTags) as $name) {

src/Rules/Generics/MethodTemplateTypeRule.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public function processNode(Node $node, Scope $scope): array
6868
sprintf('PHPDoc tag @template %%s for method %s::%s() with bound type %%s is not supported.', $escapedClassName, $escapedMethodName),
6969
sprintf('PHPDoc tag @template %%s for method %s::%s() has invalid default type %%s.', $escapedClassName, $escapedMethodName),
7070
sprintf('Default type %%s in PHPDoc tag @template %%s for method %s::%s() is not subtype of bound type %%s.', $escapedClassName, $escapedMethodName),
71+
sprintf('PHPDoc tag @template %%s for method %s::%s() does not have a default type but follows an optional @template %%s.', $escapedClassName, $escapedMethodName),
7172
);
7273

7374
$classTemplateTypes = $classReflection->getTemplateTypeMap()->getTypes();

src/Rules/Generics/TemplateTypeCheck.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,11 @@ public function check(
6464
string $notSupportedBoundMessage,
6565
string $invalidDefaultTypeMessage,
6666
string $defaultNotSubtypeOfBoundMessage,
67+
string $requiredTypeAfterOptionalMessage,
6768
): array
6869
{
6970
$messages = [];
71+
$templateTagWithDefaultType = null;
7072
foreach ($templateTags as $templateTag) {
7173
$templateTagName = $scope->resolveName(new Node\Name($templateTag->getName()));
7274
if ($this->reflectionProvider->hasClass($templateTagName)) {
@@ -146,9 +148,19 @@ public function check(
146148

147149
$defaultType = $templateTag->getDefault();
148150
if ($defaultType === null) {
151+
if ($templateTagWithDefaultType !== null) {
152+
$messages[] = RuleErrorBuilder::message(sprintf(
153+
$requiredTypeAfterOptionalMessage,
154+
$templateTagName,
155+
$templateTagWithDefaultType,
156+
))->identifier('generics.requiredTypeAfterOptional')->build();
157+
}
158+
149159
continue;
150160
}
151161

162+
$templateTagWithDefaultType = $templateTagName;
163+
152164
foreach ($defaultType->getReferencedClasses() as $referencedClass) {
153165
if (!$this->reflectionProvider->hasClass($referencedClass)) {
154166
$messages[] = RuleErrorBuilder::message(sprintf(

src/Rules/Generics/TraitTemplateTypeRule.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public function processNode(Node $node, Scope $scope): array
6262
sprintf('PHPDoc tag @template %%s for trait %s with bound type %%s is not supported.', $escapedTraitName),
6363
sprintf('PHPDoc tag @template %%s for trait %s has invalid default type %%s.', $escapedTraitName),
6464
sprintf('Default type %%s in PHPDoc tag @template %%s for trait %s is not subtype of bound type %%s.', $escapedTraitName),
65+
sprintf('PHPDoc tag @template %%s for trait %s does not have a default type but follows an optional @template %%s.', $escapedTraitName),
6566
);
6667
}
6768

src/Rules/PhpDoc/GenericCallableRuleHelper.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public function check(
6262
sprintf('PHPDoc tag %s template %%s of %s with bound type %%s is not supported.', $location, $typeDescription),
6363
sprintf('PHPDoc tag %s template %%s of %s has invalid default type %%s.', $location, $typeDescription),
6464
sprintf('Default type %%s in PHPDoc tag %s template %%s of %s is not subtype of bound type %%s.', $location, $typeDescription),
65+
sprintf('PHPDoc tag %s template %%s of %s does not have a default type but follows an optional template %%s.', $location, $typeDescription),
6566
);
6667

6768
$templateTags = $type->getTemplateTags();

tests/PHPStan/Rules/Generics/ClassTemplateTypeRuleTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ public function testRule(): void
9494
'Default type bool in PHPDoc tag @template T for class ClassTemplateType\Venenatis is not subtype of bound type object.',
9595
129,
9696
],
97+
[
98+
'PHPDoc tag @template V for class ClassTemplateType\Mauris does not have a default type but follows an optional @template U.',
99+
139,
100+
],
97101
]);
98102
}
99103

tests/PHPStan/Rules/Generics/FunctionTemplateTypeRuleTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ public function testRule(): void
7575
'Default type bool in PHPDoc tag @template T for function FunctionTemplateType\outOfBoundsDefault() is not subtype of bound type object.',
7676
110,
7777
],
78+
[
79+
'PHPDoc tag @template V for function FunctionTemplateType\requiredAfterOptional() does not have a default type but follows an optional @template U.',
80+
120,
81+
],
7882
]);
7983
}
8084

0 commit comments

Comments
 (0)