Skip to content

Commit 30d2218

Browse files
PrinsFrankondrejmirtes
authored andcommitted
Update RangeFunctionReturnTypeExtension to include known integer ranges
1 parent 091c5a7 commit 30d2218

2 files changed

Lines changed: 32 additions & 6 deletions

File tree

‎src/Type/Php/RangeFunctionReturnTypeExtension.php‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@
2828
use function abs;
2929
use function count;
3030
use function floor;
31+
use function intdiv;
3132
use function is_array;
3233
use function is_finite;
3334
use function is_numeric;
3435
use function is_string;
3536
use function max;
3637
use function min;
3738
use function range;
39+
use const PHP_INT_MAX;
3840

3941
#[AutowiredService]
4042
final class RangeFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
@@ -186,11 +188,13 @@ private static function getLongRangeType(
186188
&& $endConstant instanceof ConstantIntegerType
187189
&& $stepConstant instanceof ConstantIntegerType
188190
) {
191+
$low = min($startConstant->getValue(), $endConstant->getValue());
192+
$high = max($startConstant->getValue(), $endConstant->getValue());
193+
189194
return self::getNonEmptyListOfType(
190-
IntegerRangeType::fromInterval(
191-
min($startConstant->getValue(), $endConstant->getValue()),
192-
max($startConstant->getValue(), $endConstant->getValue()),
193-
),
195+
IntegerRangeType::fromInterval($low, $high),
196+
// the keys stay unbounded when $high - $low overflows
197+
$low >= 0 || $high <= PHP_INT_MAX + $low ? intdiv($high - $low, abs($stepConstant->getValue())) : null,
194198
);
195199
}
196200

@@ -207,11 +211,11 @@ private static function getLongRangeType(
207211
);
208212
}
209213

210-
private static function getNonEmptyListOfType(Type $type): IntersectionType
214+
private static function getNonEmptyListOfType(Type $type, ?int $maxKey = null): IntersectionType
211215
{
212216
return new IntersectionType([
213217
new ArrayType(
214-
IntegerRangeType::createAllGreaterThanOrEqualTo(0),
218+
IntegerRangeType::fromInterval(0, $maxKey),
215219
$type,
216220
),
217221
new NonEmptyArrayType(),

‎tests/PHPStan/Analyser/nsrt/range-array-count-limit.php‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,25 @@ function doFoo(): void
1313
assertType('non-empty-list<float>', range(0, 100000000, 0.5));
1414
assertType('non-empty-list<int<0, 300>>', range(0, 300));
1515
}
16+
17+
function keys(): void
18+
{
19+
foreach (range(0, 50) as $key => $value) {
20+
assertType('int<0, 50>', $key);
21+
}
22+
foreach (range(10, 1000, 7) as $key => $value) {
23+
assertType('int<0, 141>', $key);
24+
}
25+
foreach (range(1000, 10, -7) as $key => $value) {
26+
assertType('int<0, 141>', $key);
27+
}
28+
foreach (range(0, -100000000) as $key => $value) {
29+
assertType('int<0, 100000000>', $key);
30+
}
31+
foreach (range(0, 2000000000, 3) as $key => $value) {
32+
assertType('int<0, 666666666>', $key);
33+
}
34+
foreach (range(PHP_INT_MIN, PHP_INT_MAX) as $key => $value) {
35+
assertType('int<0, max>', $key);
36+
}
37+
}

0 commit comments

Comments
 (0)