diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/arithmetic.rs index 4d7e921567d..a481d46cce0 100644 --- a/clippy_lints/src/arithmetic.rs +++ b/clippy_lints/src/arithmetic.rs @@ -51,7 +51,9 @@ declare_clippy_lint! { #[derive(Copy, Clone, Default)] pub struct Arithmetic { - span: Option, + expr_span: Option, + /// This field is used to check whether expressions are constants, such as in enum discriminants and consts + const_span: Option, } impl LintPass for Arithmetic { @@ -62,9 +64,15 @@ impl LintPass for Arithmetic { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) { - if self.span.is_some() { + if self.expr_span.is_some() { return; } + + if let Some(span) = self.const_span { + if span.contains(expr.span) { + return; + } + } match expr.node { hir::ExprKind::Binary(ref op, ref l, ref r) => { match op.node { @@ -86,20 +94,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic { 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.span = Some(expr.span); + self.expr_span = Some(expr.span); } else if l_ty.is_floating_point() && r_ty.is_floating_point() { span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); - self.span = Some(expr.span); + self.expr_span = Some(expr.span); } }, hir::ExprKind::Unary(hir::UnOp::UnNeg, ref arg) => { let ty = cx.tables.expr_ty(arg); if ty.is_integral() { span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.span = Some(expr.span); + self.expr_span = Some(expr.span); } else if ty.is_floating_point() { span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); - self.span = Some(expr.span); + self.expr_span = Some(expr.span); } }, _ => (), @@ -107,8 +115,39 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic { } fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) { - if Some(expr.span) == self.span { - self.span = None; + if Some(expr.span) == self.expr_span { + self.expr_span = None; } } + + 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) { + hir::BodyOwnerKind::Static(_) + | hir::BodyOwnerKind::Const => { + let body_span = cx.tcx.hir.span(body_owner); + + if let Some(span) = self.const_span { + if span.contains(body_span) { + return; + } + } + self.const_span = Some(body_span); + } + hir::BodyOwnerKind::Fn => (), + } + } + + 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); + + if let Some(span) = self.const_span { + if span.contains(body_span) { + return; + } + } + self.const_span = None; + } } diff --git a/tests/ui/arithmetic.rs b/tests/ui/arithmetic.rs index ff550c9593c..61a601468fb 100644 --- a/tests/ui/arithmetic.rs +++ b/tests/ui/arithmetic.rs @@ -37,4 +37,36 @@ fn main() { f / 2.0; f - 2.0 * 4.2; -f; + + // No errors for the following items because they are constant expressions + enum Foo { + Bar = -2, + } + struct Baz([i32; 1 + 1]); + union Qux { + field: [i32; 1 + 1], + } + type Alias = [i32; 1 + 1]; + + const FOO: i32 = -2; + static BAR: i32 = -2; + + let _: [i32; 1 + 1] = [0, 0]; + + let _: [i32; 1 + 1] = { + let a: [i32; 1 + 1] = [0, 0]; + a + }; + + trait Trait { + const ASSOC: i32 = 1 + 1; + } + + impl Trait for Foo { + const ASSOC: i32 = { + let _: [i32; 1 + 1]; + fn foo() {} + 1 + 1 + }; + } }