mirror of
https://github.com/danog/psalm.git
synced 2024-11-27 04:45:20 +01:00
commit
e17290a9bc
@ -247,7 +247,8 @@ class ArrayAnalyzer
|
||||
$statements_analyzer,
|
||||
$array_creation_info,
|
||||
$item,
|
||||
$unpacked_array_type
|
||||
$unpacked_array_type,
|
||||
$codebase
|
||||
);
|
||||
|
||||
if (($data_flow_graph = $statements_analyzer->data_flow_graph)
|
||||
@ -482,12 +483,16 @@ class ArrayAnalyzer
|
||||
StatementsAnalyzer $statements_analyzer,
|
||||
ArrayCreationInfo $array_creation_info,
|
||||
PhpParser\Node\Expr\ArrayItem $item,
|
||||
Type\Union $unpacked_array_type
|
||||
Type\Union $unpacked_array_type,
|
||||
Codebase $codebase
|
||||
) : void {
|
||||
foreach ($unpacked_array_type->getAtomicTypes() as $unpacked_atomic_type) {
|
||||
if ($unpacked_atomic_type instanceof Type\Atomic\TKeyedArray) {
|
||||
foreach ($unpacked_atomic_type->properties as $key => $property_value) {
|
||||
if (\is_string($key)) {
|
||||
if ($codebase->php_major_version < 8 ||
|
||||
($codebase->php_major_version === 8 && $codebase->php_minor_version < 1)
|
||||
) {
|
||||
if (IssueBuffer::accepts(
|
||||
new DuplicateArrayKey(
|
||||
'String keys are not supported in unpacked arrays',
|
||||
@ -500,17 +505,20 @@ class ArrayAnalyzer
|
||||
|
||||
return;
|
||||
}
|
||||
$new_offset = $key;
|
||||
$array_creation_info->item_key_atomic_types[] = new Type\Atomic\TLiteralString($new_offset);
|
||||
} else {
|
||||
$new_offset = $array_creation_info->int_offset++;
|
||||
$array_creation_info->item_key_atomic_types[] = new Type\Atomic\TLiteralInt($new_offset);
|
||||
}
|
||||
|
||||
$new_int_offset = $array_creation_info->int_offset++;
|
||||
|
||||
$array_creation_info->item_key_atomic_types[] = new Type\Atomic\TLiteralInt($new_int_offset);
|
||||
$array_creation_info->item_value_atomic_types = array_merge(
|
||||
$array_creation_info->item_value_atomic_types,
|
||||
array_values($property_value->getAtomicTypes())
|
||||
);
|
||||
|
||||
$array_creation_info->array_keys[$new_int_offset] = true;
|
||||
$array_creation_info->property_types[$new_int_offset] = $property_value;
|
||||
$array_creation_info->array_keys[$new_offset] = true;
|
||||
$array_creation_info->property_types[$new_offset] = $property_value;
|
||||
}
|
||||
} else {
|
||||
$codebase = $statements_analyzer->getCodebase();
|
||||
@ -529,6 +537,9 @@ class ArrayAnalyzer
|
||||
$array_creation_info->can_create_objectlike = false;
|
||||
|
||||
if ($unpacked_atomic_type->type_params[0]->hasString()) {
|
||||
if ($codebase->php_major_version < 8 ||
|
||||
($codebase->php_major_version === 8 && $codebase->php_minor_version < 1)
|
||||
) {
|
||||
if (IssueBuffer::accepts(
|
||||
new DuplicateArrayKey(
|
||||
'String keys are not supported in unpacked arrays',
|
||||
@ -538,6 +549,11 @@ class ArrayAnalyzer
|
||||
)) {
|
||||
// fall through
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$array_creation_info->item_key_atomic_types[] = new Type\Atomic\TString();
|
||||
} elseif ($unpacked_atomic_type->type_params[0]->hasInt()) {
|
||||
$array_creation_info->item_key_atomic_types[] = new Type\Atomic\TInt();
|
||||
}
|
||||
|
@ -671,20 +671,20 @@ class SimpleTypeInferer
|
||||
if ($unpacked_atomic_type instanceof Type\Atomic\TKeyedArray) {
|
||||
foreach ($unpacked_atomic_type->properties as $key => $property_value) {
|
||||
if (\is_string($key)) {
|
||||
// string keys are not supported in unpacked arrays
|
||||
return false;
|
||||
$new_offset = $key;
|
||||
$array_creation_info->item_key_atomic_types[] = new Type\Atomic\TLiteralString($new_offset);
|
||||
} else {
|
||||
$new_offset = $array_creation_info->int_offset++;
|
||||
$array_creation_info->item_key_atomic_types[] = new Type\Atomic\TLiteralInt($new_offset);
|
||||
}
|
||||
|
||||
$new_int_offset = $array_creation_info->int_offset++;
|
||||
|
||||
$array_creation_info->item_key_atomic_types[] = new Type\Atomic\TLiteralInt($new_int_offset);
|
||||
$array_creation_info->item_value_atomic_types = array_merge(
|
||||
$array_creation_info->item_value_atomic_types,
|
||||
array_values($property_value->getAtomicTypes())
|
||||
);
|
||||
|
||||
$array_creation_info->array_keys[$new_int_offset] = true;
|
||||
$array_creation_info->property_types[$new_int_offset] = $property_value;
|
||||
$array_creation_info->array_keys[$new_offset] = true;
|
||||
$array_creation_info->property_types[$new_offset] = $property_value;
|
||||
}
|
||||
} elseif ($unpacked_atomic_type instanceof Type\Atomic\TArray) {
|
||||
/** @psalm-suppress PossiblyUndefinedArrayOffset provably true, but Psalm can’t see it */
|
||||
@ -694,8 +694,7 @@ class SimpleTypeInferer
|
||||
$array_creation_info->can_create_objectlike = false;
|
||||
|
||||
if ($unpacked_atomic_type->type_params[0]->hasString()) {
|
||||
// string keys are not supported in unpacked arrays
|
||||
return false;
|
||||
$array_creation_info->item_key_atomic_types[] = new Type\Atomic\TString();
|
||||
}
|
||||
|
||||
if ($unpacked_atomic_type->type_params[0]->hasInt()) {
|
||||
|
@ -1234,6 +1234,19 @@ class ArrayAssignmentTest extends TestCase
|
||||
'$arr3' => 'array{1: int, 2: int, 3: int, 4: int}',
|
||||
]
|
||||
],
|
||||
'arraySpreadWithString' => [
|
||||
'<?php
|
||||
$x = [
|
||||
"a" => 0,
|
||||
...["a" => 1],
|
||||
...["b" => 2]
|
||||
];',
|
||||
[
|
||||
'$x===' => 'array{a: 1, b: 2}',
|
||||
],
|
||||
[],
|
||||
'8.1'
|
||||
],
|
||||
'listPropertyAssignmentAfterIsset' => [
|
||||
'<?php
|
||||
class Collection {
|
||||
@ -1631,6 +1644,51 @@ class ArrayAssignmentTest extends TestCase
|
||||
',
|
||||
'assertions' => ['$_a===' => 'array{16: 16, 17: 17, 18: 18}']
|
||||
],
|
||||
'unpackTypedIterableWithStringKeysIntoArray' => [
|
||||
'<?php
|
||||
|
||||
/**
|
||||
* @param iterable<string, string> $data
|
||||
* @return list<string>
|
||||
*/
|
||||
function unpackIterable(iterable $data): array
|
||||
{
|
||||
return [...$data];
|
||||
}',
|
||||
[],
|
||||
[],
|
||||
'8.1'
|
||||
],
|
||||
'unpackTypedTraversableWithStringKeysIntoArray' => [
|
||||
'<?php
|
||||
|
||||
/**
|
||||
* @param Traversable<string, string> $data
|
||||
* @return list<string>
|
||||
*/
|
||||
function unpackIterable(Traversable $data): array
|
||||
{
|
||||
return [...$data];
|
||||
}',
|
||||
[],
|
||||
[],
|
||||
'8.1'
|
||||
],
|
||||
'unpackArrayWithArrayKeyIntoArray' => [
|
||||
'<?php
|
||||
|
||||
/**
|
||||
* @param array<array-key, mixed> $data
|
||||
* @return list<mixed>
|
||||
*/
|
||||
function unpackArray(array $data): array
|
||||
{
|
||||
return [...$data];
|
||||
}',
|
||||
[],
|
||||
[],
|
||||
'8.1'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -1882,45 +1940,6 @@ class ArrayAssignmentTest extends TestCase
|
||||
}',
|
||||
'error_message' => 'NullableReturnStatement',
|
||||
],
|
||||
'unpackTypedIterableWithStringKeysIntoArray' => [
|
||||
'<?php
|
||||
|
||||
/**
|
||||
* @param iterable<string, string> $data
|
||||
* @return list<string>
|
||||
*/
|
||||
function unpackIterable(iterable $data): array
|
||||
{
|
||||
return [...$data];
|
||||
}',
|
||||
'error_message' => 'DuplicateArrayKey'
|
||||
],
|
||||
'unpackTypedTraversableWithStringKeysIntoArray' => [
|
||||
'<?php
|
||||
|
||||
/**
|
||||
* @param Traversable<string, string> $data
|
||||
* @return list<string>
|
||||
*/
|
||||
function unpackIterable(Traversable $data): array
|
||||
{
|
||||
return [...$data];
|
||||
}',
|
||||
'error_message' => 'DuplicateArrayKey'
|
||||
],
|
||||
'unpackArrayWithArrayKeyIntoArray' => [
|
||||
'<?php
|
||||
|
||||
/**
|
||||
* @param array<array-key, mixed> $data
|
||||
* @return list<mixed>
|
||||
*/
|
||||
function unpackArray(array $data): array
|
||||
{
|
||||
return [...$data];
|
||||
}',
|
||||
'error_message' => 'DuplicateArrayKey',
|
||||
],
|
||||
'ArrayCreateOffsetObject' => [
|
||||
'<?php
|
||||
$_a = [new stdClass => "a"];
|
||||
|
Loading…
Reference in New Issue
Block a user