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:
parent
a9bc87e729
commit
b0e5df570d
@ -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">
|
||||||
|
@ -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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 = [];
|
||||||
|
|
||||||
|
@ -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} */
|
||||||
|
Loading…
Reference in New Issue
Block a user