diff --git a/src/Rules/FunctionCallParametersCheck.php b/src/Rules/FunctionCallParametersCheck.php index 81eb63c140..9132cd429a 100644 --- a/src/Rules/FunctionCallParametersCheck.php +++ b/src/Rules/FunctionCallParametersCheck.php @@ -299,9 +299,9 @@ static function (Type $type): bool { } if ($this->checkMissingTypehints && $parametersAcceptor instanceof ResolvedFunctionVariant) { - $originalReturnType = $parametersAcceptor->getOriginalParametersAcceptor()->getReturnType(); + $originalParametersAcceptor = $parametersAcceptor->getOriginalParametersAcceptor(); $returnTemplateTypes = []; - TypeTraverser::map($originalReturnType, static function (Type $type, callable $traverse) use (&$returnTemplateTypes): Type { + TypeTraverser::map($originalParametersAcceptor->getReturnType(), static function (Type $type, callable $traverse) use (&$returnTemplateTypes): Type { if ($type instanceof TemplateType) { $returnTemplateTypes[$type->getName()] = true; return $type; @@ -310,6 +310,18 @@ static function (Type $type): bool { return $traverse($type); }); + $parameterTemplateTypes = []; + foreach ($originalParametersAcceptor->getParameters() as $parameter) { + TypeTraverser::map($parameter->getType(), static function (Type $type, callable $traverse) use (&$parameterTemplateTypes): Type { + if ($type instanceof TemplateType) { + $parameterTemplateTypes[$type->getName()] = true; + return $type; + } + + return $traverse($type); + }); + } + foreach ($parametersAcceptor->getResolvedTemplateTypeMap()->getTypes() as $name => $type) { if ( !($type instanceof ErrorType) @@ -325,6 +337,10 @@ static function (Type $type): bool { continue; } + if (!array_key_exists($name, $parameterTemplateTypes)) { + continue; + } + $errors[] = RuleErrorBuilder::message(sprintf($messages[9], $name))->line($funcCall->getLine())->tip('See: https://phpstan.org/blog/solving-phpstan-error-unable-to-resolve-template-type')->build(); } } diff --git a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php index 5dcb071834..0ed220b821 100644 --- a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php @@ -1758,6 +1758,10 @@ public function testOnlyRelevantUnableToResolveTemplateType(): void $this->checkNullables = true; $this->checkUnionTypes = true; $this->analyse([__DIR__ . '/data/only-relevant-unable-to-resolve-template-type.php'], [ + [ + 'Parameter #1 $a of method OnlyRelevantUnableToResolve\Foo::doBaz() expects array, int given.', + 41, + ], [ 'Unable to resolve the template type T in call to method OnlyRelevantUnableToResolve\Foo::doBaz()', 41, diff --git a/tests/PHPStan/Rules/Methods/data/only-relevant-unable-to-resolve-template-type.php b/tests/PHPStan/Rules/Methods/data/only-relevant-unable-to-resolve-template-type.php index 636ebedbb3..2b9b0dadfc 100644 --- a/tests/PHPStan/Rules/Methods/data/only-relevant-unable-to-resolve-template-type.php +++ b/tests/PHPStan/Rules/Methods/data/only-relevant-unable-to-resolve-template-type.php @@ -27,18 +27,33 @@ public function doBar() /** * @template T * @template U + * @param T[] $a * @return T */ - public function doBaz() + public function doBaz($a) { - } public function doLorem() { $this->doFoo(1); $this->doBar(); - $this->doBaz(); + $this->doBaz(1); + } + + /** + * @template T + * @param mixed $a + * @return T + */ + public function doIpsum($a) + { + + } + + public function doDolor() + { + $this->doIpsum(1); } }