1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +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"> <file src="src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php">
<ImpureMethodCall> <ImpureMethodCall>
<code>getClassTemplateTypes</code> <code>getClassTemplateTypes</code>
<code>has</code>
</ImpureMethodCall> </ImpureMethodCall>
</file> </file>
<file src="src/Psalm/Internal/Type/TypeCombiner.php"> <file src="src/Psalm/Internal/Type/TypeCombiner.php">

View File

@ -49,10 +49,14 @@ class ClassLikeStorageProvider
return self::$storage[$fq_classlike_name_lc]; return self::$storage[$fq_classlike_name_lc];
} }
/**
* @psalm-mutation-free
*/
public function has(string $fq_classlike_name): bool public function has(string $fq_classlike_name): bool
{ {
$fq_classlike_name_lc = strtolower($fq_classlike_name); $fq_classlike_name_lc = strtolower($fq_classlike_name);
/** @psalm-suppress ImpureStaticProperty Used only for caching */
return isset(self::$storage[$fq_classlike_name_lc]); 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\TTemplatePropertiesOf;
use Psalm\Type\Atomic\TTemplateValueOf; use Psalm\Type\Atomic\TTemplateValueOf;
use Psalm\Type\Union; use Psalm\Type\Union;
use Throwable;
use function array_fill; use function array_fill;
use function array_keys; use function array_keys;
@ -1233,13 +1232,13 @@ class TemplateStandinTypeReplacer
$input_type_params = []; $input_type_params = [];
} }
try { $input_class_storage = $codebase->classlike_storage_provider->has($input_type_part->value)
$input_class_storage = $codebase->classlike_storage_provider->get($input_type_part->value); ? $codebase->classlike_storage_provider->get($input_type_part->value)
$container_class_storage = $codebase->classlike_storage_provider->get($container_type_part->value); : null;
$container_type_params_covariant = $container_class_storage->template_covariants;
} catch (Throwable $e) { $container_type_params_covariant = $codebase->classlike_storage_provider->has($container_type_part->value)
$input_class_storage = null; ? $codebase->classlike_storage_provider->get($container_type_part->value)->template_covariants
} : null;
if ($input_type_part->value !== $container_type_part->value if ($input_type_part->value !== $container_type_part->value
&& $input_class_storage && $input_class_storage
@ -1266,8 +1265,12 @@ class TemplateStandinTypeReplacer
$template_extends = $input_class_storage->template_extended_params; $template_extends = $input_class_storage->template_extended_params;
if (isset($template_extends[$container_type_part->value])) { $container_type_part_value = $container_type_part->value === 'iterable'
$params = $template_extends[$container_type_part->value]; ? 'Traversable'
: $container_type_part->value;
if (isset($template_extends[$container_type_part_value])) {
$params = $template_extends[$container_type_part_value];
$new_input_params = []; $new_input_params = [];

View File

@ -46,6 +46,40 @@ class FunctionCallTest extends TestCase
'$genericList===' => 'list{1, 2, 3}', '$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' => [ 'countShapedArrays' => [
'code' => '<?php 'code' => '<?php
/** @var array{a?: int} */ /** @var array{a?: int} */