]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/arithmetic.rs
Merge commit '4911ab124c481430672a3833b37075e6435ec34d' into clippyup
[rust.git] / clippy_lints / src / arithmetic.rs
index fd252b9ee109ccf9538228229e12a52668cd8b35..9861d8cfc4e5f4146e7101d57fbea558b2d9c081 100644 (file)
@@ -6,11 +6,17 @@
 use rustc_span::source_map::Span;
 
 declare_clippy_lint! {
-    /// **What it does:** Checks for plain integer arithmetic.
+    /// **What it does:** Checks for integer arithmetic operations which could overflow or panic.
     ///
-    /// **Why is this bad?** This is only checked against overflow in debug builds.
-    /// In some applications one wants explicitly checked, wrapping or saturating
-    /// arithmetic.
+    /// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable
+    /// of overflowing according to the [Rust
+    /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
+    /// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is
+    /// attempted.
+    ///
+    /// **Why is this bad?** Integer overflow will trigger a panic in debug builds or will wrap in
+    /// release mode. Division by zero will cause a panic in either mode. In some applications one
+    /// wants explicitly checked, wrapping or saturating arithmetic.
     ///
     /// **Known problems:** None.
     ///
@@ -21,7 +27,7 @@
     /// ```
     pub INTEGER_ARITHMETIC,
     restriction,
-    "any integer arithmetic statement"
+    "any integer arithmetic expression which could overflow or panic"
 }
 
 declare_clippy_lint! {
@@ -52,8 +58,8 @@ pub struct Arithmetic {
 
 impl_lint_pass!(Arithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]);
 
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic {
-    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
+impl<'tcx> LateLintPass<'tcx> for Arithmetic {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         if self.expr_span.is_some() {
             return;
         }
@@ -71,8 +77,6 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>)
                     | hir::BinOpKind::BitAnd
                     | hir::BinOpKind::BitOr
                     | hir::BinOpKind::BitXor
-                    | hir::BinOpKind::Shl
-                    | hir::BinOpKind::Shr
                     | hir::BinOpKind::Eq
                     | hir::BinOpKind::Lt
                     | hir::BinOpKind::Le
@@ -81,18 +85,38 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>)
                     | hir::BinOpKind::Gt => return,
                     _ => (),
                 }
-                let (l_ty, r_ty) = (cx.tables.expr_ty(l), cx.tables.expr_ty(r));
-                if l_ty.is_integral() && r_ty.is_integral() {
-                    span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
-                    self.expr_span = Some(expr.span);
-                } else if l_ty.is_floating_point() && r_ty.is_floating_point() {
+
+                let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r));
+                if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
+                    match op.node {
+                        hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind {
+                            hir::ExprKind::Lit(_lit) => (),
+                            hir::ExprKind::Unary(hir::UnOp::UnNeg, expr) => {
+                                if let hir::ExprKind::Lit(lit) = &expr.kind {
+                                    if let rustc_ast::ast::LitKind::Int(1, _) = lit.node {
+                                        span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+                                        self.expr_span = Some(expr.span);
+                                    }
+                                }
+                            },
+                            _ => {
+                                span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+                                self.expr_span = Some(expr.span);
+                            },
+                        },
+                        _ => {
+                            span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+                            self.expr_span = Some(expr.span);
+                        },
+                    }
+                } else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
                     span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
                     self.expr_span = Some(expr.span);
                 }
             },
             hir::ExprKind::Unary(hir::UnOp::UnNeg, arg) => {
-                let ty = cx.tables.expr_ty(arg);
-                if constant_simple(cx, cx.tables, expr).is_none() {
+                let ty = cx.typeck_results().expr_ty(arg);
+                if constant_simple(cx, cx.typeck_results(), expr).is_none() {
                     if ty.is_integral() {
                         span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
                         self.expr_span = Some(expr.span);
@@ -106,13 +130,13 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>)
         }
     }
 
-    fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>) {
+    fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         if Some(expr.span) == self.expr_span {
             self.expr_span = None;
         }
     }
 
-    fn check_body(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body<'_>) {
+    fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
         let body_owner = cx.tcx.hir().body_owner(body.id());
 
         match cx.tcx.hir().body_owner_kind(body_owner) {
@@ -130,7 +154,7 @@ fn check_body(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body<'_>) {
         }
     }
 
-    fn check_body_post(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body<'_>) {
+    fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
         let body_owner = cx.tcx.hir().body_owner(body.id());
         let body_span = cx.tcx.hir().span(body_owner);