From ca86a9cb405507eeccb0f9059b6639a60171f5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pudil?= Date: Sun, 27 Nov 2022 20:48:16 +0100 Subject: [PATCH 1/3] Fix invariance composition --- src/Type/Generic/TemplateTypeVariance.php | 4 + .../MethodSignatureVarianceRuleTest.php | 24 ++- .../Type/Generic/GenericObjectTypeTest.php | 190 ++++++++++++++++-- 3 files changed, 200 insertions(+), 18 deletions(-) diff --git a/src/Type/Generic/TemplateTypeVariance.php b/src/Type/Generic/TemplateTypeVariance.php index 4f5fece1e9..38488c7593 100644 --- a/src/Type/Generic/TemplateTypeVariance.php +++ b/src/Type/Generic/TemplateTypeVariance.php @@ -93,6 +93,10 @@ public function compose(self $other): self return self::createInvariant(); } + if ($this->invariant()) { + return self::createInvariant(); + } + return $other; } diff --git a/tests/PHPStan/Rules/Generics/MethodSignatureVarianceRuleTest.php b/tests/PHPStan/Rules/Generics/MethodSignatureVarianceRuleTest.php index 5915e4bfd3..07d3c237b8 100644 --- a/tests/PHPStan/Rules/Generics/MethodSignatureVarianceRuleTest.php +++ b/tests/PHPStan/Rules/Generics/MethodSignatureVarianceRuleTest.php @@ -63,7 +63,11 @@ public function testRule(): void 35, ], [ - 'Template type X is declared as covariant, but occurs in contravariant position in parameter k of method MethodSignatureVariance\Covariant\C::a().', + 'Template type X is declared as covariant, but occurs in invariant position in parameter k of method MethodSignatureVariance\Covariant\C::a().', + 35, + ], + [ + 'Template type X is declared as covariant, but occurs in invariant position in parameter l of method MethodSignatureVariance\Covariant\C::a().', 35, ], [ @@ -91,9 +95,13 @@ public function testRule(): void 65, ], [ - 'Template type X is declared as covariant, but occurs in contravariant position in return type of method MethodSignatureVariance\Covariant\C::l().', + 'Template type X is declared as covariant, but occurs in invariant position in return type of method MethodSignatureVariance\Covariant\C::l().', 68, ], + [ + 'Template type X is declared as covariant, but occurs in invariant position in return type of method MethodSignatureVariance\Covariant\C::m().', + 71, + ], ]); $this->analyse([__DIR__ . '/data/method-signature-variance-contravariant.php'], [ @@ -122,7 +130,11 @@ public function testRule(): void 35, ], [ - 'Template type X is declared as contravariant, but occurs in covariant position in parameter l of method MethodSignatureVariance\Contravariant\C::a().', + 'Template type X is declared as contravariant, but occurs in invariant position in parameter k of method MethodSignatureVariance\Contravariant\C::a().', + 35, + ], + [ + 'Template type X is declared as contravariant, but occurs in invariant position in parameter l of method MethodSignatureVariance\Contravariant\C::a().', 35, ], [ @@ -154,7 +166,11 @@ public function testRule(): void 65, ], [ - 'Template type X is declared as contravariant, but occurs in covariant position in return type of method MethodSignatureVariance\Contravariant\C::m().', + 'Template type X is declared as contravariant, but occurs in invariant position in return type of method MethodSignatureVariance\Contravariant\C::l().', + 68, + ], + [ + 'Template type X is declared as contravariant, but occurs in invariant position in return type of method MethodSignatureVariance\Contravariant\C::m().', 71, ], ]); diff --git a/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php b/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php index a023eabf8e..67ccd18d20 100644 --- a/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php +++ b/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php @@ -490,6 +490,94 @@ public function dataGetReferencedTypeArguments(): array ), ], ], + 'param: Out>' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\Out::class, [ + new GenericObjectType(D\Invariant::class, [ + $templateType('T'), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'param: In>' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\Invariant::class, [ + $templateType('T'), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'param: Invariant>' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\Out::class, [ + $templateType('T'), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'param: Invariant>' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'param: In>>' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\Out::class, [ + $templateType('T'), + ]), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'param: Out>>' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\Out::class, [ + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], 'return: Invariant' => [ TemplateTypeVariance::createCovariant(), new GenericObjectType(D\Invariant::class, [ @@ -544,20 +632,6 @@ public function dataGetReferencedTypeArguments(): array ), ], ], - 'return: Out>' => [ - TemplateTypeVariance::createCovariant(), - new GenericObjectType(D\Out::class, [ - new GenericObjectType(D\Invariant::class, [ - $templateType('T'), - ]), - ]), - [ - new TemplateTypeReference( - $templateType('T'), - TemplateTypeVariance::createInvariant(), - ), - ], - ], 'return: In' => [ TemplateTypeVariance::createCovariant(), new GenericObjectType(D\In::class, [ @@ -628,6 +702,94 @@ public function dataGetReferencedTypeArguments(): array ), ], ], + 'return: Out>' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\Out::class, [ + new GenericObjectType(D\Invariant::class, [ + $templateType('T'), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'return: In>' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\Invariant::class, [ + $templateType('T'), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'return: Invariant>' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\Out::class, [ + $templateType('T'), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'return: Invariant>' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'return: In>>' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\Out::class, [ + $templateType('T'), + ]), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'return: Out>>' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\Out::class, [ + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + ]), + ]), + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], ]; } From 8a585aebdaca5353f8eccfa18eb370d12530acc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pudil?= Date: Mon, 5 Dec 2022 22:10:39 +0100 Subject: [PATCH 2/3] Fix GenericAncestorsCheck variance and add tests --- src/Rules/Generics/GenericAncestorsCheck.php | 2 +- .../Rules/Generics/ClassAncestorsRuleTest.php | 10 +++++++++- .../Generics/data/class-ancestors-extends.php | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Rules/Generics/GenericAncestorsCheck.php b/src/Rules/Generics/GenericAncestorsCheck.php index 4210bf7e29..7660f86cb2 100644 --- a/src/Rules/Generics/GenericAncestorsCheck.php +++ b/src/Rules/Generics/GenericAncestorsCheck.php @@ -98,7 +98,7 @@ public function check( $messages[] = RuleErrorBuilder::message(sprintf($invalidTypeMessage, $referencedClass))->build(); } - $variance = TemplateTypeVariance::createInvariant(); + $variance = TemplateTypeVariance::createStatic(); $messageContext = sprintf( $invalidVarianceMessage, $ancestorType->describe(VerbosityLevel::typeOnly()), diff --git a/tests/PHPStan/Rules/Generics/ClassAncestorsRuleTest.php b/tests/PHPStan/Rules/Generics/ClassAncestorsRuleTest.php index fda1c25d6f..d0d53a3a9f 100644 --- a/tests/PHPStan/Rules/Generics/ClassAncestorsRuleTest.php +++ b/tests/PHPStan/Rules/Generics/ClassAncestorsRuleTest.php @@ -95,9 +95,17 @@ public function testRuleExtends(): void 'Template type T is declared as covariant, but occurs in invariant position in extended type ClassAncestorsExtends\FooGeneric8 of class ClassAncestorsExtends\FooGeneric9.', 192, ], + [ + 'Template type T is declared as contravariant, but occurs in covariant position in extended type ClassAncestorsExtends\FooGeneric8 of class ClassAncestorsExtends\FooGeneric10.', + 201, + ], + [ + 'Template type T is declared as contravariant, but occurs in invariant position in extended type ClassAncestorsExtends\FooGeneric8 of class ClassAncestorsExtends\FooGeneric10.', + 201, + ], [ 'Class ClassAncestorsExtends\FilterIteratorChild extends generic class FilterIterator but does not specify its types: TKey, TValue, TIterator', - 197, + 215, 'You can turn this off by setting checkGenericClassInNonGenericObjectType: false in your %configurationFile%.', ], ]); diff --git a/tests/PHPStan/Rules/Generics/data/class-ancestors-extends.php b/tests/PHPStan/Rules/Generics/data/class-ancestors-extends.php index caa906001a..b8f99af6a5 100644 --- a/tests/PHPStan/Rules/Generics/data/class-ancestors-extends.php +++ b/tests/PHPStan/Rules/Generics/data/class-ancestors-extends.php @@ -194,6 +194,24 @@ class FooGeneric9 extends FooGeneric8 } +/** + * @template-contravariant T + * @extends FooGeneric8 + */ +class FooGeneric10 extends FooGeneric8 +{ + +} + +/** + * @template T + * @extends FooGeneric8 + */ +class FooGeneric11 extends FooGeneric8 +{ + +} + class FilterIteratorChild extends \FilterIterator { From c1fe347e8c7049ec21dfda2bbb37759e64845a29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pudil?= Date: Tue, 6 Dec 2022 16:54:21 +0100 Subject: [PATCH 3/3] Hide fixed invariance composition behind a bleeding-edge toggle --- conf/bleedingEdge.neon | 1 + conf/config.neon | 2 + src/DependencyInjection/ContainerFactory.php | 2 + src/Type/Generic/TemplateTypeVariance.php | 9 +- .../Type/Generic/GenericObjectTypeTest.php | 238 +++++++++++++++++- 5 files changed, 242 insertions(+), 10 deletions(-) diff --git a/conf/bleedingEdge.neon b/conf/bleedingEdge.neon index d756ae0039..9481e3f159 100644 --- a/conf/bleedingEdge.neon +++ b/conf/bleedingEdge.neon @@ -24,3 +24,4 @@ parameters: nullContextForVoidReturningFunctions: true unescapeStrings: true duplicateStubs: true + invarianceComposition: true diff --git a/conf/config.neon b/conf/config.neon index 56b7965763..46736e4c5a 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -54,6 +54,7 @@ parameters: nullContextForVoidReturningFunctions: false unescapeStrings: false duplicateStubs: false + invarianceComposition: false fileExtensions: - php checkAdvancedIsset: false @@ -271,6 +272,7 @@ parametersSchema: nullContextForVoidReturningFunctions: bool() unescapeStrings: bool() duplicateStubs: bool() + invarianceComposition: bool() ]) fileExtensions: listOf(string()) checkAdvancedIsset: bool() diff --git a/src/DependencyInjection/ContainerFactory.php b/src/DependencyInjection/ContainerFactory.php index 573949f02b..37f2bf7bb7 100644 --- a/src/DependencyInjection/ContainerFactory.php +++ b/src/DependencyInjection/ContainerFactory.php @@ -21,6 +21,7 @@ use PHPStan\Reflection\ReflectionProvider; use PHPStan\Reflection\ReflectionProviderStaticAccessor; use PHPStan\Type\Accessory\AccessoryArrayListType; +use PHPStan\Type\Generic\TemplateTypeVariance; use Symfony\Component\Finder\Finder; use function array_diff_key; use function array_map; @@ -174,6 +175,7 @@ public static function postInitializeContainer(Container $container): void BleedingEdgeToggle::setBleedingEdge($container->getParameter('featureToggles')['bleedingEdge']); AccessoryArrayListType::setListTypeEnabled($container->getParameter('featureToggles')['listType']); + TemplateTypeVariance::setInvarianceCompositionEnabled($container->getParameter('featureToggles')['invarianceComposition']); } public function clearOldContainers(string $tempDirectory): void diff --git a/src/Type/Generic/TemplateTypeVariance.php b/src/Type/Generic/TemplateTypeVariance.php index 38488c7593..1de2d9b1dd 100644 --- a/src/Type/Generic/TemplateTypeVariance.php +++ b/src/Type/Generic/TemplateTypeVariance.php @@ -21,6 +21,8 @@ class TemplateTypeVariance /** @var self[] */ private static array $registry; + private static bool $invarianceCompositionEnabled = false; + private function __construct(private int $value) { } @@ -93,7 +95,7 @@ public function compose(self $other): self return self::createInvariant(); } - if ($this->invariant()) { + if (self::$invarianceCompositionEnabled && $this->invariant()) { return self::createInvariant(); } @@ -177,4 +179,9 @@ public static function __set_state(array $properties): self return new self($properties['value']); } + public static function setInvarianceCompositionEnabled(bool $enabled): void + { + self::$invarianceCompositionEnabled = $enabled; + } + } diff --git a/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php b/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php index 67ccd18d20..75c9c0d7fe 100644 --- a/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php +++ b/tests/PHPStan/Type/Generic/GenericObjectTypeTest.php @@ -355,7 +355,7 @@ public function testResolveTemplateTypes(Type $received, Type $template, array $ ); } - /** @return array}> */ + /** @return array}> */ public function dataGetReferencedTypeArguments(): array { $templateType = static fn (string $name, ?Type $bound = null): TemplateType => TemplateTypeFactory::create( @@ -371,6 +371,7 @@ public function dataGetReferencedTypeArguments(): array new GenericObjectType(D\Invariant::class, [ $templateType('T'), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -383,6 +384,7 @@ public function dataGetReferencedTypeArguments(): array new GenericObjectType(D\Out::class, [ $templateType('T'), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -397,6 +399,7 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -413,6 +416,7 @@ public function dataGetReferencedTypeArguments(): array ]), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -425,6 +429,7 @@ public function dataGetReferencedTypeArguments(): array new GenericObjectType(D\In::class, [ $templateType('T'), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -439,6 +444,7 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -455,6 +461,7 @@ public function dataGetReferencedTypeArguments(): array ]), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -469,6 +476,7 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -483,6 +491,7 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -497,6 +506,7 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -511,6 +521,7 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -525,10 +536,11 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), - TemplateTypeVariance::createInvariant(), + TemplateTypeVariance::createCovariant(), ), ], ], @@ -539,10 +551,11 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), - TemplateTypeVariance::createInvariant(), + TemplateTypeVariance::createContravariant(), ), ], ], @@ -555,10 +568,11 @@ public function dataGetReferencedTypeArguments(): array ]), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), - TemplateTypeVariance::createInvariant(), + TemplateTypeVariance::createCovariant(), ), ], ], @@ -571,10 +585,11 @@ public function dataGetReferencedTypeArguments(): array ]), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), - TemplateTypeVariance::createInvariant(), + TemplateTypeVariance::createContravariant(), ), ], ], @@ -583,6 +598,7 @@ public function dataGetReferencedTypeArguments(): array new GenericObjectType(D\Invariant::class, [ $templateType('T'), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -595,6 +611,7 @@ public function dataGetReferencedTypeArguments(): array new GenericObjectType(D\Out::class, [ $templateType('T'), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -609,6 +626,7 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -625,6 +643,7 @@ public function dataGetReferencedTypeArguments(): array ]), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -637,6 +656,7 @@ public function dataGetReferencedTypeArguments(): array new GenericObjectType(D\In::class, [ $templateType('T'), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -651,6 +671,7 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -667,6 +688,7 @@ public function dataGetReferencedTypeArguments(): array ]), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -681,6 +703,7 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -695,6 +718,7 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -709,6 +733,7 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -723,6 +748,7 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), @@ -737,10 +763,11 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), - TemplateTypeVariance::createInvariant(), + TemplateTypeVariance::createCovariant(), ), ], ], @@ -751,10 +778,11 @@ public function dataGetReferencedTypeArguments(): array $templateType('T'), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), - TemplateTypeVariance::createInvariant(), + TemplateTypeVariance::createContravariant(), ), ], ], @@ -767,10 +795,11 @@ public function dataGetReferencedTypeArguments(): array ]), ]), ]), + false, [ new TemplateTypeReference( $templateType('T'), - TemplateTypeVariance::createInvariant(), + TemplateTypeVariance::createCovariant(), ), ], ], @@ -783,6 +812,195 @@ public function dataGetReferencedTypeArguments(): array ]), ]), ]), + false, + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createContravariant(), + ), + ], + ], + 'param: Out> (with invariance composition)' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\Out::class, [ + new GenericObjectType(D\Invariant::class, [ + $templateType('T'), + ]), + ]), + true, + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'param: In> (with invariance composition)' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\Invariant::class, [ + $templateType('T'), + ]), + ]), + true, + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'param: Invariant> (with invariance composition)' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\Out::class, [ + $templateType('T'), + ]), + ]), + true, + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'param: Invariant> (with invariance composition)' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + ]), + true, + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'param: In>> (with invariance composition)' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\Out::class, [ + $templateType('T'), + ]), + ]), + ]), + true, + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'param: Out>> (with invariance composition)' => [ + TemplateTypeVariance::createContravariant(), + new GenericObjectType(D\Out::class, [ + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + ]), + ]), + true, + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'return: Out> (with invariance composition)' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\Out::class, [ + new GenericObjectType(D\Invariant::class, [ + $templateType('T'), + ]), + ]), + true, + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'return: In> (with invariance composition)' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\Invariant::class, [ + $templateType('T'), + ]), + ]), + true, + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'return: Invariant> (with invariance composition)' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\Out::class, [ + $templateType('T'), + ]), + ]), + true, + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'return: Invariant> (with invariance composition)' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + ]), + true, + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'return: In>> (with invariance composition)' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\In::class, [ + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\Out::class, [ + $templateType('T'), + ]), + ]), + ]), + true, + [ + new TemplateTypeReference( + $templateType('T'), + TemplateTypeVariance::createInvariant(), + ), + ], + ], + 'return: Out>> (with invariance composition)' => [ + TemplateTypeVariance::createCovariant(), + new GenericObjectType(D\Out::class, [ + new GenericObjectType(D\Invariant::class, [ + new GenericObjectType(D\In::class, [ + $templateType('T'), + ]), + ]), + ]), + true, [ new TemplateTypeReference( $templateType('T'), @@ -798,8 +1016,10 @@ public function dataGetReferencedTypeArguments(): array * * @param array $expectedReferences */ - public function testGetReferencedTypeArguments(TemplateTypeVariance $positionVariance, Type $type, array $expectedReferences): void + public function testGetReferencedTypeArguments(TemplateTypeVariance $positionVariance, Type $type, bool $invarianceComposition, array $expectedReferences): void { + TemplateTypeVariance::setInvarianceCompositionEnabled($invarianceComposition); + $result = []; foreach ($type->getReferencedTemplateTypes($positionVariance) as $r) { $result[] = $r;