Skip to content

Commit 76aae52

Browse files
zonuexeclaude
andcommitted
Treat the float 2**63 as above every int in IntegerRangeType factories
A float compared with PHP_INT_MAX compares against the float 2**63, so `$value > PHP_INT_MAX` is false for exactly 2**63. createAllSmallerThan() and createAllGreaterThanOrEqualTo() then cast 2**63 to int, which emits "not representable as an int" and yields NeverType / int<min, max> instead of int / NeverType. Use `>=` like the other two factories do. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 31470ab commit 76aae52

3 files changed

Lines changed: 117 additions & 2 deletions

File tree

‎src/Type/IntegerRangeType.php‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public static function createAllSmallerThan($value): Type
9090
return self::fromInterval(null, $value, -1);
9191
}
9292

93-
if ($value > PHP_INT_MAX) {
93+
if ($value >= PHP_INT_MAX) {
9494
return new IntegerType();
9595
}
9696

@@ -160,7 +160,7 @@ public static function createAllGreaterThanOrEqualTo($value): Type
160160
return new IntegerType();
161161
}
162162

163-
if ($value > PHP_INT_MAX) {
163+
if ($value >= PHP_INT_MAX) {
164164
return new NeverType();
165165
}
166166

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace IntegerRangeFloatTwoToThe63;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
function smaller(int $i): void
8+
{
9+
if ($i < 9.2233720368547758E18) {
10+
assertType('int', $i);
11+
} else {
12+
assertType('*NEVER*', $i);
13+
}
14+
assertType('int', $i);
15+
}
16+
17+
function greaterOrEqual(int $i): void
18+
{
19+
if ($i >= 9.2233720368547758E18) {
20+
assertType('*NEVER*', $i);
21+
} else {
22+
assertType('int', $i);
23+
}
24+
assertType('int', $i);
25+
}
26+
27+
function smallerOrEqual(int $i): void
28+
{
29+
if ($i <= 9.2233720368547758E18) {
30+
assertType('int', $i);
31+
} else {
32+
assertType('*NEVER*', $i);
33+
}
34+
}
35+
36+
function greater(int $i): void
37+
{
38+
if ($i > 9.2233720368547758E18) {
39+
assertType('*NEVER*', $i);
40+
} else {
41+
assertType('int', $i);
42+
}
43+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type;
4+
5+
use PHPStan\Testing\PHPStanTestCase;
6+
use PHPUnit\Framework\Attributes\DataProvider;
7+
8+
class IntegerRangeTypeTest extends PHPStanTestCase
9+
{
10+
11+
public static function dataCreateFromFloat(): iterable
12+
{
13+
// 2**63 is PHP_INT_MAX + 1, the smallest float greater than every int
14+
yield '2**63' => [
15+
9.2233720368547758E18,
16+
'int',
17+
'int',
18+
'*NEVER*',
19+
'*NEVER*',
20+
];
21+
22+
// the largest float smaller than 2**63
23+
yield '2**63 - 1024' => [
24+
9.2233720368547748E18,
25+
'int<min, 9223372036854774783>',
26+
'int<min, 9223372036854774784>',
27+
'int<9223372036854774785, max>',
28+
'int<9223372036854774784, max>',
29+
];
30+
31+
// -2**63 is exactly PHP_INT_MIN
32+
yield '-2**63' => [
33+
-9.2233720368547758E18,
34+
'*NEVER*',
35+
'-9223372036854775808',
36+
'int<-9223372036854775807, max>',
37+
'int',
38+
];
39+
40+
// the largest float smaller than -2**63
41+
yield '-2**63 - 2048' => [
42+
-9.2233720368547779E18,
43+
'*NEVER*',
44+
'*NEVER*',
45+
'int',
46+
'int',
47+
];
48+
}
49+
50+
#[DataProvider('dataCreateFromFloat')]
51+
public function testCreateFromFloat(
52+
float $value,
53+
string $expectedSmallerThan,
54+
string $expectedSmallerThanOrEqualTo,
55+
string $expectedGreaterThan,
56+
string $expectedGreaterThanOrEqualTo,
57+
): void
58+
{
59+
$this->assertSame([
60+
'createAllSmallerThan' => $expectedSmallerThan,
61+
'createAllSmallerThanOrEqualTo' => $expectedSmallerThanOrEqualTo,
62+
'createAllGreaterThan' => $expectedGreaterThan,
63+
'createAllGreaterThanOrEqualTo' => $expectedGreaterThanOrEqualTo,
64+
], [
65+
'createAllSmallerThan' => IntegerRangeType::createAllSmallerThan($value)->describe(VerbosityLevel::precise()),
66+
'createAllSmallerThanOrEqualTo' => IntegerRangeType::createAllSmallerThanOrEqualTo($value)->describe(VerbosityLevel::precise()),
67+
'createAllGreaterThan' => IntegerRangeType::createAllGreaterThan($value)->describe(VerbosityLevel::precise()),
68+
'createAllGreaterThanOrEqualTo' => IntegerRangeType::createAllGreaterThanOrEqualTo($value)->describe(VerbosityLevel::precise()),
69+
]);
70+
}
71+
72+
}

0 commit comments

Comments
 (0)