1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-11 16:59:45 +01:00
psalm/tests/IntRangeTest.php

1146 lines
38 KiB
PHP
Raw Normal View History

2021-07-30 21:44:51 +02:00
<?php
2023-10-19 13:12:06 +02:00
declare(strict_types=1);
2021-07-30 21:44:51 +02:00
namespace Psalm\Tests;
use Psalm\Internal\Type\TypeCombiner;
2021-12-04 21:55:53 +01:00
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
use Psalm\Type\Atomic\TIntRange;
use Psalm\Type\Atomic\TLiteralInt;
2021-12-04 21:55:53 +01:00
2021-07-30 21:44:51 +02:00
class IntRangeTest extends TestCase
{
2021-12-04 21:55:53 +01:00
use InvalidCodeAnalysisTestTrait;
use ValidCodeAnalysisTestTrait;
2021-07-30 21:44:51 +02:00
public function testCombineIntRangeDoesntAffectOriginal(): void
{
$range = new TIntRange(5, 10);
TypeCombiner::combine([new TLiteralInt(1), new TLiteralInt(50), $range]);
$this->assertEquals(5, $range->min_bound);
$this->assertEquals(10, $range->max_bound);
}
2021-07-30 21:44:51 +02:00
public function providerValidCodeParse(): iterable
{
return [
'intRangeContained' => [
'code' => '<?php
2021-07-30 21:44:51 +02:00
/**
* @param int<1,12> $a
* @return int<-1, max>
*/
function scope(int $a){
return $a;
}',
],
'positiveIntRange' => [
'code' => '<?php
2021-07-30 21:44:51 +02:00
/**
* @param int<1,12> $a
* @return positive-int
*/
function scope(int $a){
return $a;
}',
],
'nonNegativeIntRange' => [
'code' => '<?php
/**
* @param int<0,12> $a
* @return non-negative-int
*/
function scope(int $a){
return $a;
}',
],
'negativeIntRange' => [
'code' => '<?php
/**
* @param int<-12,-1> $a
* @return negative-int
*/
function scope(int $a){
return $a;
}',
],
'nonPositiveIntRange' => [
'code' => '<?php
/**
* @param int<-12,0> $a
* @return non-positive-int
*/
function scope(int $a){
return $a;
}',
],
2021-07-30 21:44:51 +02:00
'intRangeToInt' => [
'code' => '<?php
2021-07-30 21:44:51 +02:00
/**
* @param int<1,12> $a
* @return int
*/
function scope(int $a){
return $a;
}',
],
2021-08-04 20:07:58 +02:00
'intReduced' => [
'code' => '<?php
2021-08-04 20:07:58 +02:00
function getInt(): int{return 0;}
2021-09-01 21:26:56 +02:00
$a = $b = $c = getInt();
2021-08-04 20:07:58 +02:00
assert($a >= 500);
assert($a < 5000);
assert($b >= -5000);
2021-09-01 21:26:56 +02:00
assert($b < -501);
assert(-60 > $c);
assert(-500 < $c);',
2021-08-04 20:07:58 +02:00
'assertions' => [
'$a===' => 'int<500, 4999>',
2021-09-01 21:26:56 +02:00
'$b===' => 'int<-5000, -502>',
'$c===' => 'int<-499, -61>',
2022-12-18 17:15:15 +01:00
],
2021-08-04 20:07:58 +02:00
],
'complexAssertions' => [
'code' => '<?php
function getInt(): int{return 0;}
$a = getInt();
assert($a >= 495 + 5);
$b = 5000;
assert($a < $b);
',
'assertions' => [
'$a===' => 'int<500, 4999>',
2022-12-18 17:15:15 +01:00
],
],
2021-09-05 18:02:42 +02:00
'negatedAssertions' => [
'code' => '<?php
2021-09-05 18:02:42 +02:00
function getInt(): int{return 0;}
$a = $b = $c = $d = $e = $f = $g = $h = $i = $j = $k = $l = $m = $n = $o = $p = getInt();
//>
if($a > 10){
die();
}
if($b > -10){
die();
}
//<
if($c < 500){
die();
}
if($d < -500){
die();
}
//>=
if($e >= 10){
die();
}
if($f >= -10){
die();
}
//<=
if($g <= 500){
die();
}
if($h <= -500){
die();
}
//>
if(10 > $i){
die();
}
if(-10 > $j){
die();
}
//<
if(500 < $k){
die();
}
if(-500 < $l){
die();
}
//>=
if(10 >= $m){
die();
}
if(-10 >= $n){
die();
}
//<=
if(500 <= $o){
die();
}
if(-500 <= $p){
die();
}
//inverse
',
'assertions' => [
'$a===' => 'int<min, 10>',
'$b===' => 'int<min, -10>',
'$c===' => 'int<500, max>',
'$d===' => 'int<-500, max>',
'$e===' => 'int<min, 9>',
'$f===' => 'int<min, -11>',
'$g===' => 'int<501, max>',
'$h===' => 'int<-499, max>',
'$i===' => 'int<10, max>',
'$j===' => 'int<-10, max>',
'$k===' => 'int<min, 500>',
'$l===' => 'int<min, -500>',
'$m===' => 'int<11, max>',
'$n===' => 'int<-9, max>',
'$o===' => 'int<min, 499>',
'$p===' => 'int<min, -501>',
2022-12-18 17:15:15 +01:00
],
2021-09-05 18:02:42 +02:00
],
2021-08-04 20:07:58 +02:00
'intOperations' => [
'code' => '<?php
2021-08-04 20:07:58 +02:00
function getInt(): int{return 0;}
$a = getInt();
assert($a >= 500);
assert($a < 5000);
$b = $a % 10;
$c = $a ** 2;
$d = $a - 5;
$e = $a * 1;',
'assertions' => [
'$b===' => 'int<0, 9>',
2021-09-12 15:58:09 +02:00
'$c===' => 'int<1, max>',
2021-08-04 20:07:58 +02:00
'$d===' => 'int<495, 4994>',
2022-12-18 17:15:15 +01:00
'$e===' => 'int<500, 4999>',
],
2021-08-04 20:07:58 +02:00
],
2021-09-12 20:53:11 +02:00
'mod' => [
'code' => '<?php
2021-09-12 20:53:11 +02:00
function getInt(): int{return 0;}
$a = $b = $c = $d = getInt();
assert($a >= 20);//positive range
assert($b <= -20);//negative range
/** @var int<0, 0> $c */; // 0 range
assert($d >= -100);// mixed range
assert($d <= 100);// mixed range
/** @var int<5, 5> $e */; // 5 range
$f = $a % $e;
$g = $b % $e;
$h = $d % $e;
$i = -3 % $a;
$j = -3 % $b;
2021-10-13 19:37:47 +02:00
/** @psalm-suppress NoValue */
2021-09-12 20:53:11 +02:00
$k = -3 % $c;
$l = -3 % $d;
$m = 3 % $a;
$n = 3 % $b;
2021-10-13 19:37:47 +02:00
/** @psalm-suppress NoValue */
2021-09-12 20:53:11 +02:00
$o = 3 % $c;
$p = 3 % $d;
2021-10-13 19:37:47 +02:00
/** @psalm-suppress NoValue */
2021-09-12 20:53:11 +02:00
$q = $a % 0;
$r = $a % 3;
$s = $a % -3;
2021-10-13 19:37:47 +02:00
/** @psalm-suppress NoValue */
2021-09-12 20:53:11 +02:00
$t = $b % 0;
$u = $b % 3;
$v = $b % -3;
2021-10-13 19:37:47 +02:00
/** @psalm-suppress NoValue */
2021-09-12 20:53:11 +02:00
$w = $c % 0;
$x = $c % 3;
$y = $c % -3;
2021-10-13 19:37:47 +02:00
/** @psalm-suppress NoValue */
2021-09-12 20:53:11 +02:00
$z = $d % 0;
$aa = $d % 3;
$ab = $d % -3;
',
'assertions' => [
'$f===' => 'int<0, 4>',
'$g===' => 'int<-4, 0>',
'$h===' => 'int<-4, 4>',
'$i===' => 'int<min, 0>',
'$j===' => 'int<min, 0>',
'$k===' => 'mixed',
2021-09-12 20:53:11 +02:00
'$l===' => 'int',
'$m===' => 'int<0, max>',
'$n===' => 'int<min, 0>',
'$o===' => 'mixed',
2021-09-12 20:53:11 +02:00
'$p===' => 'int',
'$q===' => 'mixed',
2021-09-12 20:53:11 +02:00
'$r===' => 'int<0, 2>',
'$s===' => 'int<-2, 0>',
'$t===' => 'mixed',
2021-09-12 20:53:11 +02:00
'$u===' => 'int<-2, 0>',
'$v===' => 'int<2, 0>',
'$w===' => 'mixed',
2021-09-12 20:53:11 +02:00
'$x===' => 'int<0, 2>',
'$y===' => 'int<-2, 0>',
'$z===' => 'mixed',
2021-09-12 20:53:11 +02:00
'$aa===' => 'int<-2, 2>',
'$ab===' => 'int<-2, 2>',
2022-12-18 17:15:15 +01:00
],
2021-09-12 20:53:11 +02:00
],
2021-09-12 15:58:09 +02:00
'pow' => [
'code' => '<?php
2021-09-12 15:58:09 +02:00
function getInt(): int{return 0;}
$a = $b = $c = $d = getInt();
assert($a >= 2);//positive range
assert($b <= -2);//negative range
/** @var int<0, 0> $c */; // 0 range
assert($d >= -100);// mixed range
assert($d <= 100);// mixed range
$e = 0 ** $a;
$f = 0 ** $b;
$g = 0 ** $c;
$h = 0 ** $d;
2021-09-16 19:21:11 +02:00
$i = (-2) ** $a;
$j = (-2) ** $b;
$k = (-2) ** $c;
$l = (-2) ** $d;
2021-09-12 15:58:09 +02:00
$m = 2 ** $a;
$n = 2 ** $b;
$o = 2 ** $c;
$p = 2 ** $d;
$q = $a ** 0;
$r = $a ** 2;
$s = $a ** -2;
$t = $b ** 0;
$u = $b ** 2;
$v = $b ** -2;
$w = $c ** 0;
$x = $c ** 2;
$y = $c ** -2;
$z = $d ** 0;
$aa = $d ** 2;
$ab = $d ** -2;
',
'assertions' => [
'$e===' => '0',
2021-10-13 19:37:47 +02:00
'$f===' => 'float',
2021-09-12 15:58:09 +02:00
'$g===' => '1',
2021-10-13 19:37:47 +02:00
'$h===' => '0|1|float',
2021-09-16 19:21:11 +02:00
'$i===' => 'int',
2021-09-12 15:58:09 +02:00
'$j===' => 'float',
'$k===' => '-1',
'$l===' => 'float|int',
'$m===' => 'int<1, max>',
'$n===' => 'float',
'$o===' => '1',
'$p===' => 'float|int',
'$q===' => '1',
'$r===' => 'int<1, max>',
'$s===' => 'float',
'$t===' => '-1',
2021-09-16 19:21:11 +02:00
'$u===' => 'int<1, max>',
2021-09-12 15:58:09 +02:00
'$v===' => 'float',
'$w===' => '1',
'$x===' => '0',
2021-10-13 19:37:47 +02:00
'$y===' => 'float',
2021-09-12 15:58:09 +02:00
'$z===' => '1',
2021-09-16 19:21:11 +02:00
'$aa===' => 'int<1, max>',
2021-09-12 15:58:09 +02:00
'$ab===' => 'float',
2022-12-18 17:15:15 +01:00
],
2021-09-12 15:58:09 +02:00
],
2021-09-12 13:44:02 +02:00
'multiplications' => [
'code' => '<?php
2021-09-12 13:44:02 +02:00
function getInt(): int{return 0;}
$a = $b = $c = $d = $e = $f = $g = $h = $i = $j = $k = $l = $m = $n = $o = $p = getInt();
assert($b <= -2);
assert($c <= 2);
assert($d >= -2);
assert($e >= 2);
assert($f >= -2);
assert($f <= 2);
$g = $a * $b;
$h = $a * $c;
$i = $a * $d;
$j = $a * $e;
$k = $a * $f;
$l = $b * $b;
$m = $b * $c;
$n = $b * $d;
$o = $b * $e;
$p = $b * $f;
$q = $c * $c;
$r = $c * $d;
$s = $c * $e;
$t = $c * $f;
$u = $d * $d;
$v = $d * $e;
$w = $d * $f;
$x = $e * $e;
$y = $d * $f;
$z = $f * $f;
',
'assertions' => [
2021-09-12 15:58:09 +02:00
'$g===' => 'int',
'$h===' => 'int',
'$i===' => 'int',
'$j===' => 'int',
'$k===' => 'int',
2021-09-12 13:44:02 +02:00
'$l===' => 'int<4, max>',
2021-09-12 15:58:09 +02:00
'$m===' => 'int',
'$n===' => 'int',
2021-09-12 13:44:02 +02:00
'$o===' => 'int<min, -4>',
2021-09-12 15:58:09 +02:00
'$p===' => 'int',
'$q===' => 'int',
'$r===' => 'int',
'$s===' => 'int',
'$t===' => 'int',
'$u===' => 'int',
'$v===' => 'int',
'$w===' => 'int',
2021-09-12 13:44:02 +02:00
'$x===' => 'int<4, max>',
2021-09-12 15:58:09 +02:00
'$y===' => 'int',
2021-09-12 13:44:02 +02:00
'$z===' => 'int<-4, 4>',
2022-12-18 17:15:15 +01:00
],
2021-09-12 13:44:02 +02:00
],
2021-08-04 20:07:58 +02:00
'SKIPPED-intLoopPositive' => [
'code' => '<?php
2021-08-04 20:07:58 +02:00
//skipped, int range in loops not supported yet
for($i = 0; $i < 10; $i++){
}',
'assertions' => [
2022-12-18 17:15:15 +01:00
'$i===' => 'int<0, 9>',
],
2021-08-04 20:07:58 +02:00
],
'SKIPPED-intLoopNegative' => [
'code' => '<?php
2021-08-04 20:07:58 +02:00
//skipped, int range in loops not supported yet
for($i = 10; $i > 1; $i--){
}',
'assertions' => [
2022-12-18 17:15:15 +01:00
'$i===' => 'int<2, 10>',
],
2021-08-04 20:07:58 +02:00
],
'integrateExistingArrayPositive' => [
'code' => '<?php
2021-08-04 20:07:58 +02:00
/** @return int<5, max> */
function getInt()
{
return 7;
}
$_arr = ["a", "b", "c"];
$a = getInt();
$_arr[$a] = 12;',
'assertions' => [
2022-12-18 17:15:15 +01:00
'$_arr===' => "non-empty-array<int<0, max>, 'a'|'b'|'c'|12>",
],
2021-08-04 20:07:58 +02:00
],
'integrateExistingArrayNegative' => [
'code' => '<?php
2021-08-04 20:07:58 +02:00
/** @return int<min, -1> */
function getInt()
{
return -2;
}
$_arr = ["a", "b", "c"];
$a = getInt();
$_arr[$a] = 12;',
'assertions' => [
2022-12-18 17:15:15 +01:00
'$_arr===' => "non-empty-array<int<min, 2>, 'a'|'b'|'c'|12>",
],
2021-08-04 20:07:58 +02:00
],
'SKIPPED-statementsInLoopAffectsEverything' => [
'code' => '<?php
2021-08-04 20:07:58 +02:00
//skipped, int range in loops not supported yet
$remainder = 1;
for ($i = 0; $i < 5; $i++) {
if ($remainder) {
$remainder--;
}
}',
'assertions' => [
2022-12-18 17:15:15 +01:00
'$remainder===' => 'int<min, 1>',
],
2021-08-04 20:07:58 +02:00
],
'IntRangeRestrictWhenUntouched' => [
'code' => '<?php
2021-08-04 20:07:58 +02:00
foreach ([1, 2, 3] as $i) {
if ($i > 1) {
takesInt($i);
}
}
/** @psalm-param int<2, 3> $i */
function takesInt(int $i): void{
return;
}',
],
'intRangeExpandedByLoop' => [
'code' => '<?php
for ($i = 0; $i < 10; $i++) {
takesInt($i);
}
for (; $i < 20; $i++) {
takesInt($i);
}
/** @psalm-param int<0, 20> $i */
function takesInt(int $i): void{
return;
}',
],
'statementsInLoopPreserveNonNegativeIntRange' => [
'code' => '<?php
$sum = 0;
foreach ([-6, 0, 2] as $i) {
if ($i > 0) {
$sum += $i;
}
}
takesNonNegativeInt($sum);
/** @psalm-param int<0, max> $i */
function takesNonNegativeInt(int $i): void{
return;
}',
],
'wrongLoopAssertion' => [
'code' => '<?php
2021-08-04 20:07:58 +02:00
function a(): array {
$type_tokens = getArray();
for ($i = 0, $l = rand(0,100); $i < $l; ++$i) {
if ($i > 0 && rand(0,1)) {
continue;
}
$type_tokens[$i] = "";
if ($i > 1) {
2021-08-04 20:07:58 +02:00
$type_tokens[$i - 2];
}
}
return [];
}
/** @return array<int, string> */
Add support for strict arrays, fix type alias intersection, fix array_is_list assertion on non-lists (#8395) * Immutable CodeLocation * Remove excess clones * Remove external clones * Remove leftover clones * Fix final clone issue * Immutable storages * Refactoring * Fixes * Fixes * Fix * Fix * Fixes * Simplify * Fixes * Fix * Fixes * Update * Fix * Cache global types * Fix * Update * Update * Fixes * Fixes * Refactor * Fixes * Fix * Fix * More caching * Fix * Fix * Update * Update * Fix * Fixes * Update * Refactor * Update * Fixes * Break one more test * Fix * FIx * Fix * Fix * Fix * Fix * Improve performance and readability * Equivalent logic * Fixes * Revert * Revert "Revert" This reverts commit f9175100c8452c80559234200663fd4c4f4dd889. * Fix * Fix reference bug * Make default TypeVisitor immutable * Bugfix * Remove clones * Partial refactoring * Refactoring * Fixes * Fix * Fixes * Fixes * cs-fix * Fix final bugs * Add test * Misc fixes * Update * Fixes * Experiment with removing different property * revert "Experiment with removing different property" This reverts commit ac1156e077fc4ea633530d51096d27b6e88bfdf9. * Uniform naming * Uniform naming * Hack hotfix * Clean up $_FILES ref #8621 * Undo hack, try fixing properly * Helper method * Remove redundant call * Partially fix bugs * Cleanup * Change defaults * Fix bug * Fix (?, hope this doesn't break anything else) * cs-fix * Review fixes * Bugfix * Bugfix * Improve logic * Add support for list{} and callable-list{} types, properly implement array_is_list assertions (fixes #8389) * Default to sealed arrays * Fix array_merge bug * Fixes * Fix * Sealed type checks * Properly infer properties-of and get_object_vars on final classes * Fix array_map zipping * Fix tests * Fixes * Fixes * Fix more stuff * Recursively resolve type aliases * Fix typo * Fixes * Fix array_is_list assertion on keyed array * Add BC docs * Fixes * fix * Update * Update * Update * Update * Seal arrays with count assertions * Fix #8528 * Fix * Update * Improve sealed array foreach logic * get_object_vars on template properties * Fix sealed array assertion reconciler logic * Improved reconciler * Add tests * Single source of truth for test types * Fix tests * Fixup tests * Fixup tests * Fixup tests * Update * Fix tests * Fix tests * Final fixes * Fixes * Use list syntax only when needed * Fix tests * Cs-fix * Update docs * Update docs * Update docs * Update docs * Update docs * Document missing types * Update docs * Improve class-string-map docs * Update * Update * I love working on psalm :) * Keep arrays unsealed by default * Fixup tests * Fix syntax mistake * cs-fix * Fix typo * Re-import missing types * Keep strict types only in return types * argc/argv fixes * argc/argv fixes * Fix test * Comment-out valinor code, pinging @romm pls merge https://github.com/CuyZ/Valinor/pull/246 so we can add valinor to the psalm docs :)
2022-11-05 22:34:42 +01:00
function getArray(): array {
2021-08-04 20:07:58 +02:00
return [];
2022-12-18 17:15:15 +01:00
}',
],
'IntRangeContainedInMultipleInt' => [
'code' => '<?php
$_arr = [];
foreach ([0, 1] as $i) {
$_arr[$i] = 1;
}
/** @var int<0,1> $j */
$j = 0;
2022-12-18 17:15:15 +01:00
echo $_arr[$j];',
],
'modulo' => [
'code' => '<?php
function getInt(): int{return 0;}
$a = getInt();
$b = $a % 10;
assert($a > 0);
$c = $a % 10;
$d = $a % $a;',
'assertions' => [
'$b===' => 'int<-9, 9>',
'$c===' => 'int<0, 9>',
2022-12-18 17:15:15 +01:00
'$d===' => 'int<0, max>',
],
],
2021-09-06 23:32:47 +02:00
'minus' => [
'code' => '<?php
2021-09-06 23:32:47 +02:00
function getInt(): int{return 0;}
$a = $b = $d = $e = getInt();
2021-09-06 23:32:47 +02:00
assert($a > 5);
assert($a <= 10);
assert($b > -10);
assert($b <= 100);
$c = $a - $b;
$f = $a - $d;
assert($e > 0);
$g = $a - $e;
',
2021-09-06 23:32:47 +02:00
'assertions' => [
'$c===' => 'int<-94, 19>',
'$f===' => 'int<min, max>',
'$g===' => 'int<min, 9>',
2021-09-06 23:32:47 +02:00
],
],
2021-09-07 21:42:49 +02:00
'bits' => [
'code' => '<?php
2021-09-07 21:42:49 +02:00
function getInt(): int{return 0;}
$a = $b = $c = getInt();
assert($a > 5);
assert($b <= 6);
$d = $a ^ $b;
$e = $a & $b;
$f = $a | $b;
$g = $a << $b;
$h = $a >> $b;
',
'assertions' => [
'$d===' => 'int',
'$e===' => 'int',
'$f===' => 'int',
2021-09-12 08:53:08 +02:00
'$g===' => 'int',
'$h===' => 'int',
2021-09-07 21:42:49 +02:00
],
],
'UnaryMinus' => [
'code' => '<?php
function getInt(): int{return 0;}
$a = $c = $e = getInt();
assert($a > 5);
$b = -$a;
assert($c > 0);
$d = -$c;
assert($e > 5);
assert($e < 10);
$f = -$e;
',
'assertions' => [
'$b===' => 'int<min, -6>',
2021-09-12 21:25:28 +02:00
'$d===' => 'int<min, -1>',
'$f===' => 'int<-9, -6>',
],
],
'countOnKeyedArray' => [
'code' => '<?php
$conf = [
"K",
"M",
"G",
];
$i = count($conf) - 1;
',
'assertions' => [
'$i===' => '2',
],
],
2021-09-11 13:59:30 +02:00
'intersections' => [
'code' => '<?php
/** @return int<0, 10> */
function getInt(): int{ return rand(0, 10); }
2021-09-11 13:59:30 +02:00
$a = getInt();
$b = -$a;
$c = null;
if($b === $a){
//$b and $a should intersect at 0, so $c should be 0
$c = $b;
}
',
'assertions' => [
'$c===' => 'int<0, 0>|null',
],
],
'minMax' => [
'code' => '<?php
function getInt(): int{return 0;}
$a = $b = $c = $d = $e = getInt();
assert($b > 10);
assert($c < -15);
assert($d === 20);
assert($e > 0);
$f = min($a, $b, $c, $d);
$g = min($b, $c, $d);
$h = min($d, $e);
$i = max($b, $c, $d);
$j = max($d, $e);
$k = max($e, 40);
$l = min($a, ...[$b, $c], $d);
$m = max(...[$a, ...[$b, $c]], $d);
',
'assertions' => [
'$f===' => 'int<min, -16>',
'$g===' => 'int<min, -16>',
'$h===' => 'int<1, 20>',
'$i===' => 'int<20, max>',
'$j===' => 'int<20, max>',
'$k===' => 'int<40, max>',
'$l===' => 'int<min, -16>',
'$m===' => 'int<20, max>',
],
],
2021-10-24 13:59:21 +02:00
'dontCrashOnFalsy' => [
'code' => '<?php
2021-10-24 13:59:21 +02:00
function doAnalysis(): void
{
/** @var int<3, max> $shuffle_count */
$shuffle_count = 1;
/** @var list<string> $file_paths */
$file_paths = [];
/** @var int<0, max> $count */
$count = 1;
/** @var int<0, max> $middle */
2021-10-24 13:59:21 +02:00
$middle = 1;
/** @var int<0, max> $remainder */
$remainder = 1;
for ($i = 0; $i < $shuffle_count; $i++) {
for ($j = 0; $j < $middle; $j++) {
if ($j * $shuffle_count + $i < $count) {
echo $file_paths[$j * $shuffle_count + $i];
}
}
if ($remainder) {
echo $file_paths[$middle * $shuffle_count + $remainder - 1];
$remainder--;
}
}
}',
],
'positiveIntToRangeWithInferior' => [
'code' => '<?php
/** @var positive-int $length */
$length = 0;
if ($length < 8) {
throw new \RuntimeException();
}',
'assertions' => [
'$length===' => 'int<8, max>',
],
],
'nonNegativeIntToRangeWithInferior' => [
'code' => '<?php
/** @var non-negative-int $length */
$length = 0;
if ($length < 8) {
throw new \RuntimeException();
}',
'assertions' => [
'$length===' => 'int<8, max>',
],
],
'negativeIntToRangeWithSuperior' => [
'code' => '<?php
/** @var negative-int $length */
$length = -8;
if ($length > -8) {
throw new \RuntimeException();
}',
'assertions' => [
'$length===' => 'int<min, -8>',
],
],
'nonPositiveIntToRangeWithSuperior' => [
'code' => '<?php
/** @var non-positive-int $length */
$length = -8;
if ($length > -8) {
throw new \RuntimeException();
}',
'assertions' => [
'$length===' => 'int<min, -8>',
],
],
'positiveIntToRangeWithSuperiorOrEqual' => [
'code' => '<?php
/** @var positive-int $length */
$length = 0;
if ($length >= 8) {
throw new \RuntimeException();
}',
'assertions' => [
'$length===' => 'int<1, 7>',
],
],
'nonNegativeIntToRangeWithSuperiorOrEqual' => [
'code' => '<?php
/** @var non-negative-int $length */
$length = 0;
if ($length >= 8) {
throw new \RuntimeException();
}',
'assertions' => [
'$length===' => 'int<0, 7>',
],
],
'negativeIntToRangeWithInferiorOrEqual' => [
'code' => '<?php
/** @var negative-int $length */
$length = -1;
if ($length <= -8) {
throw new \RuntimeException();
}',
'assertions' => [
'$length===' => 'int<-7, -1>',
],
],
'nonPositiveIntToRangeWithInferiorOrEqual' => [
'code' => '<?php
/** @var non-positive-int $length */
$length = -1;
if ($length <= -8) {
throw new \RuntimeException();
}',
'assertions' => [
'$length===' => 'int<-7, 0>',
],
],
'literalEquality' => [
'code' => '<?php
/** @var string $secret */
$length = strlen($secret);
if ($length > 16) {
2022-09-08 12:01:14 +02:00
throw new Exception("");
}
assert($length === 1);
',
2022-01-07 18:18:47 +01:00
'assertions' => [
'$length===' => '1',
],
],
2022-01-15 21:29:08 +01:00
'PositiveIntCombinedWithIntRange' => [
2022-01-18 23:07:35 +01:00
'code' => '<?php
2022-01-15 21:29:08 +01:00
/** @var positive-int */
$int = 1;
/** @var array<int<0, max>, int<0, max>> */
$_arr = [];
$_arr[1] = $int;
$_arr[$int] = 2;',
'assertions' => [
'$_arr===' => 'non-empty-array<int<0, max>, int<0, max>>',
],
],
'nonNegativeIntCombinedWithIntRange' => [
'code' => '<?php
/** @var non-negative-int */
$int = 1;
/** @var array<int<0, max>, int<0, max>> */
$_arr = [];
$_arr[0] = 0;
$_arr[1] = $int;
$_arr[$int] = 2;',
'assertions' => [
'$_arr===' => 'non-empty-array<int<0, max>, int<0, max>>',
],
],
'negativeIntCombinedWithIntRange' => [
'code' => '<?php
/** @var negative-int */
$int = -1;
/** @var array<int<min, -1>, int<min, -1>> */
$_arr = [];
$_arr[-1] = $int;
$_arr[$int] = -2;',
'assertions' => [
'$_arr===' => 'non-empty-array<int<min, -1>, int<min, -1>>',
],
],
'nonPositiveIntCombinedWithIntRange' => [
'code' => '<?php
/** @var non-positive-int */
$int = -1;
/** @var array<int<min, 0>, int<min, 0>> */
$_arr = [];
$_arr[0] = 0;
$_arr[-1] = $int;
$_arr[$int] = -2;',
'assertions' => [
'$_arr===' => 'non-empty-array<int<min, 0>, int<min, 0>>',
],
],
'noErrorPushingBigShapeIntoConstant' => [
'code' => '<?php
class DocComment
{
private const PSALM_ANNOTATIONS = [
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
];
}',
'assertions' => [
],
],
2022-01-24 20:55:23 +01:00
'assertionsAndNegationsOnRanges' => [
'code' => '<?php
/** @var int $int */
$int = 1;
$a = $b = $c = $d = $e = $f = $g = $h = $int;
if ($a < 1) {
$res1 = $a; //should be int<min, 0>
throw new Exception();
}
$res2 = $a; //should be int<1, max>
if ($b > 1) {
$res3 = $b; //should be int<2, max>
throw new Exception();
}
$res4 = $b; //should be int<min, 1>
if ($c <= 1) {
$res5 = $c; //should be int<min, 1>
throw new Exception();
}
$res6 = $c; //should be int<2, max>
if ($d >= 1) {
$res7 = $d; //should be int<1, max>
throw new Exception();
}
$res8 = $d; //should be int<min, 0>
if (1 < $e) {
$res9 = $e; //should be int<2, max>
throw new Exception();
}
$res10 = $e; //should be int<min, 1>
if (1 > $f) {
$res11 = $f; //should be int<min, 0>
throw new Exception();
}
$res12 = $f; //should be int<1, max>
if (1 <= $g) {
$res13 = $g; //should be int<1, max>
throw new Exception();
}
$res14 = $g; //should be int<min, 0>
if (1 >= $h) {
$res15 = $h; //should be int<min, 1>
throw new Exception();
}
$res16 = $h; //should be int<2, max>',
'assertions' => [
//'$res1' => 'int<min, 0>',
'$res2' => 'int<1, max>',
//'$res3' => 'int<2, max>',
'$res4' => 'int<min, 1>',
//'$res5' => 'int<min, 1>',
'$res6' => 'int<2, max>',
//'$res7' => 'int<1, max>',
'$res8' => 'int<min, 0>',
//'$res9' => 'int<2, max>',
'$res10' => 'int<min, 1>',
//'$res11' => 'int<min, 0>',
'$res12' => 'int<1, max>',
//'$res13' => 'int<1, max>',
'$res14' => 'int<min, 0>',
//'$res15' => 'int<min, 1>',
'$res16' => 'int<2, max>',
],
],
'arraykeyCanBeRange' => [
'code' => '<?php
/**
* @param array-key $key
* @param positive-int|non-negative-int|negative-int|non-positive-int $expected
*/
function matches($key, int $expected): bool {
if ($key !== $expected) {
return false;
}
return true;
2022-12-18 17:15:15 +01:00
}',
],
'literalArrayUnpack' => [
'code' => '<?php
/** @var int<0, 5> */
$a = 2;
/** @var int<6, 10> */
$b = 9;
/**
* @param int<0, 5> $_a
* @param int<6, 10> $_b
*/
function foo(int $_a, int $_b): void {}
foo(...[$a, $b]);
',
],
'minMaxInNamespace' => [
'code' => '<?php
namespace Foo {
/**
* @param int<0, max> $_a
* @param int<min, 0> $_b
*/
function bar(int $_a, int $_b): void {}
}
',
2022-12-18 17:15:15 +01:00
],
'rangeOverflow' => [
'code' => '<?php
$i = (int)ceil(1);
if ($i <= 9223372036854775807) {
$z = $i;
} else {
$z = null;
}
',
'assertions' => [
'$z' => 'int<min, 9223372036854775807>|null',
],
],
2021-07-30 21:44:51 +02:00
];
}
public function providerInvalidCodeParse(): iterable
{
return [
'intRangeNotContained' => [
'code' => '<?php
2021-07-30 21:44:51 +02:00
/**
* @param int<1,12> $a
* @return int<-1, 11>
* @psalm-suppress InvalidReturnStatement
*/
function scope(int $a){
return $a;
}',
'error_message' => 'InvalidReturnType',
],
'assertOutOfRange' => [
'code' => '<?php
/**
* @param int<1, 5> $a
*/
function scope(int $a): void{
assert($a === 0);
}',
'error_message' => 'DocblockTypeContradiction',
],
'assertRedundantInferior' => [
'code' => '<?php
/**
* @param int<min, 5> $a
*/
function scope(int $a): void{
assert($a < 10);
}',
'error_message' => 'RedundantConditionGivenDocblockType',
],
'assertImpossibleInferior' => [
'code' => '<?php
/**
* @param int<5, max> $a
*/
function scope(int $a): void{
assert($a < 4);
}',
'error_message' => 'DocblockTypeContradiction',
],
'maxSpecifiedAsFirst' => [
'code' => '<?php
/**
* @param int<max, 0> $a
*/
function scope(int $a){
return $a;
}',
'error_message' => 'InvalidDocblock',
],
'minSpecifiedAsSecond' => [
'code' => '<?php
/**
* @param int<0, min> $a
*/
function scope(int $a){
return $a;
}',
'error_message' => 'InvalidDocblock',
],
'unknownConstant' => [
'code' => '<?php
/**
* @param int<0, FOO> $a
*/
function scope(int $a){
return $a;
}',
'error_message' => 'InvalidDocblock',
],
'floatAsABoundary' => [
'code' => '<?php
/**
* @param int<0, 5.5> $a
*/
function scope(int $a){
return $a;
}',
'error_message' => 'InvalidDocblock',
],
'stringAsABoundary' => [
'code' => '<?php
/**
* @param int<0, "bar"> $a
*/
function scope(int $a){
return $a;
}',
'error_message' => 'InvalidDocblock',
],
'minGreaterThanMax' => [
'code' => '<?php
/**
* @param int<4, 3> $a
*/
function scope(int $a){
return $a;
}',
'error_message' => 'InvalidDocblock',
],
2021-07-30 21:44:51 +02:00
];
}
}