psalm
psalm copied to clipboard
False positive `ParadoxicalCondition` when using null-coalescing assignment
https://psalm.dev/r/dfb092ce4c
If you remove the ?? []
on line 16 the snipped above will error out with an Undefined offset: 1
error.
So this is seen as a ParadoxicalCondition
while it really is required.
I found these snippets:
https://psalm.dev/r/dfb092ce4c
<?php
/** @var array<int, array> $list */
$list = [
1 => ['b', 'c']
];
/** @var array<int, array> $list2 */
$list2 = [
1 => [1, 2],
2 => [3, 4],
];
$map = [];
foreach ($list as $key => $value) {
$map[$key] ??= array_merge(
$map[$key] ?? [],
...array_map(
fn(int $a) => $list2[$a],
$value
)
);
}
Psalm output (using commit 85fe7e8):
ERROR: ParadoxicalCondition - 16:3 - Condition ($map[$key] is isset) contradicts a previously-established condition ($map[$key] is not isset)
Somewhat simplified: https://psalm.dev/r/3703120ab5
I would say this error is correct. If the the array_merge
is running, then it means $map[$key]
is unset or null, so the empty array []
will always be used. This code is equivalent and causes no error: https://psalm.dev/r/3703120ab5 (In fact with this change, the array_merge
is now unnecessary.)
The thing that seems like a bug to me, however, is that we don't get the ParadoxicalCondition
when we change the array to a variable, eg: https://psalm.dev/r/f6745c6ba3
I found these snippets:
https://psalm.dev/r/3703120ab5
<?php
/**
* @param list<int> $list1
*/
function f(array $list1, array $list2): void
{
$map = [];
foreach ($list1 as $key) {
$map[$key] ??= array_merge(
$map[$key] ?? [],
...$list2
);
}
}
Psalm output (using commit c488d40):
ERROR: ParadoxicalCondition - 12:13 - Condition ($map[$key] is isset) contradicts a previously-established condition ($map[$key] is not isset)
https://psalm.dev/r/f6745c6ba3
<?php
/**
* @param list<int> $list1
*/
function f(array $list1, array $list2): void
{
foreach ($list1 as $_key) {
$v ??= array_merge(
$v ?? [],
...$list2
);
}
}
Psalm output (using commit c488d40):
INFO: MixedArgument - 10:13 - Argument 1 of array_merge cannot be array<never, never>|mixed, expecting array<array-key, mixed>
That's because $v is undefined (=mixed). Fix that and you'll get TypeDoesNotContainType
(=paradox): https://psalm.dev/r/55695988d8
I found these snippets:
https://psalm.dev/r/55695988d8
<?php
/**
* @param list<int> $list1
*/
function f(array $list1, array $list2): void
{
$v = null;
foreach ($list1 as $_key) {
$v ??= array_merge(
$v ?? [],
...$list2
);
}
}
Psalm output (using commit c488d40):
ERROR: TypeDoesNotContainType - 11:13 - Type null for $v is always !null
Overall, I don't see any bugs here. cc: @AndrolGenhald