about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/floating_point_arithmetic.rs75
-rw-r--r--tests/ui/floating_point_hypot.fixed13
-rw-r--r--tests/ui/floating_point_hypot.rs13
-rw-r--r--tests/ui/floating_point_hypot.stderr30
-rw-r--r--tests/ui/floating_point_mul_add.fixed5
-rw-r--r--tests/ui/floating_point_mul_add.rs5
-rw-r--r--tests/ui/floating_point_mul_add.stderr8
-rw-r--r--tests/ui/floating_point_powi.fixed7
-rw-r--r--tests/ui/floating_point_powi.rs7
-rw-r--r--tests/ui/floating_point_powi.stderr20
10 files changed, 177 insertions, 6 deletions
diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs
index e3ee4296119..3b2e46a9a85 100644
--- a/clippy_lints/src/floating_point_arithmetic.rs
+++ b/clippy_lints/src/floating_point_arithmetic.rs
@@ -2,10 +2,10 @@ use crate::consts::{
     constant, constant_simple, Constant,
     Constant::{Int, F32, F64},
 };
-use crate::utils::{higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq};
+use crate::utils::{get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
+use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -296,6 +296,17 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
     // Check argument
     if let Some((value, _)) = constant(cx, cx.tables, &args[1]) {
+        // TODO: need more specific check. this is too wide. remember also to include tests
+        if let Some(parent) = get_parent_expr(cx, expr) {
+            if let Some(grandparent) = get_parent_expr(cx, parent) {
+                if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args) = grandparent.kind {
+                    if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
+                        return;
+                    }
+                }
+            }
+        }
+
         let (lint, help, suggestion) = match value {
             Int(2) => (
                 IMPRECISE_FLOPS,
@@ -317,6 +328,57 @@ fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
     }
 }
 
+fn detect_hypot(cx: &LateContext<'_, '_>, args: &[Expr<'_>]) -> Option<String> {
+    if let ExprKind::Binary(
+        Spanned {
+            node: BinOpKind::Add, ..
+        },
+        ref add_lhs,
+        ref add_rhs,
+    ) = args[0].kind
+    {
+        // check if expression of the form x * x + y * y
+        if_chain! {
+            if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind;
+            if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind;
+            if are_exprs_equal(cx, lmul_lhs, lmul_rhs);
+            if are_exprs_equal(cx, rmul_lhs, rmul_rhs);
+            then {
+                return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, "..")));
+            }
+        }
+
+        // check if expression of the form x.powi(2) + y.powi(2)
+        if_chain! {
+            if let ExprKind::MethodCall(PathSegment { ident: lmethod_name, .. }, ref _lspan, ref largs) = add_lhs.kind;
+            if let ExprKind::MethodCall(PathSegment { ident: rmethod_name, .. }, ref _rspan, ref rargs) = add_rhs.kind;
+            if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi";
+            if let Some((lvalue, _)) = constant(cx, cx.tables, &largs[1]);
+            if let Some((rvalue, _)) = constant(cx, cx.tables, &rargs[1]);
+            if Int(2) == lvalue && Int(2) == rvalue;
+            then {
+                return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], "..")));
+            }
+        }
+    }
+
+    None
+}
+
+fn check_hypot(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
+    if let Some(message) = detect_hypot(cx, args) {
+        span_lint_and_sugg(
+            cx,
+            IMPRECISE_FLOPS,
+            expr.span,
+            "hypotenuse can be computed more accurately",
+            "consider using",
+            message,
+            Applicability::MachineApplicable,
+        );
+    }
+}
+
 // TODO: Lint expressions of the form `x.exp() - y` where y > 1
 // and suggest usage of `x.exp_m1() - (y - 1)` instead
 fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
@@ -368,6 +430,14 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
         rhs,
     ) = &expr.kind
     {
+        if let Some(parent) = get_parent_expr(cx, expr) {
+            if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args) = parent.kind {
+                if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
+                    return;
+                }
+            }
+        }
+
         let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
             (inner_lhs, inner_rhs, rhs)
         } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
@@ -514,6 +584,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
                     "log" => check_log_base(cx, expr, args),
                     "powf" => check_powf(cx, expr, args),
                     "powi" => check_powi(cx, expr, args),
+                    "sqrt" => check_hypot(cx, expr, args),
                     _ => {},
                 }
             }
diff --git a/tests/ui/floating_point_hypot.fixed b/tests/ui/floating_point_hypot.fixed
new file mode 100644
index 00000000000..f90695bc3fe
--- /dev/null
+++ b/tests/ui/floating_point_hypot.fixed
@@ -0,0 +1,13 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
+
+fn main() {
+    let x = 3f32;
+    let y = 4f32;
+    let _ = x.hypot(y);
+    let _ = (x + 1f32).hypot(y);
+    let _ = x.hypot(y);
+    // Cases where the lint shouldn't be applied
+    let _ = x.mul_add(x, y * y).sqrt();
+    let _ = x.mul_add(4f32, y * y).sqrt();
+}
diff --git a/tests/ui/floating_point_hypot.rs b/tests/ui/floating_point_hypot.rs
new file mode 100644
index 00000000000..e7b048e262f
--- /dev/null
+++ b/tests/ui/floating_point_hypot.rs
@@ -0,0 +1,13 @@
+// run-rustfix
+#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
+
+fn main() {
+    let x = 3f32;
+    let y = 4f32;
+    let _ = (x * x + y * y).sqrt();
+    let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt();
+    let _ = (x.powi(2) + y.powi(2)).sqrt();
+    // Cases where the lint shouldn't be applied
+    let _ = x.mul_add(x, y * y).sqrt();
+    let _ = (x * 4f32 + y * y).sqrt();
+}
diff --git a/tests/ui/floating_point_hypot.stderr b/tests/ui/floating_point_hypot.stderr
new file mode 100644
index 00000000000..fe1dfc7a451
--- /dev/null
+++ b/tests/ui/floating_point_hypot.stderr
@@ -0,0 +1,30 @@
+error: hypotenuse can be computed more accurately
+  --> $DIR/floating_point_hypot.rs:7:13
+   |
+LL |     let _ = (x * x + y * y).sqrt();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)`
+   |
+   = note: `-D clippy::imprecise-flops` implied by `-D warnings`
+
+error: hypotenuse can be computed more accurately
+  --> $DIR/floating_point_hypot.rs:8:13
+   |
+LL |     let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 1f32).hypot(y)`
+
+error: hypotenuse can be computed more accurately
+  --> $DIR/floating_point_hypot.rs:9:13
+   |
+LL |     let _ = (x.powi(2) + y.powi(2)).sqrt();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> $DIR/floating_point_hypot.rs:12:13
+   |
+LL |     let _ = (x * 4f32 + y * y).sqrt();
+   |             ^^^^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(4f32, y * y)`
+   |
+   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/floating_point_mul_add.fixed b/tests/ui/floating_point_mul_add.fixed
index e343c37740d..911700bab00 100644
--- a/tests/ui/floating_point_mul_add.fixed
+++ b/tests/ui/floating_point_mul_add.fixed
@@ -18,4 +18,9 @@ fn main() {
 
     let _ = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c;
     let _ = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64);
+
+    let _ = a.mul_add(a, b).sqrt();
+
+    // Cases where the lint shouldn't be applied
+    let _ = (a * a + b * b).sqrt();
 }
diff --git a/tests/ui/floating_point_mul_add.rs b/tests/ui/floating_point_mul_add.rs
index 810f929c856..d202385fc8a 100644
--- a/tests/ui/floating_point_mul_add.rs
+++ b/tests/ui/floating_point_mul_add.rs
@@ -18,4 +18,9 @@ fn main() {
 
     let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
     let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
+
+    let _ = (a * a + b).sqrt();
+
+    // Cases where the lint shouldn't be applied
+    let _ = (a * a + b * b).sqrt();
 }
diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr
index 2dfbf562d15..ac8d0c0cae0 100644
--- a/tests/ui/floating_point_mul_add.stderr
+++ b/tests/ui/floating_point_mul_add.stderr
@@ -54,5 +54,11 @@ error: multiply and add expressions can be calculated more efficiently and accur
 LL |     let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)`
 
-error: aborting due to 9 previous errors
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> $DIR/floating_point_mul_add.rs:22:13
+   |
+LL |     let _ = (a * a + b).sqrt();
+   |             ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)`
+
+error: aborting due to 10 previous errors
 
diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed
index 0ce6f72535d..98766e68aaf 100644
--- a/tests/ui/floating_point_powi.fixed
+++ b/tests/ui/floating_point_powi.fixed
@@ -1,12 +1,17 @@
 // run-rustfix
-#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
+#![warn(clippy::imprecise_flops)]
 
 fn main() {
     let one = 1;
     let x = 3f32;
     let _ = x * x;
     let _ = x * x;
+
+    let y = 4f32;
+    let _ = (x * x + y).sqrt();
+    let _ = (x + y * y).sqrt();
     // Cases where the lint shouldn't be applied
     let _ = x.powi(3);
     let _ = x.powi(one + 1);
+    let _ = x.hypot(y);
 }
diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs
index c87e836bedd..3c4b636a3d8 100644
--- a/tests/ui/floating_point_powi.rs
+++ b/tests/ui/floating_point_powi.rs
@@ -1,12 +1,17 @@
 // run-rustfix
-#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
+#![warn(clippy::imprecise_flops)]
 
 fn main() {
     let one = 1;
     let x = 3f32;
     let _ = x.powi(2);
     let _ = x.powi(1 + 1);
+
+    let y = 4f32;
+    let _ = (x.powi(2) + y).sqrt();
+    let _ = (x + y.powi(2)).sqrt();
     // Cases where the lint shouldn't be applied
     let _ = x.powi(3);
     let _ = x.powi(one + 1);
+    let _ = (x.powi(2) + y.powi(2)).sqrt();
 }
diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr
index ae7bbaa4473..f370e24bf05 100644
--- a/tests/ui/floating_point_powi.stderr
+++ b/tests/ui/floating_point_powi.stderr
@@ -12,5 +12,23 @@ error: square can be computed more accurately
 LL |     let _ = x.powi(1 + 1);
    |             ^^^^^^^^^^^^^ help: consider using: `x * x`
 
-error: aborting due to 2 previous errors
+error: square can be computed more accurately
+  --> $DIR/floating_point_powi.rs:11:14
+   |
+LL |     let _ = (x.powi(2) + y).sqrt();
+   |              ^^^^^^^^^ help: consider using: `x * x`
+
+error: square can be computed more accurately
+  --> $DIR/floating_point_powi.rs:12:18
+   |
+LL |     let _ = (x + y.powi(2)).sqrt();
+   |                  ^^^^^^^^^ help: consider using: `y * y`
+
+error: hypotenuse can be computed more accurately
+  --> $DIR/floating_point_powi.rs:16:13
+   |
+LL |     let _ = (x.powi(2) + y.powi(2)).sqrt();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)`
+
+error: aborting due to 5 previous errors