Skip to content

Commit 58b6023

Browse files
authored
Add throw point for division by zero
1 parent 231990a commit 58b6023

File tree

6 files changed

+405
-1
lines changed

6 files changed

+405
-1
lines changed

src/Analyser/MutatingScope.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2939,11 +2939,18 @@ private function calculateFromScalars(Expr $node, ConstantScalarType $leftType,
29392939
}
29402940

29412941
if ($node instanceof Node\Expr\BinaryOp\Div || $node instanceof Node\Expr\AssignOp\Div) {
2942+
if ($rightNumberValue === 0 || $rightNumberValue === 0.0) {
2943+
return new ErrorType();
2944+
}
29422945
return $this->getTypeFromValue($leftNumberValue / $rightNumberValue);
29432946
}
29442947

29452948
if ($node instanceof Node\Expr\BinaryOp\Mod || $node instanceof Node\Expr\AssignOp\Mod) {
2946-
return $this->getTypeFromValue(((int) $leftNumberValue) % ((int) $rightNumberValue));
2949+
$rightIntegerValue = (int) $rightNumberValue;
2950+
if ($rightIntegerValue === 0) {
2951+
return new ErrorType();
2952+
}
2953+
return $this->getTypeFromValue(((int) $leftNumberValue) % $rightIntegerValue);
29472954
}
29482955

29492956
if ($node instanceof Expr\BinaryOp\ShiftLeft || $node instanceof Expr\AssignOp\ShiftLeft) {

src/Analyser/NodeScopeResolver.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use ArrayAccess;
66
use Closure;
7+
use DivisionByZeroError;
78
use PhpParser\Comment\Doc;
89
use PhpParser\Node;
910
use PhpParser\Node\Arg;
@@ -116,6 +117,7 @@
116117
use PHPStan\Type\Constant\ConstantArrayType;
117118
use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
118119
use PHPStan\Type\Constant\ConstantBooleanType;
120+
use PHPStan\Type\Constant\ConstantIntegerType;
119121
use PHPStan\Type\Constant\ConstantStringType;
120122
use PHPStan\Type\ErrorType;
121123
use PHPStan\Type\FileTypeMapper;
@@ -1745,6 +1747,12 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
17451747
$scope = $result->getScope();
17461748
$hasYield = $result->hasYield();
17471749
$throwPoints = $result->getThrowPoints();
1750+
if (
1751+
($expr instanceof Expr\AssignOp\Div || $expr instanceof Expr\AssignOp\Mod) &&
1752+
!$scope->getType($expr->expr)->toNumber()->isSuperTypeOf(new ConstantIntegerType(0))->no()
1753+
) {
1754+
$throwPoints[] = ThrowPoint::createExplicit($scope, new ObjectType(DivisionByZeroError::class), $expr, false);
1755+
}
17481756
} elseif ($expr instanceof FuncCall) {
17491757
$parametersAcceptor = null;
17501758
$functionReflection = null;
@@ -2310,6 +2318,12 @@ static function (?Type $offsetType, Type $valueType) use (&$arrayType): void {
23102318
$hasYield = $result->hasYield();
23112319
$throwPoints = $result->getThrowPoints();
23122320
$result = $this->processExprNode($expr->right, $scope, $nodeCallback, $context->enterDeep());
2321+
if (
2322+
($expr instanceof BinaryOp\Div || $expr instanceof BinaryOp\Mod) &&
2323+
!$scope->getType($expr->right)->toNumber()->isSuperTypeOf(new ConstantIntegerType(0))->no()
2324+
) {
2325+
$throwPoints[] = ThrowPoint::createExplicit($scope, new ObjectType(DivisionByZeroError::class), $expr, false);
2326+
}
23132327
$scope = $result->getScope();
23142328
$hasYield = $hasYield || $result->hasYield();
23152329
$throwPoints = array_merge($throwPoints, $result->getThrowPoints());

src/Parallel/Scheduler.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class Scheduler
1313

1414
/**
1515
* @param positive-int $jobSize
16+
* @param positive-int $maximumNumberOfProcesses
17+
* @param positive-int $minimumNumberOfJobsPerProcess
1618
*/
1719
public function __construct(
1820
private int $jobSize,

tests/PHPStan/Parallel/SchedulerTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ public function dataSchedule(): array
7373
/**
7474
* @dataProvider dataSchedule
7575
* @param positive-int $jobSize
76+
* @param positive-int $maximumNumberOfProcesses
77+
* @param positive-int $minimumNumberOfJobsPerProcess
7678
* @param 0|positive-int $numberOfFiles
7779
* @param array<int> $expectedJobSizes
7880
*/

tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,4 +334,94 @@ public function testUnionTypeError(): void
334334
]);
335335
}
336336

337+
public function testBug6349(): void
338+
{
339+
$this->analyse([__DIR__ . '/data/bug-6349.php'], [
340+
[
341+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
342+
29,
343+
],
344+
[
345+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
346+
33,
347+
],
348+
[
349+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
350+
44,
351+
],
352+
[
353+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
354+
48,
355+
],
356+
[
357+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
358+
106,
359+
],
360+
[
361+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
362+
110,
363+
],
364+
[
365+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
366+
121,
367+
],
368+
[
369+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
370+
125,
371+
],
372+
[
373+
// throw point not implemented yet, because there is no way to narrow float value by !== 0.0
374+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
375+
139,
376+
],
377+
[
378+
// throw point not implemented yet, because there is no way to narrow float value by !== 0.0
379+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
380+
143,
381+
],
382+
[
383+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
384+
172,
385+
],
386+
[
387+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
388+
176,
389+
],
390+
[
391+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
392+
187,
393+
],
394+
[
395+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
396+
191,
397+
],
398+
[
399+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
400+
249,
401+
],
402+
[
403+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
404+
253,
405+
],
406+
[
407+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
408+
264,
409+
],
410+
[
411+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
412+
268,
413+
],
414+
[
415+
// throw point not implemented yet, because there is no way to narrow float value by !== 0.0
416+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
417+
282,
418+
],
419+
[
420+
// throw point not implemented yet, because there is no way to narrow float value by !== 0.0
421+
'Dead catch - DivisionByZeroError is never thrown in the try block.',
422+
286,
423+
],
424+
]);
425+
}
426+
337427
}

0 commit comments

Comments
 (0)