Skip to content

Commit f7d2d75

Browse files
hrachondrejmirtes
authored andcommitted
generics: disallow variance definition other than in class or interface
1 parent 7c4f331 commit f7d2d75

File tree

6 files changed

+26
-38
lines changed

6 files changed

+26
-38
lines changed

src/Rules/Generics/FunctionSignatureVarianceRule.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function processNode(Node $node, Scope $scope): array
3939
ParametersAcceptorSelector::selectSingle($functionReflection->getVariants()),
4040
sprintf('in parameter %%s of function %s()', $functionName),
4141
sprintf('in return type of function %s()', $functionName),
42+
sprintf('in function %s()', $functionName),
4243
false
4344
);
4445
}

src/Rules/Generics/MethodSignatureVarianceRule.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public function processNode(Node $node, Scope $scope): array
3838
ParametersAcceptorSelector::selectSingle($method->getVariants()),
3939
sprintf('in parameter %%s of method %s::%s()', $method->getDeclaringClass()->getDisplayName(), $method->getName()),
4040
sprintf('in return type of method %s::%s()', $method->getDeclaringClass()->getDisplayName(), $method->getName()),
41+
sprintf('in method %s::%s()', $method->getDeclaringClass()->getDisplayName(), $method->getName()),
4142
$method->getName() === '__construct' || $method->isStatic()
4243
);
4344
}

src/Rules/Generics/VarianceCheck.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public function checkParametersAcceptor(
1717
ParametersAcceptor $parametersAcceptor,
1818
string $parameterTypeMessage,
1919
string $returnTypeMessage,
20+
string $generalMessage,
2021
bool $isStatic
2122
): array
2223
{
@@ -33,6 +34,21 @@ public function checkParametersAcceptor(
3334
}
3435
}
3536

37+
foreach ($parametersAcceptor->getTemplateTypeMap()->getTypes() as $templateType) {
38+
if (!$templateType instanceof TemplateType
39+
|| $templateType->getScope()->getFunctionName() === null
40+
|| $templateType->getVariance()->invariant()
41+
) {
42+
continue;
43+
}
44+
45+
$errors[] = RuleErrorBuilder::message(sprintf(
46+
'Variance annotation is only allowed for type parameters of classes and interfaces, but occurs in template type %s in %s.',
47+
$templateType->getName(),
48+
$generalMessage
49+
))->build();
50+
}
51+
3652
$variance = TemplateTypeVariance::createCovariant();
3753
$type = $parametersAcceptor->getReturnType();
3854
foreach ($this->check($variance, $type, $returnTypeMessage) as $error) {
@@ -49,7 +65,8 @@ public function check(TemplateTypeVariance $positionVariance, Type $type, string
4965

5066
foreach ($type->getReferencedTemplateTypes($positionVariance) as $reference) {
5167
$referredType = $reference->getType();
52-
if ($this->isTemplateTypeVarianceValid($reference->getPositionVariance(), $referredType)) {
68+
if (($referredType->getScope()->getFunctionName() !== null && !$referredType->getVariance()->invariant())
69+
|| $this->isTemplateTypeVarianceValid($reference->getPositionVariance(), $referredType)) {
5370
continue;
5471
}
5572

tests/PHPStan/Generics/data/variance-2.json

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,32 +40,17 @@
4040
"ignorable": true
4141
},
4242
{
43-
"message": "Template type T is declared as covariant, but occurs in contravariant position in parameter a of function PHPStan\\Generics\\Variance\\x().",
43+
"message": "Variance annotation is only allowed for type parameters of classes and interfaces, but occurs in template type T in in function PHPStan\\Generics\\Variance\\x().",
4444
"line": 101,
4545
"ignorable": true
4646
},
4747
{
48-
"message": "Template type T is declared as covariant, but occurs in contravariant position in parameter c of function PHPStan\\Generics\\Variance\\x().",
49-
"line": 101,
50-
"ignorable": true
51-
},
52-
{
53-
"message": "Template type T is declared as covariant, but occurs in contravariant position in parameter d of function PHPStan\\Generics\\Variance\\x().",
54-
"line": 101,
55-
"ignorable": true
56-
},
57-
{
58-
"message": "Template type T is declared as covariant, but occurs in contravariant position in parameter e of function PHPStan\\Generics\\Variance\\x().",
59-
"line": 101,
60-
"ignorable": true
61-
},
62-
{
63-
"message": "Template type T is declared as covariant, but occurs in invariant position in parameter b of function PHPStan\\Generics\\Variance\\x().",
64-
"line": 101,
48+
"message": "Variance annotation is only allowed for type parameters of classes and interfaces, but occurs in template type T in in function PHPStan\\Generics\\Variance\\returnOut().",
49+
"line": 109,
6550
"ignorable": true
6651
},
6752
{
68-
"message": "Template type T is declared as covariant, but occurs in invariant position in return type of function PHPStan\\Generics\\Variance\\returnInvariant().",
53+
"message": "Variance annotation is only allowed for type parameters of classes and interfaces, but occurs in template type T in in function PHPStan\\Generics\\Variance\\returnInvariant().",
6954
"line": 117,
7055
"ignorable": true
7156
},

tests/PHPStan/Rules/Generics/FunctionSignatureVarianceRuleTest.php

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,7 @@ public function testRule(): void
2222
{
2323
$this->analyse([__DIR__ . '/data/function-signature-variance.php'], [
2424
[
25-
'Template type T is declared as covariant, but occurs in contravariant position in parameter a of function FunctionSignatureVariance\f().',
26-
20,
27-
],
28-
[
29-
'Template type T is declared as covariant, but occurs in invariant position in parameter b of function FunctionSignatureVariance\f().',
30-
20,
31-
],
32-
[
33-
'Template type T is declared as covariant, but occurs in contravariant position in parameter c of function FunctionSignatureVariance\f().',
25+
'Variance annotation is only allowed for type parameters of classes and interfaces, but occurs in template type T in in function FunctionSignatureVariance\f().',
3426
20,
3527
],
3628
]);

tests/PHPStan/Rules/Generics/MethodSignatureVarianceRuleTest.php

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,7 @@ public function testRule(): void
3838
25,
3939
],
4040
[
41-
'Template type U is declared as covariant, but occurs in contravariant position in parameter a of method MethodSignatureVariance\C::b().',
42-
35,
43-
],
44-
[
45-
'Template type U is declared as covariant, but occurs in invariant position in parameter b of method MethodSignatureVariance\C::b().',
46-
35,
47-
],
48-
[
49-
'Template type U is declared as covariant, but occurs in contravariant position in parameter c of method MethodSignatureVariance\C::b().',
41+
'Variance annotation is only allowed for type parameters of classes and interfaces, but occurs in template type U in in method MethodSignatureVariance\C::b().',
5042
35,
5143
],
5244
]);

0 commit comments

Comments
 (0)