1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Merge pull request #9658 from klimick/fix-list-template-replacement

Fix list<T> template replacement
This commit is contained in:
orklah 2023-04-16 21:11:58 +02:00 committed by GitHub
commit 2a6d9a8785
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 72 additions and 6 deletions

View File

@ -549,7 +549,6 @@
<PossiblyUndefinedIntArrayOffset>
<code><![CDATA[$this->properties[0]]]></code>
<code><![CDATA[$this->properties[0]]]></code>
<code><![CDATA[$this->properties[0]]]></code>
</PossiblyUndefinedIntArrayOffset>
<PossiblyUnusedMethod>
<code>getList</code>

View File

@ -149,15 +149,24 @@ class TKeyedArray extends Atomic
return $this->fallback_params === null;
}
/**
* @psalm-assert-if-true list{Union} $this->properties
* @psalm-assert-if-true list{Union, Union} $this->fallback_params
*/
public function isGenericList(): bool
{
return $this->is_list
&& count($this->properties) === 1
&& $this->fallback_params
&& $this->properties[0]->equals($this->fallback_params[1], true, true, false);
}
public function getId(bool $exact = true, bool $nested = false): string
{
$property_strings = [];
if ($this->is_list) {
if (count($this->properties) === 1
&& $this->fallback_params
&& $this->properties[0]->equals($this->fallback_params[1], true, true, false)
) {
if ($this->isGenericList()) {
$t = $this->properties[0]->possibly_undefined ? 'list' : 'non-empty-list';
return "$t<".$this->fallback_params[1]->getId($exact).'>';
}
@ -415,7 +424,7 @@ class TKeyedArray extends Atomic
public function isNonEmpty(): bool
{
if ($this->is_list) {
if ($this->isGenericList()) {
return !$this->properties[0]->possibly_undefined;
}
foreach ($this->properties as $property) {
@ -503,6 +512,35 @@ class TKeyedArray extends Atomic
bool $add_lower_bound = false,
int $depth = 0
): self {
if ($input_type instanceof TKeyedArray
&& $input_type->is_list
&& $input_type->isSealed()
&& $this->isGenericList()
) {
$replaced_list_type = $this
->getGenericArrayType()
->replaceTemplateTypesWithStandins(
$template_result,
$codebase,
$statements_analyzer,
$input_type->getGenericArrayType(),
$input_arg_offset,
$calling_class,
$calling_function,
$replace,
$add_lower_bound,
$depth,
)
->type_params[1]
->setPossiblyUndefined(!$this->isNonEmpty());
$cloned = clone $this;
$cloned->properties = [$replaced_list_type];
$cloned->fallback_params = [$this->fallback_params[1], $replaced_list_type];
return $cloned;
}
$properties = $this->properties;
foreach ($properties as $offset => $property) {

View File

@ -17,6 +17,35 @@ class FunctionCallTest extends TestCase
public function providerValidCodeParse(): iterable
{
return [
'inferGenericListFromTuple' => [
'code' => '<?php
/**
* @template A
* @param list<A> $list
* @return list<A>
*/
function testList(array $list): array { return $list; }
/**
* @template A
* @param non-empty-list<A> $list
* @return non-empty-list<A>
*/
function testNonEmptyList(array $list): array { return $list; }
/**
* @template A of list<mixed>
* @param A $list
* @return A
*/
function testGenericList(array $list): array { return $list; }
$list = testList([1, 2, 3]);
$nonEmptyList = testNonEmptyList([1, 2, 3]);
$genericList = testGenericList([1, 2, 3]);',
'assertions' => [
'$list===' => 'list<1|2|3>',
'$nonEmptyList===' => 'non-empty-list<1|2|3>',
'$genericList===' => 'list{1, 2, 3}',
],
],
'countShapedArrays' => [
'code' => '<?php
/** @var array{a?: int} */