From d46469097be1571f6fd71e092ea5459aff3bcb45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pudil?= Date: Thu, 24 Nov 2022 14:29:25 +0100 Subject: [PATCH 1/6] resolve template-contravariant --- src/PhpDoc/PhpDocNodeResolver.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/PhpDoc/PhpDocNodeResolver.php b/src/PhpDoc/PhpDocNodeResolver.php index 4603e6aa30..bb16d18b31 100644 --- a/src/PhpDoc/PhpDocNodeResolver.php +++ b/src/PhpDoc/PhpDocNodeResolver.php @@ -264,6 +264,8 @@ public function resolveTemplateTags(PhpDocNode $phpDocNode, NameScope $nameScope $variance = TemplateTypeVariance::createInvariant(); } elseif (in_array($tagName, ['@template-covariant', '@psalm-template-covariant', '@phpstan-template-covariant'], true)) { $variance = TemplateTypeVariance::createCovariant(); + } elseif (in_array($tagName, ['@template-contravariant', '@psalm-template-contravariant', '@phpstan-template-contravariant'], true)) { + $variance = TemplateTypeVariance::createContravariant(); } else { continue; } From 4400d1648252a4f14e44c664f2cca4159535f3f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pudil?= Date: Fri, 25 Nov 2022 12:19:07 +0100 Subject: [PATCH 2/6] fix covariance x contravariance composition --- src/Type/Generic/TemplateTypeVariance.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Type/Generic/TemplateTypeVariance.php b/src/Type/Generic/TemplateTypeVariance.php index edd59b7c24..4f5fece1e9 100644 --- a/src/Type/Generic/TemplateTypeVariance.php +++ b/src/Type/Generic/TemplateTypeVariance.php @@ -85,7 +85,7 @@ public function compose(self $other): self if ($this->covariant()) { if ($other->contravariant()) { - return self::createCovariant(); + return self::createContravariant(); } if ($other->covariant()) { return self::createCovariant(); From 96940428a04902b05dcadc8ca2170817a907bf82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pudil?= Date: Mon, 5 Dec 2022 19:04:13 +0100 Subject: [PATCH 3/6] test contravariance composition --- .../Type/Generic/GenericObjectTypeTest.php | 140 ++++++++++++++++++ .../Type/Generic/data/generic-classes-d.php | 6 + 2 files changed, 146 insertions(+) diff --git a/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php b/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php index ac6c6e9ad0..3fd6096dee 100644 --- a/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php +++ b/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php @@ -405,6 +405,76 @@ public function dataGetReferencedTypeArguments(): array ), ], ], + 'param: In' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createCovariant(), + ), + ], + ], + 'param: In>' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createContravariant(), + ), + ], + ], + 'param: In>>' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createCovariant(), + ), + ], + ], + 'param: In>' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\Out::class, [ + $templateType('T'), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createCovariant(), + ), + ], + ], + 'param: Out>' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\Out::class, [ + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createCovariant(), + ), + ], + ], 'return: Invariant' => [ TemplateTypeVariance::createCovariant(), new GenericObjectType(D\Invariant::class, [ @@ -473,6 +543,76 @@ public function dataGetReferencedTypeArguments(): array ), ], ], + 'return: In' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createContravariant(), + ), + ], + ], + 'return: In>' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createCovariant(), + ), + ], + ], + 'return: In>>' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createContravariant(), + ), + ], + ], + 'return: In>' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\Out::class, [ + $templateType('T'), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createContravariant(), + ), + ], + ], + 'return: Out>' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\Out::class, [ + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createContravariant(), + ), + ], + ], ]; } diff --git a/tests/PHPStan/Type/Generic/data/generic-classes-d.php b/tests/PHPStan/Type/Generic/data/generic-classes-d.php index b25dbc6bac..69621f8e08 100644 --- a/tests/PHPStan/Type/Generic/data/generic-classes-d.php +++ b/tests/PHPStan/Type/Generic/data/generic-classes-d.php @@ -13,3 +13,9 @@ interface Out { /** @return T */ public function get(); } + +/** @template-contravariant T */ +interface In { + /** @return T */ + public function get(); +} From 898298b1d23e25f612eb7362184b0a75c0909eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pudil?= Date: Sun, 27 Nov 2022 18:06:37 +0100 Subject: [PATCH 4/6] test GenericObjectType contravariant subtyping --- .../Type/Generic/GenericObjectTypeTest.php | 15 +++++++++++++++ .../Type/Generic/data/generic-classes-c.php | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php b/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php index 3fd6096dee..a023eabf8e 100644 --- a/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php +++ b/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php @@ -97,6 +97,21 @@ public function dataIsSuperTypeOf(): array new GenericObjectType(C\Covariant::class, [new ObjectType('DateTimeInterface')]), TrinaryLogic::createMaybe(), ], + 'contravariant with equal types' => [ + new GenericObjectType(C\Contravariant::class, [new ObjectType('DateTime')]), + new GenericObjectType(C\Contravariant::class, [new ObjectType('DateTime')]), + TrinaryLogic::createYes(), + ], + 'contravariant with sub type' => [ + new GenericObjectType(C\Contravariant::class, [new ObjectType('DateTimeInterface')]), + new GenericObjectType(C\Contravariant::class, [new ObjectType('DateTime')]), + TrinaryLogic::createMaybe(), + ], + 'contravariant with super type' => [ + new GenericObjectType(C\Contravariant::class, [new ObjectType('DateTime')]), + new GenericObjectType(C\Contravariant::class, [new ObjectType('DateTimeInterface')]), + TrinaryLogic::createYes(), + ], [ new ObjectType(ReflectionClass::class), new GenericObjectType(ReflectionClass::class, [ diff --git a/tests/PHPStan/Type/Generic/data/generic-classes-c.php b/tests/PHPStan/Type/Generic/data/generic-classes-c.php index cd17cc9261..417797587b 100644 --- a/tests/PHPStan/Type/Generic/data/generic-classes-c.php +++ b/tests/PHPStan/Type/Generic/data/generic-classes-c.php @@ -9,3 +9,7 @@ interface Invariant { /** @template-covariant T */ interface Covariant { } + +/** @template-contravariant T */ +interface Contravariant { +} From 9b2de87be4ac6dffdb81e8c2f11ec207dfed7f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pudil?= Date: Sun, 27 Nov 2022 19:54:18 +0100 Subject: [PATCH 5/6] split MethodSignatureVarianceRuleTest datasets and cover template-contravariant --- .../MethodSignatureVarianceRuleTest.php | 134 ++++++++++++++++-- ...ethod-signature-variance-contravariant.php | 72 ++++++++++ .../method-signature-variance-covariant.php | 72 ++++++++++ .../method-signature-variance-invariant.php | 70 +++++++++ .../data/method-signature-variance.php | 39 ++--- 5 files changed, 351 insertions(+), 36 deletions(-) create mode 100644 tests/PHPStan/Rules/Generics/data/method-signature-variance-contravariant.php create mode 100644 tests/PHPStan/Rules/Generics/data/method-signature-variance-covariant.php create mode 100644 tests/PHPStan/Rules/Generics/data/method-signature-variance-invariant.php diff --git a/tests/PHPStan/Rules/Generics/MethodSignatureVarianceRuleTest.php b/tests/PHPStan/Rules/Generics/MethodSignatureVarianceRuleTest.php index b0b64eb766..5915e4bfd3 100644 --- a/tests/PHPStan/Rules/Generics/MethodSignatureVarianceRuleTest.php +++ b/tests/PHPStan/Rules/Generics/MethodSignatureVarianceRuleTest.php @@ -22,25 +22,141 @@ public function testRule(): void { $this->analyse([__DIR__ . '/data/method-signature-variance.php'], [ [ - 'Template type T is declared as covariant, but occurs in contravariant position in parameter a of method MethodSignatureVariance\C::a().', - 25, + 'Variance annotation is only allowed for type parameters of classes and interfaces, but occurs in template type U in in method MethodSignatureVariance\C::b().', + 16, + ], + [ + 'Variance annotation is only allowed for type parameters of classes and interfaces, but occurs in template type U in in method MethodSignatureVariance\C::c().', + 22, ], + ]); + + $this->analyse([__DIR__ . '/data/method-signature-variance-invariant.php'], []); + + $this->analyse([__DIR__ . '/data/method-signature-variance-covariant.php'], [ [ - 'Template type T is declared as covariant, but occurs in invariant position in parameter b of method MethodSignatureVariance\C::a().', - 25, + 'Template type X is declared as covariant, but occurs in contravariant position in parameter a of method MethodSignatureVariance\Covariant\C::a().', + 35, ], [ - 'Template type T is declared as covariant, but occurs in contravariant position in parameter c of method MethodSignatureVariance\C::a().', - 25, + 'Template type X is declared as covariant, but occurs in contravariant position in parameter c of method MethodSignatureVariance\Covariant\C::a().', + 35, ], [ - 'Template type W is declared as covariant, but occurs in contravariant position in parameter d of method MethodSignatureVariance\C::a().', - 25, + 'Template type X is declared as covariant, but occurs in invariant position in parameter e of method MethodSignatureVariance\Covariant\C::a().', + 35, ], [ - 'Variance annotation is only allowed for type parameters of classes and interfaces, but occurs in template type U in in method MethodSignatureVariance\C::b().', + 'Template type X is declared as covariant, but occurs in contravariant position in parameter f of method MethodSignatureVariance\Covariant\C::a().', + 35, + ], + [ + 'Template type X is declared as covariant, but occurs in contravariant position in parameter h of method MethodSignatureVariance\Covariant\C::a().', + 35, + ], + [ + 'Template type X is declared as covariant, but occurs in invariant position in parameter i of method MethodSignatureVariance\Covariant\C::a().', + 35, + ], + [ + 'Template type X is declared as covariant, but occurs in invariant position in parameter j of method MethodSignatureVariance\Covariant\C::a().', 35, ], + [ + 'Template type X is declared as covariant, but occurs in contravariant position in parameter k of method MethodSignatureVariance\Covariant\C::a().', + 35, + ], + [ + 'Template type X is declared as covariant, but occurs in contravariant position in return type of method MethodSignatureVariance\Covariant\C::c().', + 41, + ], + [ + 'Template type X is declared as covariant, but occurs in contravariant position in return type of method MethodSignatureVariance\Covariant\C::e().', + 47, + ], + [ + 'Template type X is declared as covariant, but occurs in invariant position in return type of method MethodSignatureVariance\Covariant\C::f().', + 50, + ], + [ + 'Template type X is declared as covariant, but occurs in contravariant position in return type of method MethodSignatureVariance\Covariant\C::h().', + 56, + ], + [ + 'Template type X is declared as covariant, but occurs in invariant position in return type of method MethodSignatureVariance\Covariant\C::j().', + 62, + ], + [ + 'Template type X is declared as covariant, but occurs in invariant position in return type of method MethodSignatureVariance\Covariant\C::k().', + 65, + ], + [ + 'Template type X is declared as covariant, but occurs in contravariant position in return type of method MethodSignatureVariance\Covariant\C::l().', + 68, + ], + ]); + + $this->analyse([__DIR__ . '/data/method-signature-variance-contravariant.php'], [ + [ + 'Template type X is declared as contravariant, but occurs in covariant position in parameter b of method MethodSignatureVariance\Contravariant\C::a().', + 35, + ], + [ + 'Template type X is declared as contravariant, but occurs in covariant position in parameter d of method MethodSignatureVariance\Contravariant\C::a().', + 35, + ], + [ + 'Template type X is declared as contravariant, but occurs in invariant position in parameter e of method MethodSignatureVariance\Contravariant\C::a().', + 35, + ], + [ + 'Template type X is declared as contravariant, but occurs in covariant position in parameter g of method MethodSignatureVariance\Contravariant\C::a().', + 35, + ], + [ + 'Template type X is declared as contravariant, but occurs in invariant position in parameter i of method MethodSignatureVariance\Contravariant\C::a().', + 35, + ], + [ + 'Template type X is declared as contravariant, but occurs in invariant position in parameter j of method MethodSignatureVariance\Contravariant\C::a().', + 35, + ], + [ + 'Template type X is declared as contravariant, but occurs in covariant position in parameter l of method MethodSignatureVariance\Contravariant\C::a().', + 35, + ], + [ + 'Template type X is declared as contravariant, but occurs in covariant position in return type of method MethodSignatureVariance\Contravariant\C::b().', + 38, + ], + [ + 'Template type X is declared as contravariant, but occurs in covariant position in return type of method MethodSignatureVariance\Contravariant\C::d().', + 44, + ], + [ + 'Template type X is declared as contravariant, but occurs in invariant position in return type of method MethodSignatureVariance\Contravariant\C::f().', + 50, + ], + [ + 'Template type X is declared as contravariant, but occurs in covariant position in return type of method MethodSignatureVariance\Contravariant\C::g().', + 53, + ], + [ + 'Template type X is declared as contravariant, but occurs in covariant position in return type of method MethodSignatureVariance\Contravariant\C::i().', + 59, + ], + [ + 'Template type X is declared as contravariant, but occurs in invariant position in return type of method MethodSignatureVariance\Contravariant\C::j().', + 62, + ], + [ + 'Template type X is declared as contravariant, but occurs in invariant position in return type of method MethodSignatureVariance\Contravariant\C::k().', + 65, + ], + [ + 'Template type X is declared as contravariant, but occurs in covariant position in return type of method MethodSignatureVariance\Contravariant\C::m().', + 71, + ], ]); } diff --git a/tests/PHPStan/Rules/Generics/data/method-signature-variance-contravariant.php b/tests/PHPStan/Rules/Generics/data/method-signature-variance-contravariant.php new file mode 100644 index 0000000000..8e38f07574 --- /dev/null +++ b/tests/PHPStan/Rules/Generics/data/method-signature-variance-contravariant.php @@ -0,0 +1,72 @@ + $b + * @param In> $c + * @param In> $d + * @param In> $e + * @param Out $f + * @param Out> $g + * @param Out> $h + * @param Out> $i + * @param Invariant $j + * @param Invariant> $k + * @param Invariant> $l + */ + function a($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l) {} + + /** @return X */ + function b() {} + + /** @return In */ + function c() {} + + /** @return In> */ + function d() {} + + /** @return In> */ + function e() {} + + /** @return In> */ + function f() {} + + /** @return Out */ + function g() {} + + /** @return Out> */ + function h() {} + + /** @return Out> */ + function i() {} + + /** @return Out> */ + function j() {} + + /** @return Invariant */ + function k() {} + + /** @return Invariant> */ + function l() {} + + /** @return Invariant> */ + function m() {} +} diff --git a/tests/PHPStan/Rules/Generics/data/method-signature-variance-covariant.php b/tests/PHPStan/Rules/Generics/data/method-signature-variance-covariant.php new file mode 100644 index 0000000000..bca969516a --- /dev/null +++ b/tests/PHPStan/Rules/Generics/data/method-signature-variance-covariant.php @@ -0,0 +1,72 @@ + $b + * @param In> $c + * @param In> $d + * @param In> $e + * @param Out $f + * @param Out> $g + * @param Out> $h + * @param Out> $i + * @param Invariant $j + * @param Invariant> $k + * @param Invariant> $l + */ + function a($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l) {} + + /** @return X */ + function b() {} + + /** @return In */ + function c() {} + + /** @return In> */ + function d() {} + + /** @return In> */ + function e() {} + + /** @return In> */ + function f() {} + + /** @return Out */ + function g() {} + + /** @return Out> */ + function h() {} + + /** @return Out> */ + function i() {} + + /** @return Out> */ + function j() {} + + /** @return Invariant */ + function k() {} + + /** @return Invariant> */ + function l() {} + + /** @return Invariant> */ + function m() {} +} diff --git a/tests/PHPStan/Rules/Generics/data/method-signature-variance-invariant.php b/tests/PHPStan/Rules/Generics/data/method-signature-variance-invariant.php new file mode 100644 index 0000000000..54dd3e4eb7 --- /dev/null +++ b/tests/PHPStan/Rules/Generics/data/method-signature-variance-invariant.php @@ -0,0 +1,70 @@ + $b + * @param In> $c + * @param In> $d + * @param In $e + * @param Out $f + * @param Out> $g + * @param Out> $h + * @param Out $i + * @param Invariant $j + * @param Invariant> $k + * @param Invariant> $l + * @return X + */ + function a($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l) {} + + /** @return In */ + function b() {} + + /** @return In>*/ + function c() {} + + /** @return In>*/ + function d() {} + + /** @return In */ + function e() {} + + /** @return Out */ + function f() {} + + /** @return Out>*/ + function g() {} + + /** @return Out>*/ + function h() {} + + /** @return Out */ + function i() {} + + /** @return Invariant */ + function j() {} + + /** @return Invariant>*/ + function k() {} + + /** @return Invariant>*/ + function l() {} +} diff --git a/tests/PHPStan/Rules/Generics/data/method-signature-variance.php b/tests/PHPStan/Rules/Generics/data/method-signature-variance.php index c783c69191..e7ffcbfaa2 100644 --- a/tests/PHPStan/Rules/Generics/data/method-signature-variance.php +++ b/tests/PHPStan/Rules/Generics/data/method-signature-variance.php @@ -2,37 +2,22 @@ namespace MethodSignatureVariance; -/** @template-covariant T */ -interface Out { -} - -/** @template T */ -interface Invariant { -} - -/** - * @template-covariant T - * @template-covariant W of \DateTimeInterface - */ class C { /** - * @param Out $a - * @param Invariant $b - * @param T $c - * @param W $d - * @return T + * @template U + * @return void */ - function a($a, $b, $c, $d) { - return $c; - } + function a() {} + /** * @template-covariant U - * @param Out $a - * @param Invariant $b - * @param U $c - * @return U + * @return void + */ + function b() {} + + /** + * @template-contravariant U + * @return void */ - function b($a, $b, $c) { - return $c; - } + function c() {} } From fac074a263329bb6fa9cf83cbd614a119174705f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pudil?= Date: Mon, 5 Dec 2022 21:23:27 +0100 Subject: [PATCH 6/6] Add an integration test case for generic variance --- .../Rules/Methods/CallMethodsRuleTest.php | 26 ++++++ .../Rules/Methods/data/generic-variance.php | 85 +++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 tests/PHPStan/Rules/Methods/data/generic-variance.php diff --git a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php index 9b49ad8937..826e1df3c6 100644 --- a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php @@ -2512,6 +2512,32 @@ public function testGenericsInferCollectionLevel8(): void ]); } + public function testGenericVariance(): void + { + $this->checkThisOnly = false; + $this->checkNullables = true; + $this->checkUnionTypes = true; + $this->checkExplicitMixed = false; + $this->analyse([__DIR__ . '/data/generic-variance.php'], [ + [ + 'Parameter #1 $param of method GenericVarianceCall\Foo::invariant() expects GenericVarianceCall\Invariant, GenericVarianceCall\Invariant given.', + 45, + ], + [ + 'Parameter #1 $param of method GenericVarianceCall\Foo::invariant() expects GenericVarianceCall\Invariant, GenericVarianceCall\Invariant given.', + 53, + ], + [ + 'Parameter #1 $param of method GenericVarianceCall\Foo::covariant() expects GenericVarianceCall\Covariant, GenericVarianceCall\Covariant given.', + 60, + ], + [ + 'Parameter #1 $param of method GenericVarianceCall\Foo::contravariant() expects GenericVarianceCall\Contravariant, GenericVarianceCall\Contravariant given.', + 83, + ], + ]); + } + public function testBug6904(): void { if (PHP_VERSION_ID < 80100) { diff --git a/tests/PHPStan/Rules/Methods/data/generic-variance.php b/tests/PHPStan/Rules/Methods/data/generic-variance.php new file mode 100644 index 0000000000..eb02f09430 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/generic-variance.php @@ -0,0 +1,85 @@ + $param + */ + public function invariant(Invariant $param): void + { + } + + /** + * @param Covariant $param + */ + public function covariant(Covariant $param): void + { + } + + /** + * @param Contravariant $param + */ + public function contravariant(Contravariant $param): void + { + } + + public function testInvariant(): void + { + /** @var Invariant $invariantA */ + $invariantA = new Invariant(); + $this->invariant($invariantA); + + /** @var Invariant $invariantB */ + $invariantB = new Invariant(); + $this->invariant($invariantB); + + /** @var Invariant $invariantC */ + $invariantC = new Invariant(); + $this->invariant($invariantC); + } + + public function testCovariant(): void + { + /** @var Covariant $covariantA */ + $covariantA = new Covariant(); + $this->covariant($covariantA); + + /** @var Covariant $covariantB */ + $covariantB = new Covariant(); + $this->covariant($covariantB); + + /** @var Covariant $covariantC */ + $covariantC = new Covariant(); + $this->covariant($covariantC); + } + + public function testContravariant(): void + { + /** @var Contravariant $contravariantA */ + $contravariantA = new Contravariant(); + $this->contravariant($contravariantA); + + /** @var Contravariant $contravariantB */ + $contravariantB = new Contravariant(); + $this->contravariant($contravariantB); + + /** @var Contravariant $contravariantC */ + $contravariantC = new Contravariant(); + $this->contravariant($contravariantC); + } +}