diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php index e0529936f97..1795639cc35 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php @@ -437,12 +437,6 @@ protected static function checkFunctionArguments( && $param && $param->type && !$arg->value->getDocComment() - && !array_filter( - $arg->value->params, - function (PhpParser\Node\Param $closure_param) : bool { - return !!$closure_param->type; - } - ) ) { if (count($args) === 2 && (($argument_offset === 1 && $method_id === 'array_filter') @@ -497,6 +491,22 @@ function (PhpParser\Node\Param $closure_param) : bool { if (isset($replaced_type_part->params[$closure_param_offset]->type) && !$replaced_type_part->params[$closure_param_offset]->type->hasTemplate() ) { + if ($param_storage->type) { + if ($param_storage->type !== $param_storage->signature_type) { + continue; + } + + $type_match_found = TypeAnalyzer::isContainedBy( + $codebase, + $replaced_type_part->params[$closure_param_offset]->type, + $param_storage->type + ); + + if (!$type_match_found) { + continue; + } + } + $param_storage->type = $replaced_type_part->params[$closure_param_offset]->type; } } diff --git a/tests/CallableTest.php b/tests/CallableTest.php index ffebef0ce9f..af6928f934a 100644 --- a/tests/CallableTest.php +++ b/tests/CallableTest.php @@ -933,6 +933,30 @@ function foo(string $key) : void { if (is_callable($setter)) {} }' ], + 'refineCallableTypeWithTypehint' => [ + ' [ + '