Skip to content

Commit cda0159

Browse files
ondrejmirtesclaude
andcommitted
Infer rand() with $min greater than $max as the swapped range
rand() swaps the arguments when $min is greater than $max, so rand(5, 1) returns an int between 1 and 5, but it was inferred as never like random_int() and mt_rand(), which throw ValueError for such arguments. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dhb4ssXuKVWcVBcNFpsdn7
1 parent d52db07 commit cda0159

2 files changed

Lines changed: 26 additions & 0 deletions

File tree

‎src/Type/Php/RandomIntFunctionReturnTypeExtension.php‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
1111
use PHPStan\Type\IntegerRangeType;
1212
use PHPStan\Type\Type;
13+
use PHPStan\Type\TypeCombinator;
1314
use PHPStan\Type\UnionType;
1415
use function array_map;
1516
use function assert;
@@ -41,6 +42,13 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
4142
$minType = $scope->getType($args[0]->value)->toInteger();
4243
$maxType = $scope->getType($args[1]->value)->toInteger();
4344

45+
if ($functionReflection->getName() === 'rand') {
46+
// rand() swaps the arguments when $min is greater than $max
47+
$bothType = TypeCombinator::union($minType, $maxType);
48+
49+
return $this->createRange($bothType, $bothType);
50+
}
51+
4452
return $this->createRange($minType, $maxType);
4553
}
4654

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace RandSwappedArguments;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
function doFoo(int $i): void
8+
{
9+
assertType('int<1, 5>', rand(5, 1));
10+
assertType('int<-5, 5>', rand(5, -5));
11+
assertType('int<1, 10>', rand(10, random_int(1, 5)));
12+
13+
\assert($i >= 0);
14+
assertType('int<0, max>', rand($i, 0));
15+
16+
assertType('*NEVER*', mt_rand(5, 1));
17+
assertType('*NEVER*', random_int(5, 1));
18+
}

0 commit comments

Comments
 (0)