|
2 | 2 |
|
3 | 3 | namespace Pepakriz\PHPStanExceptionRules\Rules;
|
4 | 4 |
|
| 5 | +use ArithmeticError; |
| 6 | +use DivisionByZeroError; |
5 | 7 | use Iterator;
|
6 | 8 | use IteratorAggregate;
|
7 | 9 | use Pepakriz\PHPStanExceptionRules\CheckedExceptionService;
|
|
35 | 37 | use PHPStan\Reflection\ThrowableReflection;
|
36 | 38 | use PHPStan\Rules\Rule;
|
37 | 39 | use PHPStan\ShouldNotHappenException;
|
| 40 | +use PHPStan\Type\NeverType; |
38 | 41 | use PHPStan\Type\ObjectType;
|
39 | 42 | use PHPStan\Type\Type;
|
40 | 43 | use PHPStan\Type\TypeCombinator;
|
@@ -185,6 +188,22 @@ public function processNode(Node $node, Scope $scope): array
|
185 | 188 | return $this->processFuncCall($node, $scope);
|
186 | 189 | }
|
187 | 190 |
|
| 191 | + if ($node instanceof Expr\BinaryOp\Div || $node instanceof Expr\BinaryOp\Mod) { |
| 192 | + return $this->processDiv($node->right, $scope); |
| 193 | + } |
| 194 | + |
| 195 | + if ($node instanceof Expr\AssignOp\Div || $node instanceof Expr\AssignOp\Mod) { |
| 196 | + return $this->processDiv($node->expr, $scope); |
| 197 | + } |
| 198 | + |
| 199 | + if ($node instanceof Expr\BinaryOp\ShiftLeft || $node instanceof Expr\BinaryOp\ShiftRight) { |
| 200 | + return $this->processShift($node->right, $scope); |
| 201 | + } |
| 202 | + |
| 203 | + if ($node instanceof Expr\AssignOp\ShiftLeft || $node instanceof Expr\AssignOp\ShiftRight) { |
| 204 | + return $this->processShift($node->expr, $scope); |
| 205 | + } |
| 206 | + |
188 | 207 | return [];
|
189 | 208 | }
|
190 | 209 |
|
@@ -599,6 +618,53 @@ private function processFuncCall(FuncCall $node, Scope $scope): array
|
599 | 618 | return $this->processThrowsTypes($throwType);
|
600 | 619 | }
|
601 | 620 |
|
| 621 | + /** |
| 622 | + * @return string[] |
| 623 | + */ |
| 624 | + private function processDiv(Expr $divisor, Scope $scope): array |
| 625 | + { |
| 626 | + $divisionByZero = false; |
| 627 | + $divisorType = $scope->getType($divisor); |
| 628 | + foreach (TypeUtils::getConstantScalars($divisorType) as $constantScalarType) { |
| 629 | + if ($constantScalarType->getValue() === 0) { |
| 630 | + $divisionByZero = true; |
| 631 | + } |
| 632 | + |
| 633 | + $divisorType = TypeCombinator::remove($divisorType, $constantScalarType); |
| 634 | + } |
| 635 | + |
| 636 | + if (!$divisorType instanceof NeverType) { |
| 637 | + return $this->processThrowsTypes(new ObjectType(ArithmeticError::class)); |
| 638 | + } |
| 639 | + |
| 640 | + if ($divisionByZero) { |
| 641 | + return $this->processThrowsTypes(new ObjectType(DivisionByZeroError::class)); |
| 642 | + } |
| 643 | + |
| 644 | + return []; |
| 645 | + } |
| 646 | + |
| 647 | + /** |
| 648 | + * @return string[] |
| 649 | + */ |
| 650 | + private function processShift(Expr $value, Scope $scope): array |
| 651 | + { |
| 652 | + $valueType = $scope->getType($value); |
| 653 | + foreach (TypeUtils::getConstantScalars($valueType) as $constantScalarType) { |
| 654 | + if ($constantScalarType->getValue() < 0) { |
| 655 | + return $this->processThrowsTypes(new ObjectType(ArithmeticError::class)); |
| 656 | + } |
| 657 | + |
| 658 | + $valueType = TypeCombinator::remove($valueType, $constantScalarType); |
| 659 | + } |
| 660 | + |
| 661 | + if (!$valueType instanceof NeverType) { |
| 662 | + return $this->processThrowsTypes(new ObjectType(ArithmeticError::class)); |
| 663 | + } |
| 664 | + |
| 665 | + return []; |
| 666 | + } |
| 667 | + |
602 | 668 | /**
|
603 | 669 | * @param Name|Expr|ClassLike $class
|
604 | 670 | * @param string[] $methods
|
|
0 commit comments