5.4: Issue with flow analysis causes NoValue false positive
https://psalm.dev/r/8a407af77c
psalm seems to get confused that $v is updated in the loop after the condition.
This was fine in 5.3 and prior and is broken in 5.4
I found these snippets:
https://psalm.dev/r/8a407af77c
<?php
$v = 0;
do {
$foo = "a";
if ($v > 0) {
$foo .= sprintf('_%02d', $v);
}
$v++;
} while (rand(1,10) < 5);
echo $foo;
Psalm output (using commit 62db5d4):
ERROR: NoValue - 7:34 - All possible types for this argument were invalidated - This may be dead code
Encountered this too when making a PR, possibly caused by https://github.com/vimeo/psalm/pull/8934
The issue is that the if statement is analyzed twice, once with the impossible values inferred on the first iteration (always never), then on future iterations which are not never: https://psalm.dev/r/3b6e234333
I found these snippets:
https://psalm.dev/r/3b6e234333
<?php
$v = 0;
do {
$foo = "a";
if ($v > 0) {
/** @psalm-trace $v */;
$foo .= sprintf('_%02d', $v);
}
$v++;
} while (rand(1,10) < 5);
echo $foo;
Psalm output (using commit 62db5d4):
INFO: Trace - 7:31 - $v: never
ERROR: NoValue - 8:34 - All possible types for this argument were invalidated - This may be dead code
INFO: Trace - 7:31 - $v: int<1, max>
in my case, the workaround is to put the increment before the if and change the condition to > 1. Which is also strange because that should IMHO fail the same way, but it doesn't.
Yeah, in the case of https://psalm.dev/r/b33c14eaf6 there should be a paradox since $v is always > 0, there's already an open issue for this: https://github.com/vimeo/psalm/issues/8059.
Overall I think the current behavior for https://psalm.dev/r/8a407af77c is correct, as it detects that there might be an impossible code path at some point in the iteration, closing this unless someone else comes up with a more broken example.
I found these snippets:
https://psalm.dev/r/b33c14eaf6
<?php
$v = 0;
do {
$foo = "a";
$v++;
/** @psalm-trace $v */;
if ($v > 0) {
$foo .= sprintf('_%02d', $v);
}
} while (rand(1,10) < 5);
echo $foo;
Psalm output (using commit 62db5d4):
INFO: Trace - 7:27 - $v: int<1, max>
https://psalm.dev/r/8a407af77c
<?php
$v = 0;
do {
$foo = "a";
if ($v > 0) {
$foo .= sprintf('_%02d', $v);
}
$v++;
} while (rand(1,10) < 5);
echo $foo;
Psalm output (using commit 62db5d4):
ERROR: NoValue - 7:34 - All possible types for this argument were invalidated - This may be dead code
cc @orklah just in case you want to comment on this too
The issue is that the if statement is analyzed twice, once with the impossible values inferred on the first iteration (always never), then on future iterations which are not never: https://psalm.dev/r/3b6e234333
I agree with that, so it seems there is indeed a bug here. I would keep this open? Unless I missed something?
I found these snippets:
https://psalm.dev/r/3b6e234333
<?php
$v = 0;
do {
$foo = "a";
if ($v > 0) {
/** @psalm-trace $v */;
$foo .= sprintf('_%02d', $v);
}
$v++;
} while (rand(1,10) < 5);
echo $foo;
Psalm output (using commit 62db5d4):
INFO: Trace - 7:31 - $v: never
ERROR: NoValue - 8:34 - All possible types for this argument were invalidated - This may be dead code
INFO: Trace - 7:31 - $v: int<1, max>
@danog Does this count as a more broken example?
https://psalm.dev/r/47669b633f
$sum = 0;
$i = 0;
do {
if ($i > 0) {
$sum += $i;
/** @psalm-trace $i */ // `never` AND `int<1, 9>`
echo $i; // NoValue
}
$i++;
} while ($i < 10);
The sum is silly, but it's the same issue as what I'm really trying to do: https://psalm.dev/r/dbcf7a3b01
I found these snippets:
https://psalm.dev/r/47669b633f
<?php
$sum = 0;
$i = 0;
do {
if ($i > 0) {
$sum += $i;
/** @psalm-trace $i */ // `never` AND `int<1, 9>`
echo $i; // NoValue
}
$i++;
} while ($i < 10);
echo $sum;
Psalm output (using commit cdceda0):
ERROR: NoValue - 9:14 - All possible types for this argument were invalidated - This may be dead code
INFO: Trace - 9:9 - $i: never
INFO: Trace - 9:9 - $i: int<1, 9>
https://psalm.dev/r/dbcf7a3b01
<?php
// Print the sum of the numbers 1 thru 9.
// (Yes, I know there's a closed form for this....)
$sum = 0;
$i = 0;
do {
if ($i > 0) {
$sum += $i;
echo "$i\t$sum\n"; // `never` cannot be cast to string
}
$i++;
} while ($i < 10);
Psalm output (using commit cdceda0):
ERROR: InvalidCast - 10:9 - never cannot be cast to string