From b19dd25881b00842ba243e3d41d517420a8e8332 Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Sun, 6 Jan 2019 12:16:09 -0500 Subject: [PATCH] Fix #742 - infer template type from closure --- .../Statements/Expression/CallAnalyzer.php | 14 ++++- src/Psalm/Type/Atomic.php | 2 +- src/Psalm/Type/Atomic/CallableTrait.php | 5 +- src/Psalm/Type/Atomic/GenericTrait.php | 2 +- src/Psalm/Type/Union.php | 8 ++- tests/TemplateTest.php | 57 +++++++++++++++++++ 6 files changed, 79 insertions(+), 9 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php index 42399957e..bbcb5fe72 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php @@ -834,9 +834,17 @@ class CallAnalyzer if ($class_storage && $class_storage->template_types) { $template_types = array_merge($template_types, $class_storage->template_types); } + + foreach ($template_types as $key => $type) { + $template_types[$key] = clone $type; + } } - $existing_generic_params_to_strings = $generic_params ?: []; + $existing_generic_params = $generic_params ?: []; + + foreach ($existing_generic_params as $key => $type) { + $existing_generic_params[$key] = clone $type; + } foreach ($args as $argument_offset => $arg) { $function_param = count($function_params) > $argument_offset @@ -952,11 +960,11 @@ class CallAnalyzer } if ($function_storage) { - if ($existing_generic_params_to_strings) { + if ($existing_generic_params) { $empty_generic_params = []; $param_type->replaceTemplateTypesWithStandins( - $existing_generic_params_to_strings, + $existing_generic_params, $empty_generic_params, $codebase, $arg->value->inferredType diff --git a/src/Psalm/Type/Atomic.php b/src/Psalm/Type/Atomic.php index 6d817b74e..b1e78bf1f 100644 --- a/src/Psalm/Type/Atomic.php +++ b/src/Psalm/Type/Atomic.php @@ -517,7 +517,7 @@ abstract class Atomic * @return void */ public function replaceTemplateTypesWithStandins( - array $template_types, + array &$template_types, array &$generic_params, Codebase $codebase = null, Type\Atomic $input_type = null diff --git a/src/Psalm/Type/Atomic/CallableTrait.php b/src/Psalm/Type/Atomic/CallableTrait.php index 8976a0fb5..dfd975750 100644 --- a/src/Psalm/Type/Atomic/CallableTrait.php +++ b/src/Psalm/Type/Atomic/CallableTrait.php @@ -166,7 +166,7 @@ trait CallableTrait * @return void */ public function replaceTemplateTypesWithStandins( - array $template_types, + array &$template_types, array &$generic_params, Codebase $codebase = null, Atomic $input_type = null @@ -189,7 +189,8 @@ trait CallableTrait $template_types, $generic_params, $codebase, - $input_param_type + $input_param_type, + true ); } } diff --git a/src/Psalm/Type/Atomic/GenericTrait.php b/src/Psalm/Type/Atomic/GenericTrait.php index 2dd9c5596..455048d97 100644 --- a/src/Psalm/Type/Atomic/GenericTrait.php +++ b/src/Psalm/Type/Atomic/GenericTrait.php @@ -146,7 +146,7 @@ trait GenericTrait * @return void */ public function replaceTemplateTypesWithStandins( - array $template_types, + array &$template_types, array &$generic_params, Codebase $codebase = null, Atomic $input_type = null diff --git a/src/Psalm/Type/Union.php b/src/Psalm/Type/Union.php index c17cd0238..a5352e590 100644 --- a/src/Psalm/Type/Union.php +++ b/src/Psalm/Type/Union.php @@ -789,10 +789,11 @@ class Union * @return void */ public function replaceTemplateTypesWithStandins( - array $template_types, + array &$template_types, array &$generic_params, Codebase $codebase = null, - Type\Union $input_type = null + Type\Union $input_type = null, + bool $add_upper_bound = false ) { $keys_to_unset = []; @@ -802,6 +803,9 @@ class Union ) { if ($template_types[$key]->getId() !== $key) { $first_atomic_type = array_values($template_types[$key]->getTypes())[0]; + if ($add_upper_bound && $input_type) { + $template_types[$key] = clone $input_type; + } $this->types[$first_atomic_type->getKey()] = clone $first_atomic_type; if ($first_atomic_type->getKey() !== $key) { $keys_to_unset[] = $key; diff --git a/tests/TemplateTest.php b/tests/TemplateTest.php index 3ac506ddc..d19ae3b1f 100644 --- a/tests/TemplateTest.php +++ b/tests/TemplateTest.php @@ -1141,6 +1141,28 @@ class TemplateTest extends TestCase 'assertions' => [], 'error_levels' => [], ], + 'bindFirstTemplatedClosureParameter' => [ + ' 'MixedTypeCoercion', ], + 'bindFirstTemplatedClosureParameter' => [ + ' 'InvalidScalarArgument', + ], + 'bindFirstTemplatedClosureParameterTypeCoercion' => [ + ' 'TypeCoercion', + ], ]; } }