Skip to content

Commit

Permalink
Fix IntegerRangeType overflowing
Browse files Browse the repository at this point in the history
  • Loading branch information
jlherren committed Nov 11, 2020
1 parent 3d0f6d2 commit a61a5d3
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 7 deletions.
12 changes: 6 additions & 6 deletions src/Analyser/TypeSpecifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -401,20 +401,20 @@ public function specifyTypesInCondition(
if ($expr->right instanceof Expr\PostDec) {
$result = $result->unionWith($this->createRangeTypes(
$expr->right->var,
IntegerRangeType::fromInterval($leftType->getValue() + $offset - 1, null),
IntegerRangeType::fromInterval($leftType->getValue(), null, $offset - 1),
$context
));
} elseif ($expr->right instanceof Expr\PreDec) {
$result = $result->unionWith($this->createRangeTypes(
$expr->right->var,
IntegerRangeType::fromInterval($leftType->getValue() + $offset, null),
IntegerRangeType::fromInterval($leftType->getValue(), null, $offset),
$context
));
}

$result = $result->unionWith($this->createRangeTypes(
$expr->right,
IntegerRangeType::fromInterval($leftType->getValue() + $offset, null),
IntegerRangeType::fromInterval($leftType->getValue(), null, $offset),
$context
));
}
Expand All @@ -423,20 +423,20 @@ public function specifyTypesInCondition(
if ($expr->left instanceof Expr\PostInc) {
$result = $result->unionWith($this->createRangeTypes(
$expr->left->var,
IntegerRangeType::fromInterval(null, $rightType->getValue() - $offset + 1),
IntegerRangeType::fromInterval(null, $rightType->getValue(), -$offset + 1),
$context
));
} elseif ($expr->left instanceof Expr\PreInc) {
$result = $result->unionWith($this->createRangeTypes(
$expr->left->var,
IntegerRangeType::fromInterval(null, $rightType->getValue() - $offset),
IntegerRangeType::fromInterval(null, $rightType->getValue(), -$offset),
$context
));
}

$result = $result->unionWith($this->createRangeTypes(
$expr->left,
IntegerRangeType::fromInterval(null, $rightType->getValue() - $offset),
IntegerRangeType::fromInterval(null, $rightType->getValue(), -$offset),
$context
));
}
Expand Down
18 changes: 17 additions & 1 deletion src/Type/IntegerRangeType.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,27 @@ private function __construct(?int $min, ?int $max)
}


public static function fromInterval(?int $min, ?int $max): Type
public static function fromInterval(?int $min, ?int $max, ?int $shift = null): Type
{
$min = $min ?? PHP_INT_MIN;
$max = $max ?? PHP_INT_MAX;

if ($shift !== null) {
if ($shift < 0) {
if ($max < PHP_INT_MIN - $shift) {
return new NeverType();
}
$max += $shift;
$min = $min < PHP_INT_MIN - $shift ? PHP_INT_MIN : $min + $shift;
} else {
if ($min > PHP_INT_MAX - $shift) {
return new NeverType();
}
$min += $shift;
$max = $max > PHP_INT_MAX - $shift ? PHP_INT_MAX : $max + $shift;
}
}

if ($min > $max) {
return new NeverType();
}
Expand Down
6 changes: 6 additions & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10332,6 +10332,11 @@ public function dataBug1233(): array
return $this->gatherAssertTypes(__DIR__ . '/data/bug-1233.php');
}

public function dataBug3880(): array
{
return $this->gatherAssertTypes(__DIR__ . '/data/bug-3880.php');
}

/**
* @param string $file
* @return array<string, mixed[]>
Expand Down Expand Up @@ -10511,6 +10516,7 @@ private function gatherAssertTypes(string $file): array
* @dataProvider dataBug2733
* @dataProvider dataBug3132
* @dataProvider dataBug1233
* @dataProvider dataBug3880
* @param string $assertType
* @param string $file
* @param mixed ...$args
Expand Down
40 changes: 40 additions & 0 deletions tests/PHPStan/Analyser/TypeSpecifierTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,46 @@ public function dataCondition(): array
'$n' => '~int<min, 2>',
],
],
[
new Expr\BinaryOp\Smaller(
new Variable('n'),
new LNumber(PHP_INT_MIN)
),
[], // would be nice to specify that $n cannot be an int
[],
],
[
new Expr\BinaryOp\Greater(
new Variable('n'),
new LNumber(PHP_INT_MAX)
),
[], // would be nice to specify that $n cannot be an int
[],
],
[
new Expr\BinaryOp\SmallerOrEqual(
new Variable('n'),
new LNumber(PHP_INT_MIN)
),
[
'$n' => '~int<' . (PHP_INT_MIN + 1) . ', max>',
],
[
'$n' => '~' . PHP_INT_MIN,
],
],
[
new Expr\BinaryOp\GreaterOrEqual(
new Variable('n'),
new LNumber(PHP_INT_MAX)
),
[
'$n' => '~int<min, ' . (PHP_INT_MAX - 1) . '>',
],
[
'$n' => '~' . PHP_INT_MAX,
],
],
[
new Expr\BinaryOp\BooleanAnd(
new Expr\BinaryOp\GreaterOrEqual(
Expand Down
12 changes: 12 additions & 0 deletions tests/PHPStan/Analyser/data/bug-3880.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Bug3880;

use function PHPStan\Analyser\assertType;

function ($value): void {
$num = (float) $value;
if ((!is_numeric($value) && !is_bool($value)) || $num > 9223372036854775807 || $num < -9223372036854775808) {
assertType('mixed', $value);
}
};

0 comments on commit a61a5d3

Please sign in to comment.