Skip to content

Commit 1d219a5

Browse files
zonuexeclaude
authored 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 20f4804 commit 1d219a5

2 files changed

Lines changed: 105 additions & 0 deletions

File tree

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+
}

‎tests/PHPStan/Type/IntegerRangeTypeTest.php‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHPStan\Type;
44

55
use PHPStan\Testing\PHPStanTestCase;
6+
use PHPUnit\Framework\Attributes\DataProvider;
67
use const PHP_INT_MAX;
78

89
class IntegerRangeTypeTest extends PHPStanTestCase
@@ -20,4 +21,65 @@ public function testCreateFromFloatPastTheIntRange(): void
2021
$this->assertSame('*NEVER*', IntegerRangeType::createAllGreaterThanOrEqualTo($pastMax)->describe(VerbosityLevel::precise()));
2122
}
2223

24+
public static function dataCreateFromFloat(): iterable
25+
{
26+
// 2**63 is PHP_INT_MAX + 1, the smallest float greater than every int
27+
yield '2**63' => [
28+
9.2233720368547758E18,
29+
'int',
30+
'int',
31+
'*NEVER*',
32+
'*NEVER*',
33+
];
34+
35+
// the largest float smaller than 2**63
36+
yield '2**63 - 1024' => [
37+
9.2233720368547748E18,
38+
'int<min, 9223372036854774783>',
39+
'int<min, 9223372036854774784>',
40+
'int<9223372036854774785, max>',
41+
'int<9223372036854774784, max>',
42+
];
43+
44+
// -2**63 is exactly PHP_INT_MIN
45+
yield '-2**63' => [
46+
-9.2233720368547758E18,
47+
'*NEVER*',
48+
'-9223372036854775808',
49+
'int<-9223372036854775807, max>',
50+
'int',
51+
];
52+
53+
// the largest float smaller than -2**63
54+
yield '-2**63 - 2048' => [
55+
-9.2233720368547779E18,
56+
'*NEVER*',
57+
'*NEVER*',
58+
'int',
59+
'int',
60+
];
61+
}
62+
63+
#[DataProvider('dataCreateFromFloat')]
64+
public function testCreateFromFloat(
65+
float $value,
66+
string $expectedSmallerThan,
67+
string $expectedSmallerThanOrEqualTo,
68+
string $expectedGreaterThan,
69+
string $expectedGreaterThanOrEqualTo,
70+
): void
71+
{
72+
$this->assertSame([
73+
'createAllSmallerThan' => $expectedSmallerThan,
74+
'createAllSmallerThanOrEqualTo' => $expectedSmallerThanOrEqualTo,
75+
'createAllGreaterThan' => $expectedGreaterThan,
76+
'createAllGreaterThanOrEqualTo' => $expectedGreaterThanOrEqualTo,
77+
], [
78+
'createAllSmallerThan' => IntegerRangeType::createAllSmallerThan($value)->describe(VerbosityLevel::precise()),
79+
'createAllSmallerThanOrEqualTo' => IntegerRangeType::createAllSmallerThanOrEqualTo($value)->describe(VerbosityLevel::precise()),
80+
'createAllGreaterThan' => IntegerRangeType::createAllGreaterThan($value)->describe(VerbosityLevel::precise()),
81+
'createAllGreaterThanOrEqualTo' => IntegerRangeType::createAllGreaterThanOrEqualTo($value)->describe(VerbosityLevel::precise()),
82+
]);
83+
}
84+
2385
}

0 commit comments

Comments
 (0)