Skip to content

Commit 7ceb19d

Browse files
committed
Bleeding edge - check template type variance in @param-out
1 parent e679f11 commit 7ceb19d

11 files changed

+51
-10
lines changed

conf/bleedingEdge.neon

+1
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ parameters:
3131
closureDefaultParameterTypeRule: true
3232
newRuleLevelHelper: true
3333
instanceofType: true
34+
paramOutVariance: true

conf/config.neon

+4
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ parameters:
6161
closureDefaultParameterTypeRule: false
6262
newRuleLevelHelper: false
6363
instanceofType: false
64+
paramOutVariance: false
6465
fileExtensions:
6566
- php
6667
checkAdvancedIsset: false
@@ -289,6 +290,7 @@ parametersSchema:
289290
closureDefaultParameterTypeRule: bool()
290291
newRuleLevelHelper: bool()
291292
instanceofType: bool()
293+
paramOutVariance: bool()
292294
])
293295
fileExtensions: listOf(string())
294296
checkAdvancedIsset: bool()
@@ -1031,6 +1033,8 @@ services:
10311033

10321034
-
10331035
class: PHPStan\Rules\Generics\VarianceCheck
1036+
arguments:
1037+
checkParamOutVariance: %featureToggles.paramOutVariance%
10341038

10351039
-
10361040
class: PHPStan\Rules\IssetCheck

src/Rules/Generics/FunctionSignatureVarianceRule.php

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public function processNode(Node $node, Scope $scope): array
3333
return $this->varianceCheck->checkParametersAcceptor(
3434
ParametersAcceptorSelector::selectSingle($functionReflection->getVariants()),
3535
sprintf('in parameter %%s of function %s()', SprintfHelper::escapeFormatString($functionName)),
36+
sprintf('in param-out type of parameter %%s of function %s()', SprintfHelper::escapeFormatString($functionName)),
3637
sprintf('in return type of function %s()', $functionName),
3738
sprintf('in function %s()', $functionName),
3839
false,

src/Rules/Generics/MethodSignatureVarianceRule.php

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public function processNode(Node $node, Scope $scope): array
3232
return $this->varianceCheck->checkParametersAcceptor(
3333
ParametersAcceptorSelector::selectSingle($method->getVariants()),
3434
sprintf('in parameter %%s of method %s::%s()', SprintfHelper::escapeFormatString($method->getDeclaringClass()->getDisplayName()), SprintfHelper::escapeFormatString($method->getName())),
35+
sprintf('in param-out type of parameter %%s of method %s::%s()', SprintfHelper::escapeFormatString($method->getDeclaringClass()->getDisplayName()), SprintfHelper::escapeFormatString($method->getName())),
3536
sprintf('in return type of method %s::%s()', $method->getDeclaringClass()->getDisplayName(), $method->getName()),
3637
sprintf('in method %s::%s()', $method->getDeclaringClass()->getDisplayName(), $method->getName()),
3738
$method->getName() === '__construct' || $method->isStatic(),

src/Rules/Generics/VarianceCheck.php

+28-6
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,17 @@
1313
class VarianceCheck
1414
{
1515

16+
public function __construct(
17+
private bool $checkParamOutVariance,
18+
)
19+
{
20+
}
21+
1622
/** @return RuleError[] */
1723
public function checkParametersAcceptor(
1824
ParametersAcceptorWithPhpDocs $parametersAcceptor,
1925
string $parameterTypeMessage,
26+
string $parameterOutTypeMessage,
2027
string $returnTypeMessage,
2128
string $generalMessage,
2229
bool $isStatic,
@@ -44,20 +51,35 @@ public function checkParametersAcceptor(
4451
return $errors;
4552
}
4653

54+
$covariant = TemplateTypeVariance::createCovariant();
55+
$parameterVariance = $isStatic
56+
? TemplateTypeVariance::createStatic()
57+
: TemplateTypeVariance::createContravariant();
58+
4759
foreach ($parametersAcceptor->getParameters() as $parameterReflection) {
48-
$variance = $isStatic
49-
? TemplateTypeVariance::createStatic()
50-
: TemplateTypeVariance::createContravariant();
5160
$type = $parameterReflection->getType();
5261
$message = sprintf($parameterTypeMessage, $parameterReflection->getName());
53-
foreach ($this->check($variance, $type, $message) as $error) {
62+
foreach ($this->check($parameterVariance, $type, $message) as $error) {
63+
$errors[] = $error;
64+
}
65+
66+
if (!$this->checkParamOutVariance) {
67+
continue;
68+
}
69+
70+
$paramOutType = $parameterReflection->getOutType();
71+
if ($paramOutType === null) {
72+
continue;
73+
}
74+
75+
$outMessage = sprintf($parameterOutTypeMessage, $parameterReflection->getName());
76+
foreach ($this->check($covariant, $paramOutType, $outMessage) as $error) {
5477
$errors[] = $error;
5578
}
5679
}
5780

58-
$variance = TemplateTypeVariance::createCovariant();
5981
$type = $parametersAcceptor->getReturnType();
60-
foreach ($this->check($variance, $type, $returnTypeMessage) as $error) {
82+
foreach ($this->check($covariant, $type, $returnTypeMessage) as $error) {
6183
$errors[] = $error;
6284
}
6385

tests/PHPStan/Rules/Generics/ClassAncestorsRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ protected function getRule(): Rule
1717
new GenericAncestorsCheck(
1818
$this->createReflectionProvider(),
1919
new GenericObjectTypeCheck(),
20-
new VarianceCheck(),
20+
new VarianceCheck(true),
2121
true,
2222
[],
2323
),

tests/PHPStan/Rules/Generics/EnumAncestorsRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ protected function getRule(): Rule
1818
new GenericAncestorsCheck(
1919
$this->createReflectionProvider(),
2020
new GenericObjectTypeCheck(),
21-
new VarianceCheck(),
21+
new VarianceCheck(true),
2222
true,
2323
[],
2424
),

tests/PHPStan/Rules/Generics/InterfaceAncestorsRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ protected function getRule(): Rule
1717
new GenericAncestorsCheck(
1818
$this->createReflectionProvider(),
1919
new GenericObjectTypeCheck(),
20-
new VarianceCheck(),
20+
new VarianceCheck(true),
2121
true,
2222
[],
2323
),

tests/PHPStan/Rules/Generics/MethodSignatureVarianceRuleTest.php

+4
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ public function testRule(): void
173173
'Template type X is declared as contravariant, but occurs in invariant position in return type of method MethodSignatureVariance\Contravariant\C::m().',
174174
71,
175175
],
176+
[
177+
'Template type X is declared as contravariant, but occurs in covariant position in param-out type of parameter a of method MethodSignatureVariance\Contravariant\C::paramOut().',
178+
79,
179+
],
176180
]);
177181
}
178182

tests/PHPStan/Rules/Generics/UsedTraitsRuleTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ protected function getRule(): Rule
1919
new GenericAncestorsCheck(
2020
$this->createReflectionProvider(),
2121
new GenericObjectTypeCheck(),
22-
new VarianceCheck(),
22+
new VarianceCheck(true),
2323
true,
2424
[],
2525
),

tests/PHPStan/Rules/Generics/data/method-signature-variance-contravariant.php

+8
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,12 @@ function m() {}
7272

7373
/** @return X */
7474
private function n() {}
75+
76+
/**
77+
* @param-out X $a
78+
*/
79+
public function paramOut(&$a)
80+
{
81+
82+
}
7583
}

0 commit comments

Comments
 (0)