2018-11-10 01:54:10 +01:00
|
|
|
<?php
|
|
|
|
namespace Psalm\Tests\Loop;
|
|
|
|
|
2019-06-26 22:52:29 +02:00
|
|
|
use const DIRECTORY_SEPARATOR;
|
2019-07-05 22:24:00 +02:00
|
|
|
use Psalm\Tests\Traits;
|
2018-11-10 01:54:10 +01:00
|
|
|
|
|
|
|
class ForTest extends \Psalm\Tests\TestCase
|
|
|
|
{
|
2018-11-06 03:57:36 +01:00
|
|
|
use Traits\InvalidCodeAnalysisTestTrait;
|
|
|
|
use Traits\ValidCodeAnalysisTestTrait;
|
2018-11-10 01:54:10 +01:00
|
|
|
|
|
|
|
/**
|
2019-03-01 21:55:20 +01:00
|
|
|
* @return iterable<string,array{string,assertions?:array<string,string>,error_levels?:string[]}>
|
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 [
|
|
|
|
'implicitFourthLoop' => [
|
|
|
|
'<?php
|
|
|
|
function test(): int {
|
|
|
|
$x = 0;
|
|
|
|
$y = 1;
|
|
|
|
$z = 2;
|
|
|
|
for ($i = 0; $i < 3; $i++) {
|
|
|
|
$x = $y;
|
|
|
|
$y = $z;
|
|
|
|
$z = 5;
|
|
|
|
}
|
|
|
|
return $x;
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'falseToBoolInContinueAndBreak' => [
|
|
|
|
'<?php
|
|
|
|
$a = false;
|
|
|
|
|
|
|
|
for ($i = 0; $i < 4; $i++) {
|
|
|
|
$j = rand(0, 10);
|
|
|
|
|
|
|
|
if ($j === 2) {
|
|
|
|
$a = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($j === 3) {
|
|
|
|
$a = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
'assignments' => [
|
|
|
|
'$a' => 'bool',
|
|
|
|
],
|
|
|
|
],
|
|
|
|
'forLoopwithOKChange' => [
|
|
|
|
'<?php
|
|
|
|
$j = 5;
|
|
|
|
for ($i = $j; $i < 4; $i++) {
|
|
|
|
$j = 9;
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'preventNegativeZeroScrewingThingsUp' => [
|
|
|
|
'<?php
|
|
|
|
function foo() : void {
|
|
|
|
$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' => [
|
|
|
|
'<?php
|
|
|
|
for (;;) {
|
|
|
|
$a = "hello";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
for (;;) {
|
|
|
|
$b = 5;
|
|
|
|
break;
|
|
|
|
}',
|
|
|
|
'assertions' => [
|
|
|
|
'$a' => 'string',
|
|
|
|
'$b' => 'int',
|
|
|
|
],
|
|
|
|
],
|
|
|
|
'continueOutsideLoop' => [
|
|
|
|
'<?php
|
|
|
|
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' => [
|
|
|
|
'<?php
|
|
|
|
for ($i = 0; $i < 5; $i++);
|
|
|
|
echo $i;',
|
|
|
|
],
|
2020-01-04 19:05:23 +01:00
|
|
|
'nestedEchoAfterFor' => [
|
|
|
|
'<?php
|
|
|
|
for ($i = 1; $i < 2; $i++) {
|
|
|
|
for ($j = 1; $j < 2; $j++) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
echo $i * $j;'
|
|
|
|
],
|
2020-07-03 17:13:44 +02:00
|
|
|
'reconcileOuterVars' => [
|
|
|
|
'<?php
|
|
|
|
for ($i = 0; $i < 2; $i++) {
|
|
|
|
if ($i === 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}'
|
|
|
|
],
|
2020-08-17 21:47:39 +02:00
|
|
|
'noException' => [
|
|
|
|
'<?php
|
|
|
|
/**
|
|
|
|
* @param list<int> $arr
|
|
|
|
*/
|
|
|
|
function cartesianProduct(array $arr) : void {
|
|
|
|
for ($i = 20; $arr[$i] === 5 && $i > 0; $i--) {}
|
|
|
|
}'
|
|
|
|
],
|
2018-11-10 01:54:10 +01:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-03-01 21:55:20 +01:00
|
|
|
* @return iterable<string,array{string,error_message:string,2?:string[],3?:bool,4?:string}>
|
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' => [
|
|
|
|
'<?php
|
|
|
|
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' => [
|
|
|
|
'<?php
|
|
|
|
for ($i = 0; $i < 4; $i++) {
|
|
|
|
foreach ([1, 2, 3] as $i) {}
|
|
|
|
}',
|
|
|
|
'error_message' => 'LoopInvalidation',
|
|
|
|
],
|
|
|
|
'forInfiniteNoBreak' => [
|
|
|
|
'<?php
|
|
|
|
for (;;) {
|
|
|
|
$a = "hello";
|
|
|
|
}
|
|
|
|
|
|
|
|
echo $a;',
|
|
|
|
'error_message' => 'UndefinedGlobalVariable',
|
|
|
|
],
|
2020-01-04 19:05:23 +01:00
|
|
|
'nestedEchoAfterFor' => [
|
|
|
|
'<?php
|
|
|
|
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
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|