Skip to content

Commit

Permalink
Bleeding edge - check template type variance in @param-out
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Feb 20, 2023
1 parent e679f11 commit 7ceb19d
Show file tree
Hide file tree
Showing 11 changed files with 51 additions and 10 deletions.
1 change: 1 addition & 0 deletions conf/bleedingEdge.neon
Expand Up @@ -31,3 +31,4 @@ parameters:
closureDefaultParameterTypeRule: true
newRuleLevelHelper: true
instanceofType: true
paramOutVariance: true
4 changes: 4 additions & 0 deletions conf/config.neon
Expand Up @@ -61,6 +61,7 @@ parameters:
closureDefaultParameterTypeRule: false
newRuleLevelHelper: false
instanceofType: false
paramOutVariance: false
fileExtensions:
- php
checkAdvancedIsset: false
Expand Down Expand Up @@ -289,6 +290,7 @@ parametersSchema:
closureDefaultParameterTypeRule: bool()
newRuleLevelHelper: bool()
instanceofType: bool()
paramOutVariance: bool()
])
fileExtensions: listOf(string())
checkAdvancedIsset: bool()
Expand Down Expand Up @@ -1031,6 +1033,8 @@ services:

-
class: PHPStan\Rules\Generics\VarianceCheck
arguments:
checkParamOutVariance: %featureToggles.paramOutVariance%

-
class: PHPStan\Rules\IssetCheck
Expand Down
1 change: 1 addition & 0 deletions src/Rules/Generics/FunctionSignatureVarianceRule.php
Expand Up @@ -33,6 +33,7 @@ public function processNode(Node $node, Scope $scope): array
return $this->varianceCheck->checkParametersAcceptor(
ParametersAcceptorSelector::selectSingle($functionReflection->getVariants()),
sprintf('in parameter %%s of function %s()', SprintfHelper::escapeFormatString($functionName)),
sprintf('in param-out type of parameter %%s of function %s()', SprintfHelper::escapeFormatString($functionName)),
sprintf('in return type of function %s()', $functionName),
sprintf('in function %s()', $functionName),
false,
Expand Down
1 change: 1 addition & 0 deletions src/Rules/Generics/MethodSignatureVarianceRule.php
Expand Up @@ -32,6 +32,7 @@ public function processNode(Node $node, Scope $scope): array
return $this->varianceCheck->checkParametersAcceptor(
ParametersAcceptorSelector::selectSingle($method->getVariants()),
sprintf('in parameter %%s of method %s::%s()', SprintfHelper::escapeFormatString($method->getDeclaringClass()->getDisplayName()), SprintfHelper::escapeFormatString($method->getName())),
sprintf('in param-out type of parameter %%s of method %s::%s()', SprintfHelper::escapeFormatString($method->getDeclaringClass()->getDisplayName()), SprintfHelper::escapeFormatString($method->getName())),
sprintf('in return type of method %s::%s()', $method->getDeclaringClass()->getDisplayName(), $method->getName()),
sprintf('in method %s::%s()', $method->getDeclaringClass()->getDisplayName(), $method->getName()),
$method->getName() === '__construct' || $method->isStatic(),
Expand Down
34 changes: 28 additions & 6 deletions src/Rules/Generics/VarianceCheck.php
Expand Up @@ -13,10 +13,17 @@
class VarianceCheck
{

public function __construct(
private bool $checkParamOutVariance,
)
{
}

/** @return RuleError[] */
public function checkParametersAcceptor(
ParametersAcceptorWithPhpDocs $parametersAcceptor,
string $parameterTypeMessage,
string $parameterOutTypeMessage,
string $returnTypeMessage,
string $generalMessage,
bool $isStatic,
Expand Down Expand Up @@ -44,20 +51,35 @@ public function checkParametersAcceptor(
return $errors;
}

$covariant = TemplateTypeVariance::createCovariant();
$parameterVariance = $isStatic
? TemplateTypeVariance::createStatic()
: TemplateTypeVariance::createContravariant();

foreach ($parametersAcceptor->getParameters() as $parameterReflection) {
$variance = $isStatic
? TemplateTypeVariance::createStatic()
: TemplateTypeVariance::createContravariant();
$type = $parameterReflection->getType();
$message = sprintf($parameterTypeMessage, $parameterReflection->getName());
foreach ($this->check($variance, $type, $message) as $error) {
foreach ($this->check($parameterVariance, $type, $message) as $error) {
$errors[] = $error;
}

if (!$this->checkParamOutVariance) {
continue;
}

$paramOutType = $parameterReflection->getOutType();
if ($paramOutType === null) {
continue;
}

$outMessage = sprintf($parameterOutTypeMessage, $parameterReflection->getName());
foreach ($this->check($covariant, $paramOutType, $outMessage) as $error) {
$errors[] = $error;
}
}

$variance = TemplateTypeVariance::createCovariant();
$type = $parametersAcceptor->getReturnType();
foreach ($this->check($variance, $type, $returnTypeMessage) as $error) {
foreach ($this->check($covariant, $type, $returnTypeMessage) as $error) {
$errors[] = $error;
}

Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Generics/ClassAncestorsRuleTest.php
Expand Up @@ -17,7 +17,7 @@ protected function getRule(): Rule
new GenericAncestorsCheck(
$this->createReflectionProvider(),
new GenericObjectTypeCheck(),
new VarianceCheck(),
new VarianceCheck(true),
true,
[],
),
Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Generics/EnumAncestorsRuleTest.php
Expand Up @@ -18,7 +18,7 @@ protected function getRule(): Rule
new GenericAncestorsCheck(
$this->createReflectionProvider(),
new GenericObjectTypeCheck(),
new VarianceCheck(),
new VarianceCheck(true),
true,
[],
),
Expand Down
Expand Up @@ -17,7 +17,7 @@ protected function getRule(): Rule
new GenericAncestorsCheck(
$this->createReflectionProvider(),
new GenericObjectTypeCheck(),
new VarianceCheck(),
new VarianceCheck(true),
true,
[],
),
Expand Down
Expand Up @@ -173,6 +173,10 @@ public function testRule(): void
'Template type X is declared as contravariant, but occurs in invariant position in return type of method MethodSignatureVariance\Contravariant\C::m().',
71,
],
[
'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().',
79,
],
]);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Rules/Generics/UsedTraitsRuleTest.php
Expand Up @@ -19,7 +19,7 @@ protected function getRule(): Rule
new GenericAncestorsCheck(
$this->createReflectionProvider(),
new GenericObjectTypeCheck(),
new VarianceCheck(),
new VarianceCheck(true),
true,
[],
),
Expand Down
Expand Up @@ -72,4 +72,12 @@ function m() {}

/** @return X */
private function n() {}

/**
* @param-out X $a
*/
public function paramOut(&$a)
{

}
}

0 comments on commit 7ceb19d

Please sign in to comment.