From 9789b536172327713ea096eaa26cee919282c8f9 Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Sun, 29 Nov 2020 21:36:50 -0500 Subject: [PATCH] Fix #4731 - expand out class-bound generic types when evaluating instance method --- .../Expression/Call/ArgumentsAnalyzer.php | 3 +- .../Statements/Expression/CallAnalyzer.php | 12 +++++-- tests/Template/ClassTemplateTest.php | 31 +++++++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php index b093db507..29caef5b1 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php @@ -570,7 +570,8 @@ class ArgumentsAnalyzer $class_storage, $self_fq_class_name, $calling_class_storage, - $function_storage->template_types ?: [] + $function_storage->template_types ?: [], + $class_generic_params ); if ($template_types) { diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php index b4476e5f7..0b030aa97 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php @@ -341,15 +341,20 @@ class CallAnalyzer } /** + * This gets all the template params (and their types) that we think + * we'll need to know about + * * @return array> * @param array> $existing_template_types + * @param array> $class_template_params */ public static function getTemplateTypesForCall( \Psalm\Codebase $codebase, ?ClassLikeStorage $declaring_class_storage, ?string $appearing_class_name, ?ClassLikeStorage $calling_class_storage, - array $existing_template_types = [] + array $existing_template_types = [], + array $class_template_params = [] ) : array { $template_types = $existing_template_types; @@ -369,7 +374,7 @@ class CallAnalyzer $atomic_type->defining_class, $atomic_type->param_name, $calling_class_storage->template_extended_params, - $template_types + $class_template_params + $template_types ); } else { $output_type_candidate = new Type\Union([$atomic_type]); @@ -392,7 +397,8 @@ class CallAnalyzer } elseif ($declaring_class_storage->template_types) { foreach ($declaring_class_storage->template_types as $template_name => $type_map) { foreach ($type_map as $key => $type) { - $template_types[$template_name][$key] = $type; + $template_types[$template_name][$key] + = $class_template_params[$template_name][$key] ?? $type; } } } diff --git a/tests/Template/ClassTemplateTest.php b/tests/Template/ClassTemplateTest.php index 492c917ad..74e7d51a1 100644 --- a/tests/Template/ClassTemplateTest.php +++ b/tests/Template/ClassTemplateTest.php @@ -3770,6 +3770,37 @@ class ClassTemplateTest extends TestCase }', 'error_message' => 'InvalidArgument', ], + 'bindRedirectedTemplate' => [ + ' + * @param TIn2 $in + * @return list + */ + public function __invoke(array $in) : array { + return array_map( + $this->c, + $in + ); + } + } + + $m = new Map(fn(int $num) => (string) $num); + $m(["a"]);', + 'error_message' => 'InvalidScalarArgument', + [], + false, + '8.0' + ], ]; } }