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

Merge pull request #9866 from klimick/fix-generic-type-params-mapping

Fix generic type params mapping
This commit is contained in:
orklah 2023-06-04 23:05:04 +02:00 committed by GitHub
commit 9d1c2c4596
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 28 deletions

View File

@ -36,6 +36,7 @@ use Psalm\Type\Atomic\TTemplateValueOf;
use Psalm\Type\Union;
use function array_fill;
use function array_filter;
use function array_keys;
use function array_merge;
use function array_search;
@ -1253,6 +1254,7 @@ class TemplateStandinTypeReplacer
Atomic $container_type_part,
?array &$container_type_params_covariant = null
): array {
$_ = null;
if ($input_type_part instanceof TGenericObject || $input_type_part instanceof TIterable) {
$input_type_params = $input_type_part->type_params;
} elseif ($codebase->classlike_storage_provider->has($input_type_part->value)) {
@ -1316,40 +1318,43 @@ class TemplateStandinTypeReplacer
foreach ($params as $extended_input_param_type) {
$new_input_param = null;
foreach ($extended_input_param_type->getAtomicTypes() as $et) {
if ($et instanceof TTemplateParam) {
$ets = Methods::getExtendedTemplatedTypes(
$et,
$template_extends,
);
} else {
$ets = [];
}
if ($ets
&& $ets[0] instanceof TTemplateParam
&& isset(
$input_class_storage->template_types
[$ets[0]->param_name]
[$ets[0]->defining_class],
foreach ($extended_input_param_type->getAtomicTypes() as $extended_template) {
$extended_templates = $extended_template instanceof TTemplateParam
? array_values(
array_filter(
Methods::getExtendedTemplatedTypes($extended_template, $template_extends),
static fn(Atomic $a) => $a instanceof TTemplateParam,
),
)
) {
$old_params_offset = (int) array_search(
$ets[0]->param_name,
array_keys($input_class_storage->template_types),
);
: [];
$candidate_param_type = $input_type_params[$old_params_offset] ?? Type::getMixed();
$candidate_param_type = $candidate_param_type->setProperties([
'from_template_default' => true,
]);
} else {
$candidate_param_type = new Union([$et], ['from_template_default' => true]);
$candidate_param_types = [];
if ($extended_templates) {
foreach ($extended_templates as $template) {
if (!isset(
$input_class_storage->template_types
[$template->param_name]
[$template->defining_class],
)) {
continue;
}
$old_params_offset = (int) array_search(
$template->param_name,
array_keys($input_class_storage->template_types),
);
$candidate_param_types[] = ($input_type_params[$old_params_offset] ?? Type::getMixed())
->setProperties(['from_template_default' => true]);
}
}
$new_input_param = Type::combineUnionTypes(
$new_input_param,
$candidate_param_type,
$candidate_param_types
? Type::combineUnionTypeArray($candidate_param_types, $codebase)
: new Union([$extended_template], ['from_template_default' => true]),
);
}

View File

@ -4138,6 +4138,48 @@ class ClassTemplateTest extends TestCase
}
',
],
'typesOrderInsideImplementsNotMatter' => [
'code' => '<?php
/** @template T */
interface I {}
/**
* @template T
* @extends I<T>
*/
interface ExtendedI extends I {}
/**
* @template T
* @implements ExtendedI<T|null>
*/
final class TWithNull implements ExtendedI
{
/** @param T $_value */
public function __construct($_value) {}
}
/**
* @template T
* @implements ExtendedI<null|T>
*/
final class NullWithT implements ExtendedI
{
/** @param T $_value */
public function __construct($_value) {}
}
/** @param I<null|int> $_type */
function nullWithInt(I $_type): void {}
/** @param I<int|null> $_type */
function intWithNull(I $_type): void {}
nullWithInt(new TWithNull(1));
nullWithInt(new NullWithT(1));
intWithNull(new TWithNull(1));
intWithNull(new NullWithT(1));',
],
];
}