1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00
This commit is contained in:
Daniil Gentili 2023-12-01 17:46:24 +01:00
parent 147129345e
commit 17bf9a45a6
3 changed files with 111 additions and 63 deletions

View File

@ -46,6 +46,8 @@ use Psalm\Type\Atomic\TFalse;
use Psalm\Type\Atomic\TInt;
use Psalm\Type\Atomic\TKeyedArray;
use Psalm\Type\Atomic\TList;
use Psalm\Type\Atomic\TLiteralInt;
use Psalm\Type\Atomic\TLiteralString;
use Psalm\Type\Atomic\TMixed;
use Psalm\Type\Atomic\TNamedObject;
use Psalm\Type\Atomic\TNever;
@ -1108,88 +1110,103 @@ class Reconciler
throw new UnexpectedValueException('Not expecting null array key');
}
$array_key_offsets = [];
if ($array_key[0] === '$') {
return;
if (!isset($existing_types[$array_key])) {
return;
}
$t = $existing_types[$array_key];
foreach ($t->getAtomicTypes() as $lit) {
if ($lit instanceof TLiteralInt || $lit instanceof TLiteralString) {
$array_key_offsets []= $lit->value;
continue;
}
return;
}
} else {
$array_key_offsets []= $array_key[0] === '\'' || $array_key[0] === '"' ? substr($array_key, 1, -1) : $array_key;
}
$array_key_offset = $array_key[0] === '\'' || $array_key[0] === '"' ? substr($array_key, 1, -1) : $array_key;
$base_key = implode($key_parts);
if (isset($existing_types[$base_key]) && $array_key_offset !== false) {
foreach ($existing_types[$base_key]->getAtomicTypes() as $base_atomic_type) {
if ($base_atomic_type instanceof TList) {
$base_atomic_type = $base_atomic_type->getKeyedArray();
}
if ($base_atomic_type instanceof TKeyedArray
$result_type = $result_type->setPossiblyUndefined(count($array_key_offsets) > 1);
foreach ($array_key_offsets as $array_key_offset) {
if (isset($existing_types[$base_key]) && $array_key_offset !== false) {
foreach ($existing_types[$base_key]->getAtomicTypes() as $base_atomic_type) {
if ($base_atomic_type instanceof TList) {
$base_atomic_type = $base_atomic_type->getKeyedArray();
}
if ($base_atomic_type instanceof TKeyedArray
|| ($base_atomic_type instanceof TArray
&& !$base_atomic_type->isEmptyArray())
|| $base_atomic_type instanceof TClassStringMap
) {
$new_base_type = $existing_types[$base_key];
) {
$new_base_type = $existing_types[$base_key];
if ($base_atomic_type instanceof TArray) {
$fallback_key_type = $base_atomic_type->type_params[0];
$fallback_value_type = $base_atomic_type->type_params[1];
if ($base_atomic_type instanceof TArray) {
$fallback_key_type = $base_atomic_type->type_params[0];
$fallback_value_type = $base_atomic_type->type_params[1];
$base_atomic_type = new TKeyedArray(
[
$base_atomic_type = new TKeyedArray(
[
$array_key_offset => $result_type,
],
null,
$fallback_key_type->isNever() ? null : [$fallback_key_type, $fallback_value_type],
);
} elseif ($base_atomic_type instanceof TClassStringMap) {
// do nothing
} else {
$properties = $base_atomic_type->properties;
$properties[$array_key_offset] = $result_type;
if ($base_atomic_type->is_list
],
null,
$fallback_key_type->isNever() ? null : [$fallback_key_type, $fallback_value_type],
);
} elseif ($base_atomic_type instanceof TClassStringMap) {
// do nothing
} else {
$properties = $base_atomic_type->properties;
$properties[$array_key_offset] = $result_type;
if ($base_atomic_type->is_list
&& (!is_numeric($array_key_offset)
|| ($array_key_offset
&& !isset($properties[$array_key_offset-1])
)
)
) {
if ($base_atomic_type->fallback_params && is_numeric($array_key_offset)) {
$fallback = $base_atomic_type->fallback_params[1]->setPossiblyUndefined(
$result_type->isNever(),
);
for ($x = 0; $x < $array_key_offset; $x++) {
$properties[$x] ??= $fallback;
) {
if ($base_atomic_type->fallback_params && is_numeric($array_key_offset)) {
$fallback = $base_atomic_type->fallback_params[1]->setPossiblyUndefined(
$result_type->isNever(),
);
for ($x = 0; $x < $array_key_offset; $x++) {
$properties[$x] ??= $fallback;
}
ksort($properties);
$base_atomic_type = $base_atomic_type->setProperties($properties);
} else {
// This should actually be a paradox
$base_atomic_type = new TKeyedArray(
$properties,
null,
$base_atomic_type->fallback_params,
false,
$base_atomic_type->from_docblock,
);
}
ksort($properties);
$base_atomic_type = $base_atomic_type->setProperties($properties);
} else {
// This should actually be a paradox
$base_atomic_type = new TKeyedArray(
$properties,
null,
$base_atomic_type->fallback_params,
false,
$base_atomic_type->from_docblock,
);
$base_atomic_type = $base_atomic_type->setProperties($properties);
}
} else {
$base_atomic_type = $base_atomic_type->setProperties($properties);
}
$new_base_type = $new_base_type->getBuilder()->addType($base_atomic_type)->freeze();
$changed_var_ids[$base_key . '[' . $array_key . ']'] = true;
if ($key_parts[count($key_parts) - 1] === ']') {
self::adjustTKeyedArrayType(
$key_parts,
$existing_types,
$changed_var_ids,
$new_base_type,
);
}
$existing_types[$base_key] = $new_base_type;
break;
}
$new_base_type = $new_base_type->getBuilder()->addType($base_atomic_type)->freeze();
$changed_var_ids[$base_key . '[' . $array_key . ']'] = true;
if ($key_parts[count($key_parts) - 1] === ']') {
self::adjustTKeyedArrayType(
$key_parts,
$existing_types,
$changed_var_ids,
$new_base_type,
);
}
$existing_types[$base_key] = $new_base_type;
break;
}
}
}

View File

@ -1027,7 +1027,9 @@ class ForeachTest extends TestCase
$arr = [];
foreach ([1, 2, 3] as $i) {
$arr[$i]["a"] ??= 0;
if (!isset($arr[$i]["a"])) {
$arr[$i]["a"] = 0;
}
$arr[$i]["a"] += 5;
}

View File

@ -54,7 +54,36 @@ class IssetTest extends TestCase
$arr[$i] = 1;
}
return $arr;
}'
}',
],
'issetWithArrayAssignment2' => [
'code'=> '<?php
/**
* @param array{0?: 0, 1?: 0} $arr
* @param 0|1 $i
* @return array{0?: 0|1, 1?: 0|1}
*/
function t2(array $arr, int $i): array {
if (!isset($arr[$i])) {
$arr[$i] = 1;
}
return $arr;
}',
],
'issetWithArrayAssignmentSubVar' => [
'code'=> '<?php
/**
* @param array{0?: 0, v: 0} $arr
* @return array{0: 0|1, v: 0}
*/
function t2(array $arr): array {
if (!isset($arr[$arr["v"]])) {
$arr[$arr["v"]] = 1;
}
return $arr;
}',
],
'isset' => [
'code' => '<?php