1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 12:24:49 +01:00

Fix iterable template replacement

This commit is contained in:
andrew 2023-04-17 11:03:39 +03:00
parent a9bc87e729
commit b0e5df570d
4 changed files with 51 additions and 11 deletions

View File

@ -388,7 +388,6 @@
<file src="src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php">
<ImpureMethodCall>
<code>getClassTemplateTypes</code>
<code>has</code>
</ImpureMethodCall>
</file>
<file src="src/Psalm/Internal/Type/TypeCombiner.php">

View File

@ -49,10 +49,14 @@ class ClassLikeStorageProvider
return self::$storage[$fq_classlike_name_lc];
}
/**
* @psalm-mutation-free
*/
public function has(string $fq_classlike_name): bool
{
$fq_classlike_name_lc = strtolower($fq_classlike_name);
/** @psalm-suppress ImpureStaticProperty Used only for caching */
return isset(self::$storage[$fq_classlike_name_lc]);
}

View File

@ -33,7 +33,6 @@ use Psalm\Type\Atomic\TTemplateParamClass;
use Psalm\Type\Atomic\TTemplatePropertiesOf;
use Psalm\Type\Atomic\TTemplateValueOf;
use Psalm\Type\Union;
use Throwable;
use function array_fill;
use function array_keys;
@ -1233,13 +1232,13 @@ class TemplateStandinTypeReplacer
$input_type_params = [];
}
try {
$input_class_storage = $codebase->classlike_storage_provider->get($input_type_part->value);
$container_class_storage = $codebase->classlike_storage_provider->get($container_type_part->value);
$container_type_params_covariant = $container_class_storage->template_covariants;
} catch (Throwable $e) {
$input_class_storage = null;
}
$input_class_storage = $codebase->classlike_storage_provider->has($input_type_part->value)
? $codebase->classlike_storage_provider->get($input_type_part->value)
: null;
$container_type_params_covariant = $codebase->classlike_storage_provider->has($container_type_part->value)
? $codebase->classlike_storage_provider->get($container_type_part->value)->template_covariants
: null;
if ($input_type_part->value !== $container_type_part->value
&& $input_class_storage
@ -1266,8 +1265,12 @@ class TemplateStandinTypeReplacer
$template_extends = $input_class_storage->template_extended_params;
if (isset($template_extends[$container_type_part->value])) {
$params = $template_extends[$container_type_part->value];
$container_type_part_value = $container_type_part->value === 'iterable'
? 'Traversable'
: $container_type_part->value;
if (isset($template_extends[$container_type_part_value])) {
$params = $template_extends[$container_type_part_value];
$new_input_params = [];

View File

@ -46,6 +46,40 @@ class FunctionCallTest extends TestCase
'$genericList===' => 'list{1, 2, 3}',
],
],
'inferIterableFromTraversable' => [
'code' => '<?php
/**
* @return SplFixedArray<string>
*/
function getStrings(): SplFixedArray
{
return SplFixedArray::fromArray(["fst", "snd", "thr"]);
}
/**
* @return SplFixedArray<int>
*/
function getIntegers(): SplFixedArray
{
return SplFixedArray::fromArray([1, 2, 3]);
}
/**
* @template K
* @template A
* @template B
* @param iterable<K, A> $lhs
* @param iterable<K, B> $rhs
* @return iterable<K, A|B>
*/
function mergeIterable(iterable $lhs, iterable $rhs): iterable
{
foreach ($lhs as $k => $v) { yield $k => $v; }
foreach ($rhs as $k => $v) { yield $k => $v; }
}
$iterable = mergeIterable(getStrings(), getIntegers());',
'assertions' => [
'$iterable===' => 'iterable<int, int|string>',
],
],
'countShapedArrays' => [
'code' => '<?php
/** @var array{a?: int} */