Skip to content

Commit

Permalink
Add throw point for division by zero
Browse files Browse the repository at this point in the history
  • Loading branch information
rajyan committed May 4, 2022
1 parent 231990a commit 58b6023
Show file tree
Hide file tree
Showing 6 changed files with 405 additions and 1 deletion.
9 changes: 8 additions & 1 deletion src/Analyser/MutatingScope.php
Expand Up @@ -2939,11 +2939,18 @@ private function calculateFromScalars(Expr $node, ConstantScalarType $leftType,
}

if ($node instanceof Node\Expr\BinaryOp\Div || $node instanceof Node\Expr\AssignOp\Div) {
if ($rightNumberValue === 0 || $rightNumberValue === 0.0) {
return new ErrorType();
}
return $this->getTypeFromValue($leftNumberValue / $rightNumberValue);
}

if ($node instanceof Node\Expr\BinaryOp\Mod || $node instanceof Node\Expr\AssignOp\Mod) {
return $this->getTypeFromValue(((int) $leftNumberValue) % ((int) $rightNumberValue));
$rightIntegerValue = (int) $rightNumberValue;
if ($rightIntegerValue === 0) {
return new ErrorType();
}
return $this->getTypeFromValue(((int) $leftNumberValue) % $rightIntegerValue);
}

if ($node instanceof Expr\BinaryOp\ShiftLeft || $node instanceof Expr\AssignOp\ShiftLeft) {
Expand Down
14 changes: 14 additions & 0 deletions src/Analyser/NodeScopeResolver.php
Expand Up @@ -4,6 +4,7 @@

use ArrayAccess;
use Closure;
use DivisionByZeroError;
use PhpParser\Comment\Doc;
use PhpParser\Node;
use PhpParser\Node\Arg;
Expand Down Expand Up @@ -116,6 +117,7 @@
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\FileTypeMapper;
Expand Down Expand Up @@ -1745,6 +1747,12 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
$scope = $result->getScope();
$hasYield = $result->hasYield();
$throwPoints = $result->getThrowPoints();
if (
($expr instanceof Expr\AssignOp\Div || $expr instanceof Expr\AssignOp\Mod) &&
!$scope->getType($expr->expr)->toNumber()->isSuperTypeOf(new ConstantIntegerType(0))->no()
) {
$throwPoints[] = ThrowPoint::createExplicit($scope, new ObjectType(DivisionByZeroError::class), $expr, false);
}
} elseif ($expr instanceof FuncCall) {
$parametersAcceptor = null;
$functionReflection = null;
Expand Down Expand Up @@ -2310,6 +2318,12 @@ static function (?Type $offsetType, Type $valueType) use (&$arrayType): void {
$hasYield = $result->hasYield();
$throwPoints = $result->getThrowPoints();
$result = $this->processExprNode($expr->right, $scope, $nodeCallback, $context->enterDeep());
if (
($expr instanceof BinaryOp\Div || $expr instanceof BinaryOp\Mod) &&
!$scope->getType($expr->right)->toNumber()->isSuperTypeOf(new ConstantIntegerType(0))->no()
) {
$throwPoints[] = ThrowPoint::createExplicit($scope, new ObjectType(DivisionByZeroError::class), $expr, false);
}
$scope = $result->getScope();
$hasYield = $hasYield || $result->hasYield();
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());
Expand Down
2 changes: 2 additions & 0 deletions src/Parallel/Scheduler.php
Expand Up @@ -13,6 +13,8 @@ class Scheduler

/**
* @param positive-int $jobSize
* @param positive-int $maximumNumberOfProcesses
* @param positive-int $minimumNumberOfJobsPerProcess
*/
public function __construct(
private int $jobSize,
Expand Down
2 changes: 2 additions & 0 deletions tests/PHPStan/Parallel/SchedulerTest.php
Expand Up @@ -73,6 +73,8 @@ public function dataSchedule(): array
/**
* @dataProvider dataSchedule
* @param positive-int $jobSize
* @param positive-int $maximumNumberOfProcesses
* @param positive-int $minimumNumberOfJobsPerProcess
* @param 0|positive-int $numberOfFiles
* @param array<int> $expectedJobSizes
*/
Expand Down
Expand Up @@ -334,4 +334,94 @@ public function testUnionTypeError(): void
]);
}

public function testBug6349(): void
{
$this->analyse([__DIR__ . '/data/bug-6349.php'], [
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
29,
],
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
33,
],
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
44,
],
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
48,
],
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
106,
],
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
110,
],
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
121,
],
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
125,
],
[
// throw point not implemented yet, because there is no way to narrow float value by !== 0.0
'Dead catch - DivisionByZeroError is never thrown in the try block.',
139,
],
[
// throw point not implemented yet, because there is no way to narrow float value by !== 0.0
'Dead catch - DivisionByZeroError is never thrown in the try block.',
143,
],
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
172,
],
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
176,
],
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
187,
],
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
191,
],
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
249,
],
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
253,
],
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
264,
],
[
'Dead catch - DivisionByZeroError is never thrown in the try block.',
268,
],
[
// throw point not implemented yet, because there is no way to narrow float value by !== 0.0
'Dead catch - DivisionByZeroError is never thrown in the try block.',
282,
],
[
// throw point not implemented yet, because there is no way to narrow float value by !== 0.0
'Dead catch - DivisionByZeroError is never thrown in the try block.',
286,
],
]);
}

}

0 comments on commit 58b6023

Please sign in to comment.