diff --git a/src/Type/Php/IntdivThrowTypeExtension.php b/src/Type/Php/IntdivThrowTypeExtension.php index aab1448733..ef8ac1ef54 100644 --- a/src/Type/Php/IntdivThrowTypeExtension.php +++ b/src/Type/Php/IntdivThrowTypeExtension.php @@ -7,11 +7,10 @@ use PhpParser\Node\Expr\FuncCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\FunctionReflection; +use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\DynamicFunctionThrowTypeExtension; -use PHPStan\Type\NeverType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; -use PHPStan\Type\TypeCombinator; use function count; use const PHP_INT_MIN; @@ -29,39 +28,19 @@ public function getThrowTypeFromFunctionCall(FunctionReflection $functionReflect return $functionReflection->getThrowType(); } - $containsMin = false; - $valueType = $scope->getType($funcCall->getArgs()[0]->value); - foreach ($valueType->getConstantScalarTypes() as $constantScalarType) { - if ($constantScalarType->getValue() === PHP_INT_MIN) { - $containsMin = true; - } - - $valueType = TypeCombinator::remove($valueType, $constantScalarType); - } - - if (!$valueType instanceof NeverType) { - $containsMin = true; - } + $valueType = $scope->getType($funcCall->getArgs()[0]->value)->toInteger(); + $containsMin = $valueType->isSuperTypeOf(new ConstantIntegerType(PHP_INT_MIN)); - $divisionByZero = false; - $divisorType = $scope->getType($funcCall->getArgs()[1]->value); - foreach ($divisorType->getConstantScalarTypes() as $constantScalarType) { - if ($containsMin && $constantScalarType->getValue() === -1) { + $divisorType = $scope->getType($funcCall->getArgs()[1]->value)->toInteger(); + if (!$containsMin->no()) { + $divisionByMinusOne = $divisorType->isSuperTypeOf(new ConstantIntegerType(-1)); + if (!$divisionByMinusOne->no()) { return new ObjectType(ArithmeticError::class); } - - if ($constantScalarType->getValue() === 0) { - $divisionByZero = true; - } - - $divisorType = TypeCombinator::remove($divisorType, $constantScalarType); - } - - if (!$divisorType instanceof NeverType) { - return new ObjectType($containsMin ? ArithmeticError::class : DivisionByZeroError::class); } - if ($divisionByZero) { + $divisionByZero = $divisorType->isSuperTypeOf(new ConstantIntegerType(0)); + if (!$divisionByZero->no()) { return new ObjectType(DivisionByZeroError::class); } diff --git a/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php b/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php index 83943ccbb2..0958befd2f 100644 --- a/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php +++ b/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php @@ -137,6 +137,10 @@ public function testRule(): void 'Dead catch - InvalidArgumentException is never thrown in the try block.', 741, ], + [ + 'Dead catch - ArithmeticError is never thrown in the try block.', + 762, + ], ]); } diff --git a/tests/PHPStan/Rules/Exceptions/data/unthrown-exception.php b/tests/PHPStan/Rules/Exceptions/data/unthrown-exception.php index d501e1cffc..0327fcb086 100644 --- a/tests/PHPStan/Rules/Exceptions/data/unthrown-exception.php +++ b/tests/PHPStan/Rules/Exceptions/data/unthrown-exception.php @@ -744,3 +744,49 @@ public function passCallableToMethod2(): void } } + +class TestIntdivWithRange +{ + /** + * @param int $int + * @param int $negativeInt + * @param int<1, max> $positiveInt + */ + public function doFoo(int $int, int $negativeInt, int $positiveInt): void + { + try { + intdiv($int, $positiveInt); + intdiv($positiveInt, $negativeInt); + intdiv($negativeInt, $positiveInt); + intdiv($positiveInt, $positiveInt); + } catch (\ArithmeticError $e) { + + } + try { + intdiv($int, $negativeInt); + } catch (\ArithmeticError $e) { + + } + try { + intdiv($negativeInt, $negativeInt); + } catch (\ArithmeticError $e) { + + } + try { + intdiv($positiveInt, $int); + } catch (\ArithmeticError $e) { + + } + try { + intdiv($negativeInt, $int); + } catch (\ArithmeticError $e) { + + } + try { + intdiv($int, '-1,5'); + } catch (\ArithmeticError $e) { + + } + } + +}