From 1401bf94db5ee15bdda6a62f65085640cdf2771e Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 17 Aug 2022 13:11:27 +0200 Subject: [PATCH 1/5] Fix #8414 --- .../Call/ClassTemplateParamCollector.php | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php index ab31e700a52..15daaca6cba 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php @@ -3,6 +3,7 @@ namespace Psalm\Internal\Analyzer\Statements\Expression\Call; use Psalm\Codebase; +use Psalm\Internal\Type\TemplateResult; use Psalm\Internal\Type\TypeExpander; use Psalm\Storage\ClassLikeStorage; use Psalm\Type; @@ -118,6 +119,7 @@ public static function collect( $input_type_extends = $e[$class_storage->name][$type_name]; $output_type_extends = self::resolveTemplateParam( + $codebase, $input_type_extends, $static_class_storage, $lhs_type_part @@ -163,11 +165,23 @@ public static function collect( return $class_template_params; } - public static function resolveTemplateParam( + private static function resolveTemplateParam( + Codebase $codebase, Union $input_type_extends, ClassLikeStorage $static_class_storage, - TGenericObject $lhs_type_part + TGenericObject $lhs_type_part, + ?TemplateResult $template_result = null ): ?Union { + if ($template_result === null) { + $templates = self::collect( + $codebase, + $static_class_storage, + $static_class_storage, + null, + $lhs_type_part + ); + $template_result = new TemplateResult($static_class_storage->template_types, $templates); + } $output_type_extends = null; foreach ($input_type_extends->getAtomicTypes() as $type_extends_atomic) { if ($type_extends_atomic instanceof TTemplateParam) { @@ -199,12 +213,14 @@ public static function resolveTemplateParam( [$type_extends_atomic->param_name] )) { $nested_output_type = self::resolveTemplateParam( + $codebase, $static_class_storage ->template_extended_params [$type_extends_atomic->defining_class] [$type_extends_atomic->param_name], $static_class_storage, - $lhs_type_part + $lhs_type_part, + $template_result ); if ($nested_output_type !== null) { $output_type_extends = Type::combineUnionTypes( @@ -214,6 +230,10 @@ public static function resolveTemplateParam( } } } else { + $type_extends_atomic->replaceTemplateTypesWithArgTypes( + $template_result, + $codebase + ); $output_type_extends = Type::combineUnionTypes( new Union([$type_extends_atomic]), $output_type_extends From 24b008ee40a6094ec6d6d2ab39a3f31b1cbe6d2c Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 17 Aug 2022 13:19:25 +0200 Subject: [PATCH 2/5] Fix --- .../Expression/Call/ClassTemplateParamCollector.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php index 15daaca6cba..6c2ffec1477 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php @@ -172,7 +172,7 @@ private static function resolveTemplateParam( TGenericObject $lhs_type_part, ?TemplateResult $template_result = null ): ?Union { - if ($template_result === null) { + if ($template_result === null && $static_class_storage->template_types) { $templates = self::collect( $codebase, $static_class_storage, @@ -230,10 +230,12 @@ private static function resolveTemplateParam( } } } else { - $type_extends_atomic->replaceTemplateTypesWithArgTypes( - $template_result, - $codebase - ); + if ($template_result !== null) { + $type_extends_atomic->replaceTemplateTypesWithArgTypes( + $template_result, + $codebase + ); + } $output_type_extends = Type::combineUnionTypes( new Union([$type_extends_atomic]), $output_type_extends From 95ef63ece9502386a2f36339091f975cfa267122 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 17 Aug 2022 13:25:00 +0200 Subject: [PATCH 3/5] Update --- .../Expression/Call/ClassTemplateParamCollector.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php index 6c2ffec1477..27b4bc44871 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php @@ -2,6 +2,7 @@ namespace Psalm\Internal\Analyzer\Statements\Expression\Call; +use AssertionError; use Psalm\Codebase; use Psalm\Internal\Type\TemplateResult; use Psalm\Internal\Type\TypeExpander; @@ -180,6 +181,9 @@ private static function resolveTemplateParam( null, $lhs_type_part ); + if ($templates === null) { + throw new AssertionError("Could not collect templates!"); + } $template_result = new TemplateResult($static_class_storage->template_types, $templates); } $output_type_extends = null; From 0220da0b32b898405639f5cf27ffe797f5f40972 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 17 Aug 2022 13:30:36 +0200 Subject: [PATCH 4/5] Add tests --- .../Call/ClassTemplateParamCollector.php | 1 + tests/Template/ClassTemplateTest.php | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php index 27b4bc44871..3620c152076 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php @@ -235,6 +235,7 @@ private static function resolveTemplateParam( } } else { if ($template_result !== null) { + $type_extends_atomic = clone $type_extends_atomic; $type_extends_atomic->replaceTemplateTypesWithArgTypes( $template_result, $codebase diff --git a/tests/Template/ClassTemplateTest.php b/tests/Template/ClassTemplateTest.php index ef1cda7ba81..91c733e18a4 100644 --- a/tests/Template/ClassTemplateTest.php +++ b/tests/Template/ClassTemplateTest.php @@ -3764,6 +3764,40 @@ protected function setUp(): void } }', ], + 'complexTypes' => [ + 'code' => 'v; } + } + + + /** + * @template TTObject + * + * @extends Future> + */ + class FutureB extends Future { + /** @param TTObject $data */ + public function __construct($data) { parent::__construct(new ArrayObject([$data])); } + } + + $a = new FutureB(123); + + $r = $a->get();', + 'assertions' => [ + '$a===' => 'FutureB<123>', + '$r===' => 'ArrayObject' + ] + ] ]; } From 979091d2abef6ef7fc98a63cbbae200143dfbd91 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 17 Aug 2022 13:38:11 +0200 Subject: [PATCH 5/5] Optimize --- .../Call/ClassTemplateParamCollector.php | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php index 3620c152076..d498beff791 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php @@ -109,6 +109,23 @@ public static function collect( } } + $template_result = null; + if ($class_storage !== $static_class_storage && $static_class_storage->template_types) { + $templates = self::collect( + $codebase, + $static_class_storage, + $static_class_storage, + null, + $lhs_type_part + ); + if ($templates === null) { + throw new AssertionError("Could not collect templates!"); + } + $template_result = new TemplateResult( + $static_class_storage->template_types, + $templates + ); + } foreach ($template_types as $type_name => $_) { if (isset($class_template_params[$type_name])) { continue; @@ -123,7 +140,8 @@ public static function collect( $codebase, $input_type_extends, $static_class_storage, - $lhs_type_part + $lhs_type_part, + $template_result ); $class_template_params[$type_name][$class_storage->name] @@ -173,19 +191,6 @@ private static function resolveTemplateParam( TGenericObject $lhs_type_part, ?TemplateResult $template_result = null ): ?Union { - if ($template_result === null && $static_class_storage->template_types) { - $templates = self::collect( - $codebase, - $static_class_storage, - $static_class_storage, - null, - $lhs_type_part - ); - if ($templates === null) { - throw new AssertionError("Could not collect templates!"); - } - $template_result = new TemplateResult($static_class_storage->template_types, $templates); - } $output_type_extends = null; foreach ($input_type_extends->getAtomicTypes() as $type_extends_atomic) { if ($type_extends_atomic instanceof TTemplateParam) {