1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00

Merge pull request #8418 from Nicelocal/fix_8414

fix: Fix template resolution of complex extended types
This commit is contained in:
orklah 2022-08-17 20:38:02 +02:00 committed by GitHub
commit afe85fad86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 70 additions and 4 deletions

View File

@ -2,7 +2,9 @@
namespace Psalm\Internal\Analyzer\Statements\Expression\Call;
use AssertionError;
use Psalm\Codebase;
use Psalm\Internal\Type\TemplateResult;
use Psalm\Internal\Type\TypeExpander;
use Psalm\Storage\ClassLikeStorage;
use Psalm\Type;
@ -107,6 +109,23 @@ class ClassTemplateParamCollector
}
}
$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;
@ -118,9 +137,11 @@ class ClassTemplateParamCollector
$input_type_extends = $e[$class_storage->name][$type_name];
$output_type_extends = self::resolveTemplateParam(
$codebase,
$input_type_extends,
$static_class_storage,
$lhs_type_part
$lhs_type_part,
$template_result
);
$class_template_params[$type_name][$class_storage->name]
@ -163,10 +184,12 @@ class ClassTemplateParamCollector
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 {
$output_type_extends = null;
foreach ($input_type_extends->getAtomicTypes() as $type_extends_atomic) {
@ -199,12 +222,14 @@ class ClassTemplateParamCollector
[$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 +239,13 @@ class ClassTemplateParamCollector
}
}
} else {
if ($template_result !== null) {
$type_extends_atomic = clone $type_extends_atomic;
$type_extends_atomic->replaceTemplateTypesWithArgTypes(
$template_result,
$codebase
);
}
$output_type_extends = Type::combineUnionTypes(
new Union([$type_extends_atomic]),
$output_type_extends

View File

@ -3764,6 +3764,40 @@ class ClassTemplateTest extends TestCase
}
}',
],
'complexTypes' => [
'code' => '<?php
/**
* @template T
*/
class Future {
/**
* @param T $v
*/
public function __construct(private $v) {}
/** @return T */
public function get() { return $this->v; }
}
/**
* @template TTObject
*
* @extends Future<ArrayObject<int, TTObject>>
*/
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<int, 123>'
]
]
];
}