]> git.lizzy.rs Git - rust.git/commitdiff
Merge branch 'master' of github.com:rust-lang/rust-clippy
authorJarredAllen <jarredallen73@gmail.com>
Sun, 1 Mar 2020 20:48:22 +0000 (12:48 -0800)
committerJarredAllen <jarredallen73@gmail.com>
Sun, 1 Mar 2020 20:48:22 +0000 (12:48 -0800)
clippy_lints/src/floating_point_arithmetic.rs
tests/ui/floating_point_abs.rs [new file with mode: 0644]
tests/ui/floating_point_abs.stderr [new file with mode: 0644]

index 29f924ebe1c5af14be675934f662eb3ee5e4d233..6a6583f66102cbf17896ccaf9c46c1099bf8ba98 100644 (file)
@@ -2,11 +2,11 @@
     constant, Constant,
     Constant::{F32, F64},
 };
-use crate::utils::{span_lint_and_sugg, sugg};
+use crate::utils::{higher, span_lint_and_sugg, sugg, SpanlessEq};
 use if_chain::if_chain;
 use rustc::ty;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
+use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Lit, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Spanned;
@@ -15,6 +15,7 @@
 use std::f32::consts as f32_consts;
 use std::f64::consts as f64_consts;
 use sugg::{format_numeric_literal, Sugg};
+use syntax::ast::{self, FloatTy, LitFloatType, LitKind};
 
 declare_clippy_lint! {
     /// **What it does:** Looks for floating-point expressions that
     /// let _ = a.log(E);
     /// let _ = a.powf(2.0);
     /// let _ = a * 2.0 + 4.0;
+    /// let _ = if a < 0.0 {
+    ///     -a
+    /// } else {
+    ///     a
+    /// }
+    /// let _ = if a < 0.0 {
+    ///     a
+    /// } else {
+    ///     -a
+    /// }
     /// ```
     ///
     /// is better expressed as
@@ -88,6 +99,8 @@
     /// let _ = a.ln();
     /// let _ = a.powi(2);
     /// let _ = a.mul_add(2.0, 4.0);
+    /// let _ = a.abs();
+    /// let _ = -a.abs();
     /// ```
     pub SUBOPTIMAL_FLOPS,
     nursery,
@@ -359,6 +372,154 @@ fn check_mul_add(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
     }
 }
 
+/// Returns true iff expr is an expression which tests whether or not
+/// test is positive or an expression which tests whether or not test
+/// is nonnegative.
+/// Used for check-custom-abs function below
+fn is_testing_positive(cx: &LateContext<'_, '_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
+    if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
+        match op {
+            BinOpKind::Gt | BinOpKind::Ge => is_zero(right) && are_exprs_equal(cx, left, test),
+            BinOpKind::Lt | BinOpKind::Le => is_zero(left) && are_exprs_equal(cx, right, test),
+            _ => false,
+        }
+    } else {
+        false
+    }
+}
+
+fn is_testing_negative(cx: &LateContext<'_, '_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
+    if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
+        match op {
+            BinOpKind::Gt | BinOpKind::Ge => is_zero(left) && are_exprs_equal(cx, right, test),
+            BinOpKind::Lt | BinOpKind::Le => is_zero(right) && are_exprs_equal(cx, left, test),
+            _ => false,
+        }
+    } else {
+        false
+    }
+}
+
+fn are_exprs_equal(cx: &LateContext<'_, '_>, expr1: &Expr<'_>, expr2: &Expr<'_>) -> bool {
+    SpanlessEq::new(cx).ignore_fn().eq_expr(expr1, expr2)
+}
+
+/// Returns true iff expr is some zero literal
+fn is_zero(expr: &Expr<'_>) -> bool {
+    if let ExprKind::Lit(Lit { node: lit, .. }) = &expr.kind {
+        match lit {
+            LitKind::Int(0, _) => true,
+            LitKind::Float(symb, LitFloatType::Unsuffixed)
+            | LitKind::Float(symb, LitFloatType::Suffixed(FloatTy::F64)) => {
+                symb.as_str().parse::<f64>().unwrap() == 0.0
+            },
+            LitKind::Float(symb, LitFloatType::Suffixed(FloatTy::F32)) => symb.as_str().parse::<f32>().unwrap() == 0.0,
+            _ => false,
+        }
+    } else {
+        false
+    }
+}
+
+fn check_custom_abs(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
+    if let Some((cond, body, Some(else_body))) = higher::if_block(&expr) {
+        if let ExprKind::Block(
+            Block {
+                stmts: [],
+                expr:
+                    Some(Expr {
+                        kind: ExprKind::Unary(UnOp::UnNeg, else_expr),
+                        ..
+                    }),
+                ..
+            },
+            _,
+        ) = else_body.kind
+        {
+            if let ExprKind::Block(
+                Block {
+                    stmts: [],
+                    expr: Some(body),
+                    ..
+                },
+                _,
+            ) = &body.kind
+            {
+                if are_exprs_equal(cx, else_expr, body) {
+                    if is_testing_positive(cx, cond, body) {
+                        span_lint_and_sugg(
+                            cx,
+                            SUBOPTIMAL_FLOPS,
+                            expr.span,
+                            "This looks like you've implemented your own absolute value function",
+                            "try",
+                            format!("{}.abs()", Sugg::hir(cx, body, "..")),
+                            Applicability::MachineApplicable,
+                        );
+                    } else if is_testing_negative(cx, cond, body) {
+                        span_lint_and_sugg(
+                            cx,
+                            SUBOPTIMAL_FLOPS,
+                            expr.span,
+                            "This looks like you've implemented your own negative absolute value function",
+                            "try",
+                            format!("-{}.abs()", Sugg::hir(cx, body, "..")),
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                }
+            }
+        }
+        if let ExprKind::Block(
+            Block {
+                stmts: [],
+                expr:
+                    Some(Expr {
+                        kind: ExprKind::Unary(UnOp::UnNeg, else_expr),
+                        ..
+                    }),
+                ..
+            },
+            _,
+        ) = &body.kind
+        {
+            if let ExprKind::Block(
+                Block {
+                    stmts: [],
+                    expr: Some(body),
+                    ..
+                },
+                _,
+            ) = &else_body.kind
+            {
+                if are_exprs_equal(cx, else_expr, body) {
+                    if is_testing_negative(cx, cond, body) {
+                        span_lint_and_sugg(
+                            cx,
+                            SUBOPTIMAL_FLOPS,
+                            expr.span,
+                            "This looks like you've implemented your own absolute value function",
+                            "try",
+                            format!("{}.abs()", Sugg::hir(cx, body, "..")),
+                            Applicability::MachineApplicable,
+                        );
+                    } else if is_testing_positive(cx, cond, body) {
+                        span_lint_and_sugg(
+                            cx,
+                            SUBOPTIMAL_FLOPS,
+                            expr.span,
+                            "This looks like you've implemented your own negative absolute value function",
+                            "try",
+                            format!("-{}.abs()", Sugg::hir(cx, body, "..")),
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                }
+            }
+        }
+    }
+}
+
 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatingPointArithmetic {
     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
         if let ExprKind::MethodCall(ref path, _, args) = &expr.kind {
@@ -375,6 +536,7 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
         } else {
             check_expm1(cx, expr);
             check_mul_add(cx, expr);
+            check_custom_abs(cx, expr);
         }
     }
 }
diff --git a/tests/ui/floating_point_abs.rs b/tests/ui/floating_point_abs.rs
new file mode 100644 (file)
index 0000000..b0c15e5
--- /dev/null
@@ -0,0 +1,111 @@
+#![warn(clippy::suboptimal_flops)]
+
+struct A {
+    a: f64,
+    b: f64,
+}
+
+fn fake_abs1(num: f64) -> f64 {
+    if num >= 0.0 {
+        num
+    } else {
+        -num
+    }
+}
+
+fn fake_abs2(num: f64) -> f64 {
+    if 0.0 < num {
+        num
+    } else {
+        -num
+    }
+}
+
+fn fake_abs3(a: A) -> f64 {
+    if a.a > 0.0 {
+        a.a
+    } else {
+        -a.a
+    }
+}
+
+fn fake_abs4(num: f64) -> f64 {
+    if 0.0 >= num {
+        -num
+    } else {
+        num
+    }
+}
+
+fn fake_abs5(a: A) -> f64 {
+    if a.a < 0.0 {
+        -a.a
+    } else {
+        a.a
+    }
+}
+
+fn fake_nabs1(num: f64) -> f64 {
+    if num < 0.0 {
+        num
+    } else {
+        -num
+    }
+}
+
+fn fake_nabs2(num: f64) -> f64 {
+    if 0.0 >= num {
+        num
+    } else {
+        -num
+    }
+}
+
+fn fake_nabs3(a: A) -> A {
+    A {
+        a: if a.a >= 0.0 { -a.a } else { a.a },
+        b: a.b,
+    }
+}
+
+fn not_fake_abs1(num: f64) -> f64 {
+    if num > 0.0 {
+        num
+    } else {
+        -num - 1f64
+    }
+}
+
+fn not_fake_abs2(num: f64) -> f64 {
+    if num > 0.0 {
+        num + 1.0
+    } else {
+        -(num + 1.0)
+    }
+}
+
+fn not_fake_abs3(num1: f64, num2: f64) -> f64 {
+    if num1 > 0.0 {
+        num2
+    } else {
+        -num2
+    }
+}
+
+fn not_fake_abs4(a: A) -> f64 {
+    if a.a > 0.0 {
+        a.b
+    } else {
+        -a.b
+    }
+}
+
+fn not_fake_abs5(a: A) -> f64 {
+    if a.a > 0.0 {
+        a.a
+    } else {
+        -a.b
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/floating_point_abs.stderr b/tests/ui/floating_point_abs.stderr
new file mode 100644 (file)
index 0000000..44a9dbe
--- /dev/null
@@ -0,0 +1,80 @@
+error: This looks like you've implemented your own absolute value function
+  --> $DIR/floating_point_abs.rs:9:5
+   |
+LL | /     if num >= 0.0 {
+LL | |         num
+LL | |     } else {
+LL | |         -num
+LL | |     }
+   | |_____^ help: try: `num.abs()`
+   |
+   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: This looks like you've implemented your own absolute value function
+  --> $DIR/floating_point_abs.rs:17:5
+   |
+LL | /     if 0.0 < num {
+LL | |         num
+LL | |     } else {
+LL | |         -num
+LL | |     }
+   | |_____^ help: try: `num.abs()`
+
+error: This looks like you've implemented your own absolute value function
+  --> $DIR/floating_point_abs.rs:25:5
+   |
+LL | /     if a.a > 0.0 {
+LL | |         a.a
+LL | |     } else {
+LL | |         -a.a
+LL | |     }
+   | |_____^ help: try: `a.a.abs()`
+
+error: This looks like you've implemented your own absolute value function
+  --> $DIR/floating_point_abs.rs:33:5
+   |
+LL | /     if 0.0 >= num {
+LL | |         -num
+LL | |     } else {
+LL | |         num
+LL | |     }
+   | |_____^ help: try: `num.abs()`
+
+error: This looks like you've implemented your own absolute value function
+  --> $DIR/floating_point_abs.rs:41:5
+   |
+LL | /     if a.a < 0.0 {
+LL | |         -a.a
+LL | |     } else {
+LL | |         a.a
+LL | |     }
+   | |_____^ help: try: `a.a.abs()`
+
+error: This looks like you've implemented your own negative absolute value function
+  --> $DIR/floating_point_abs.rs:49:5
+   |
+LL | /     if num < 0.0 {
+LL | |         num
+LL | |     } else {
+LL | |         -num
+LL | |     }
+   | |_____^ help: try: `-num.abs()`
+
+error: This looks like you've implemented your own negative absolute value function
+  --> $DIR/floating_point_abs.rs:57:5
+   |
+LL | /     if 0.0 >= num {
+LL | |         num
+LL | |     } else {
+LL | |         -num
+LL | |     }
+   | |_____^ help: try: `-num.abs()`
+
+error: This looks like you've implemented your own negative absolute value function
+  --> $DIR/floating_point_abs.rs:66:12
+   |
+LL |         a: if a.a >= 0.0 { -a.a } else { a.a },
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-a.a.abs()`
+
+error: aborting due to 8 previous errors
+