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'], []); + } + }