From 48aea56c5ed8fda80caf3d1dbaa8b9dd2c8fd301 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 8 Mar 2021 06:25:18 +0100 Subject: [PATCH] Nested generic type - correctly infer template types --- src/Type/Generic/TemplateTypeTrait.php | 7 +++-- .../Analyser/NodeScopeResolverTest.php | 6 ++++ tests/PHPStan/Analyser/data/bug-4642.php | 28 +++++++++++++++++++ .../data/nested-generic-types-unwrapping.php | 4 +-- .../Rules/Methods/CallMethodsRuleTest.php | 8 ++++++ 5 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 tests/PHPStan/Analyser/data/bug-4642.php diff --git a/src/Type/Generic/TemplateTypeTrait.php b/src/Type/Generic/TemplateTypeTrait.php index 9a7e857777..664a28805b 100644 --- a/src/Type/Generic/TemplateTypeTrait.php +++ b/src/Type/Generic/TemplateTypeTrait.php @@ -158,14 +158,15 @@ public function inferTemplateTypes(Type $receivedType): TemplateTypeMap ]); } - $resolvedBound = TemplateTypeHelper::resolveToBounds($this->getBound()); + $map = $this->getBound()->inferTemplateTypes($receivedType); + $resolvedBound = TemplateTypeHelper::resolveTemplateTypes($this->getBound(), $map); if ($resolvedBound->isSuperTypeOf($receivedType)->yes()) { return (new TemplateTypeMap([ $this->name => $this->shouldGeneralizeInferredType() ? TemplateTypeHelper::generalizeType($receivedType) : $receivedType, - ]))->union($this->getBound()->inferTemplateTypes($receivedType)); + ]))->union($map); } - return $this->getBound()->inferTemplateTypes($receivedType); + return $map; } public function getReferencedTemplateTypes(TemplateTypeVariance $positionVariance): array diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index a7e733a0fe..258190aab0 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -10961,6 +10961,11 @@ public function dataIteratorIterator(): array return $this->gatherAssertTypes(__DIR__ . '/data/iterator-iterator.php'); } + public function dataBug4642(): array + { + return $this->gatherAssertTypes(__DIR__ . '/data/bug-4642.php'); + } + /** * @param string $file * @return array @@ -11214,6 +11219,7 @@ private function gatherAssertTypes(string $file): array * @dataProvider dataNestedGenericTypesUnwrapping * @dataProvider dataNestedGenericIncompleteConstructor * @dataProvider dataIteratorIterator + * @dataProvider dataBug4642 * @param string $assertType * @param string $file * @param mixed ...$args diff --git a/tests/PHPStan/Analyser/data/bug-4642.php b/tests/PHPStan/Analyser/data/bug-4642.php new file mode 100644 index 0000000000..7783fe4d59 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-4642.php @@ -0,0 +1,28 @@ + + * @phpstan-param class-string $className + * @phpstan-return T + */ + function getRepository(string $className): IRepository; +} + +class User implements IEntity {} +/** @implements IRepository */ +class UsersRepository implements IRepository {} + +function (I $model): void { + assertType(UsersRepository::class, $model->getRepository(UsersRepository::class)); +}; diff --git a/tests/PHPStan/Analyser/data/nested-generic-types-unwrapping.php b/tests/PHPStan/Analyser/data/nested-generic-types-unwrapping.php index 3544e3cbf1..346448bd6f 100644 --- a/tests/PHPStan/Analyser/data/nested-generic-types-unwrapping.php +++ b/tests/PHPStan/Analyser/data/nested-generic-types-unwrapping.php @@ -103,7 +103,7 @@ function (): void { function (): void { $result = loadWithIndirectUnwrap2(SomePackage::class); - assertType('NestedGenericTypesUnwrapping\GenericPackage', $result); + assertType(SomePackage::class, $result); }; function (SomePackage $somePackage): void { @@ -111,5 +111,5 @@ function (SomePackage $somePackage): void { assertType(SomeInnerPackage::class, $result); $result = unwrapGeneric2($somePackage); - assertType('NestedGenericTypesUnwrapping\GenericPackage', $result); + assertType(SomePackage::class, $result); }; diff --git a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php index 4118533763..ae12a3a262 100644 --- a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php @@ -1883,4 +1883,12 @@ public function testBug3922(): void ]); } + public function testBug4642(): void + { + $this->checkThisOnly = false; + $this->checkNullables = true; + $this->checkUnionTypes = true; + $this->analyse([__DIR__ . '/../../Analyser/data/bug-4642.php'], []); + } + }