2018-11-10 01:54:10 +01:00
|
|
|
<?php
|
2021-12-15 04:58:32 +01:00
|
|
|
|
2023-10-19 14:16:41 +02:00
|
|
|
declare(strict_types=1);
|
|
|
|
|
2018-11-10 01:54:10 +01:00
|
|
|
namespace Psalm\Tests\Loop;
|
|
|
|
|
2021-12-03 20:11:20 +01:00
|
|
|
use Psalm\Tests\TestCase;
|
2021-12-04 21:55:53 +01:00
|
|
|
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
|
|
|
|
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
|
2018-11-10 01:54:10 +01:00
|
|
|
|
2021-06-08 04:55:21 +02:00
|
|
|
use const DIRECTORY_SEPARATOR;
|
|
|
|
|
2021-12-03 20:11:20 +01:00
|
|
|
class ForTest extends TestCase
|
2018-11-10 01:54:10 +01:00
|
|
|
{
|
2021-12-04 21:55:53 +01:00
|
|
|
use InvalidCodeAnalysisTestTrait;
|
|
|
|
use ValidCodeAnalysisTestTrait;
|
2018-11-10 01:54:10 +01:00
|
|
|
|
2020-09-12 17:24:05 +02:00
|
|
|
public function providerValidCodeParse(): iterable
|
2018-11-10 01:54:10 +01:00
|
|
|
{
|
|
|
|
return [
|
2022-12-18 18:19:22 +01:00
|
|
|
'forTrue' => [
|
|
|
|
'code' => '<?php
|
|
|
|
function ret(): int {
|
|
|
|
for (;;) {
|
|
|
|
return 1;
|
|
|
|
}
|
2022-12-18 17:15:15 +01:00
|
|
|
}',
|
2022-12-18 18:19:22 +01:00
|
|
|
],
|
2018-11-10 01:54:10 +01:00
|
|
|
'implicitFourthLoop' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2018-11-10 01:54:10 +01:00
|
|
|
function test(): int {
|
|
|
|
$x = 0;
|
|
|
|
$y = 1;
|
|
|
|
$z = 2;
|
|
|
|
for ($i = 0; $i < 3; $i++) {
|
|
|
|
$x = $y;
|
|
|
|
$y = $z;
|
|
|
|
$z = 5;
|
|
|
|
}
|
|
|
|
return $x;
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'falseToBoolInContinueAndBreak' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2018-11-10 01:54:10 +01:00
|
|
|
$a = false;
|
|
|
|
|
|
|
|
for ($i = 0; $i < 4; $i++) {
|
|
|
|
$j = rand(0, 10);
|
|
|
|
|
|
|
|
if ($j === 2) {
|
|
|
|
$a = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($j === 3) {
|
|
|
|
$a = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}',
|
2022-11-05 22:34:42 +01:00
|
|
|
'assertions' => [
|
2018-11-10 01:54:10 +01:00
|
|
|
'$a' => 'bool',
|
|
|
|
],
|
|
|
|
],
|
|
|
|
'forLoopwithOKChange' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2018-11-10 01:54:10 +01:00
|
|
|
$j = 5;
|
|
|
|
for ($i = $j; $i < 4; $i++) {
|
|
|
|
$j = 9;
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'preventNegativeZeroScrewingThingsUp' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2018-11-10 01:54:10 +01:00
|
|
|
function foo() : void {
|
2022-01-23 22:05:38 +01:00
|
|
|
/** @var array<int, int> $v */
|
2018-11-10 01:54:10 +01:00
|
|
|
$v = [1 => 0];
|
|
|
|
for ($d = 0; $d <= 10; $d++) {
|
|
|
|
for ($k = -$d; $k <= $d; $k += 2) {
|
|
|
|
if ($k === -$d || ($k !== $d && $v[$k-1] < $v[$k+1])) {
|
|
|
|
$x = $v[$k+1];
|
|
|
|
} else {
|
|
|
|
$x = $v[$k-1] + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
$v[$k] = $x;
|
|
|
|
}
|
|
|
|
}
|
2019-03-23 19:27:54 +01:00
|
|
|
}',
|
2018-11-10 01:54:10 +01:00
|
|
|
],
|
2018-11-10 22:10:59 +01:00
|
|
|
'whileTrueWithBreak' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2018-11-10 22:10:59 +01:00
|
|
|
for (;;) {
|
|
|
|
$a = "hello";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
for (;;) {
|
|
|
|
$b = 5;
|
|
|
|
break;
|
|
|
|
}',
|
|
|
|
'assertions' => [
|
|
|
|
'$a' => 'string',
|
|
|
|
'$b' => 'int',
|
|
|
|
],
|
|
|
|
],
|
|
|
|
'continueOutsideLoop' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2018-11-10 22:10:59 +01:00
|
|
|
class Node {
|
|
|
|
/** @var Node|null */
|
|
|
|
public $next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @return void */
|
|
|
|
function test(Node $head) {
|
|
|
|
for ($node = $head; $node; $node = $next) {
|
|
|
|
$next = $node->next;
|
|
|
|
$node->next = null;
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'echoAfterFor' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2018-11-10 22:10:59 +01:00
|
|
|
for ($i = 0; $i < 5; $i++);
|
|
|
|
echo $i;',
|
|
|
|
],
|
2020-01-04 19:05:23 +01:00
|
|
|
'nestedEchoAfterFor' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2020-01-04 19:05:23 +01:00
|
|
|
for ($i = 1; $i < 2; $i++) {
|
|
|
|
for ($j = 1; $j < 2; $j++) {}
|
|
|
|
}
|
|
|
|
|
2022-12-18 17:15:15 +01:00
|
|
|
echo $i * $j;',
|
2020-01-04 19:05:23 +01:00
|
|
|
],
|
2020-07-03 17:13:44 +02:00
|
|
|
'reconcileOuterVars' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2020-07-03 17:13:44 +02:00
|
|
|
for ($i = 0; $i < 2; $i++) {
|
|
|
|
if ($i === 0) {
|
|
|
|
continue;
|
|
|
|
}
|
2022-12-18 17:15:15 +01:00
|
|
|
}',
|
2020-07-03 17:13:44 +02:00
|
|
|
],
|
2020-08-17 21:47:39 +02:00
|
|
|
'noException' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2020-08-17 21:47:39 +02:00
|
|
|
/**
|
|
|
|
* @param list<int> $arr
|
|
|
|
*/
|
|
|
|
function cartesianProduct(array $arr) : void {
|
|
|
|
for ($i = 20; $arr[$i] === 5 && $i > 0; $i--) {}
|
2022-12-18 17:15:15 +01:00
|
|
|
}',
|
2020-08-17 21:47:39 +02:00
|
|
|
],
|
2021-05-13 22:27:05 +02:00
|
|
|
'noCrashOnLongThing' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2021-05-13 22:27:05 +02:00
|
|
|
/**
|
2022-11-12 02:14:21 +01:00
|
|
|
* @param list<array{a: array{int, int}}> $data
|
2021-05-13 22:27:05 +02:00
|
|
|
*/
|
|
|
|
function makeData(array $data) : array {
|
|
|
|
while (rand(0, 1)) {
|
|
|
|
while (rand(0, 1)) {
|
|
|
|
while (rand(0, 1)) {
|
|
|
|
if (rand(0, 1)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-12-13 21:40:19 +01:00
|
|
|
/** @psalm-suppress PossiblyUndefinedArrayOffset */
|
2021-05-13 22:27:05 +02:00
|
|
|
$data[0]["a"] = array_merge($data[0]["a"], $data[0]["a"]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $data;
|
2022-12-18 17:15:15 +01:00
|
|
|
}',
|
2021-05-13 22:27:05 +02:00
|
|
|
],
|
2021-10-10 10:31:30 +02:00
|
|
|
'InfiniteForLoop' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2021-10-10 10:31:30 +02:00
|
|
|
/**
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
function g() {
|
|
|
|
for (;;) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
function h() {
|
|
|
|
for (;1;) {
|
|
|
|
return 1;
|
|
|
|
}
|
2022-12-18 17:15:15 +01:00
|
|
|
}',
|
2021-10-10 10:31:30 +02:00
|
|
|
],
|
2018-11-10 01:54:10 +01:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2020-09-12 17:24:05 +02:00
|
|
|
public function providerInvalidCodeParse(): iterable
|
2018-11-10 01:54:10 +01:00
|
|
|
{
|
|
|
|
return [
|
|
|
|
'possiblyUndefinedArrayInWhileAndForeach' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2018-11-10 01:54:10 +01:00
|
|
|
for ($i = 0; $i < 4; $i++) {
|
|
|
|
while (rand(0,10) === 5) {
|
|
|
|
$array[] = "hello";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
echo $array;',
|
2019-02-27 22:00:44 +01:00
|
|
|
'error_message' => 'PossiblyUndefinedGlobalVariable - src' . DIRECTORY_SEPARATOR . 'somefile.php:4:29 - Possibly undefined ' .
|
2018-11-10 01:54:10 +01:00
|
|
|
'global variable $array, first seen on line 4',
|
|
|
|
],
|
|
|
|
'forLoopInvalidation' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2018-11-10 01:54:10 +01:00
|
|
|
for ($i = 0; $i < 4; $i++) {
|
|
|
|
foreach ([1, 2, 3] as $i) {}
|
|
|
|
}',
|
|
|
|
'error_message' => 'LoopInvalidation',
|
|
|
|
],
|
|
|
|
'forInfiniteNoBreak' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2018-11-10 01:54:10 +01:00
|
|
|
for (;;) {
|
|
|
|
$a = "hello";
|
|
|
|
}
|
|
|
|
|
|
|
|
echo $a;',
|
|
|
|
'error_message' => 'UndefinedGlobalVariable',
|
|
|
|
],
|
2020-01-04 19:05:23 +01:00
|
|
|
'nestedEchoAfterFor' => [
|
2022-01-13 19:49:37 +01:00
|
|
|
'code' => '<?php
|
2020-01-04 19:05:23 +01:00
|
|
|
for ($i = 1; $i < 2; $i++) {
|
|
|
|
if (rand(0, 1)) break;
|
|
|
|
for ($j = 1; $j < 2; $j++) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
echo $i * $j;',
|
|
|
|
'error_message' => 'PossiblyUndefinedGlobalVariable',
|
|
|
|
],
|
2018-11-10 01:54:10 +01:00
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|