]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/floating_point_arithmetic.rs
Last PR adjustments
[rust.git] / clippy_lints / src / floating_point_arithmetic.rs
index 4fefd9fd18a0a00abdbdb95b7729c84686418e11..f95b628e6c31eb67b3bbd17e9b22534d0460ebe8 100644 (file)
@@ -35,8 +35,7 @@
     /// let _ = a.exp() - 1.0;
     /// ```
     ///
-    /// is better expressed as
-    ///
+    /// Use instead:
     /// ```rust
     /// let a = 3f32;
     /// let _ = a.cbrt();
@@ -143,8 +142,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su
         if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node;
         then {
             let op = format!(
-                "{}{}{}",
-                suggestion,
+                "{suggestion}{}{}",
                 // Check for float literals without numbers following the decimal
                 // separator such as `2.` and adds a trailing zero
                 if sym.as_str().ends_with('.') {
@@ -165,15 +163,15 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su
     suggestion.maybe_par()
 }
 
-fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
-    if let Some(method) = get_specialized_log_method(cx, &args[1]) {
+fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
+    if let Some(method) = get_specialized_log_method(cx, &args[0]) {
         span_lint_and_sugg(
             cx,
             SUBOPTIMAL_FLOPS,
             expr.span,
             "logarithm for bases 2, 10 and e can be computed more accurately",
             "consider using",
-            format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method),
+            format!("{}.{method}()", Sugg::hir(cx, receiver, "..").maybe_par()),
             Applicability::MachineApplicable,
         );
     }
@@ -181,14 +179,14 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 
 // TODO: Lint expressions of the form `(x + y).ln()` where y > 1 and
 // suggest usage of `(x + (y - 1)).ln_1p()` instead
-fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
+fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
     if let ExprKind::Binary(
         Spanned {
             node: BinOpKind::Add, ..
         },
         lhs,
         rhs,
-    ) = &args[0].kind
+    ) = receiver.kind
     {
         let recv = match (
             constant(cx, cx.typeck_results(), lhs),
@@ -215,7 +213,7 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 // converted to an integer without loss of precision. For now we only check
 // ranges [-16777215, 16777216) for type f32 as whole number floats outside
 // this range are lossy and ambiguous.
-#[allow(clippy::cast_possible_truncation)]
+#[expect(clippy::cast_possible_truncation)]
 fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
     match value {
         F32(num) if num.fract() == 0.0 => {
@@ -236,41 +234,41 @@ fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
     }
 }
 
-fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
+fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
     // Check receiver
-    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) {
-        let method = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
-            "exp"
+    if let Some((value, _)) = constant(cx, cx.typeck_results(), receiver) {
+        if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
+            Some("exp")
         } else if F32(2.0) == value || F64(2.0) == value {
-            "exp2"
+            Some("exp2")
         } else {
-            return;
-        };
-
-        span_lint_and_sugg(
-            cx,
-            SUBOPTIMAL_FLOPS,
-            expr.span,
-            "exponent for bases 2 and e can be computed more accurately",
-            "consider using",
-            format!("{}.{}()", prepare_receiver_sugg(cx, &args[1]), method),
-            Applicability::MachineApplicable,
-        );
+            None
+        } {
+            span_lint_and_sugg(
+                cx,
+                SUBOPTIMAL_FLOPS,
+                expr.span,
+                "exponent for bases 2 and e can be computed more accurately",
+                "consider using",
+                format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])),
+                Applicability::MachineApplicable,
+            );
+        }
     }
 
     // Check argument
-    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[1]) {
+    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) {
         let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
             (
                 SUBOPTIMAL_FLOPS,
                 "square-root of a number can be computed more efficiently and accurately",
-                format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")),
+                format!("{}.sqrt()", Sugg::hir(cx, receiver, "..").maybe_par()),
             )
         } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
             (
                 IMPRECISE_FLOPS,
                 "cube-root of a number can be computed more accurately",
-                format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..")),
+                format!("{}.cbrt()", Sugg::hir(cx, receiver, "..").maybe_par()),
             )
         } else if let Some(exponent) = get_integer_from_float_constant(&value) {
             (
@@ -278,7 +276,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
                 "exponentiation with integer powers can be computed more efficiently",
                 format!(
                     "{}.powi({})",
-                    Sugg::hir(cx, &args[0], ".."),
+                    Sugg::hir(cx, receiver, "..").maybe_par(),
                     numeric_literal::format(&exponent.to_string(), None, false)
                 ),
             )
@@ -298,13 +296,14 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
     }
 }
 
-fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
-    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[1]) {
+fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
+    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) {
         if value == Int(2) {
             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() {
+                    if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = grandparent.kind
+                    {
+                        if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() {
                             return;
                         }
                     }
@@ -312,7 +311,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 
                 if let ExprKind::Binary(
                     Spanned {
-                        node: BinOpKind::Add, ..
+                        node: op @ (BinOpKind::Add | BinOpKind::Sub),
+                        ..
                     },
                     lhs,
                     rhs,
@@ -320,6 +320,16 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
                 {
                     let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
 
+                    // Negate expr if original code has subtraction and expr is on the right side
+                    let maybe_neg_sugg = |expr, hir_id| {
+                        let sugg = Sugg::hir(cx, expr, "..");
+                        if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id {
+                            format!("-{}", sugg.maybe_par())
+                        } else {
+                            sugg.to_string()
+                        }
+                    };
+
                     span_lint_and_sugg(
                         cx,
                         SUBOPTIMAL_FLOPS,
@@ -328,9 +338,9 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
                         "consider using",
                         format!(
                             "{}.mul_add({}, {})",
-                            Sugg::hir(cx, &args[0], ".."),
-                            Sugg::hir(cx, &args[0], ".."),
-                            Sugg::hir(cx, other_addend, ".."),
+                            Sugg::hir(cx, receiver, "..").maybe_par(),
+                            maybe_neg_sugg(receiver, expr.hir_id),
+                            maybe_neg_sugg(other_addend, other_addend.hir_id),
                         ),
                         Applicability::MachineApplicable,
                     );
@@ -340,14 +350,14 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
     }
 }
 
-fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
+fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> {
     if let ExprKind::Binary(
         Spanned {
             node: BinOpKind::Add, ..
         },
         add_lhs,
         add_rhs,
-    ) = args[0].kind
+    ) = receiver.kind
     {
         // check if expression of the form x * x + y * y
         if_chain! {
@@ -364,14 +374,12 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
         if_chain! {
             if let ExprKind::MethodCall(
                 PathSegment { ident: lmethod_name, .. },
-                _lspan,
-                [largs_0, largs_1, ..],
+                largs_0, [largs_1, ..],
                 _
             ) = &add_lhs.kind;
             if let ExprKind::MethodCall(
                 PathSegment { ident: rmethod_name, .. },
-                _rspan,
-                [rargs_0, rargs_1, ..],
+                rargs_0, [rargs_1, ..],
                 _
             ) = &add_rhs.kind;
             if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi";
@@ -387,8 +395,8 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
     None
 }
 
-fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
-    if let Some(message) = detect_hypot(cx, args) {
+fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
+    if let Some(message) = detect_hypot(cx, receiver) {
         span_lint_and_sugg(
             cx,
             IMPRECISE_FLOPS,
@@ -409,7 +417,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
         if cx.typeck_results().expr_ty(lhs).is_floating_point();
         if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs);
         if F32(1.0) == value || F64(1.0) == value;
-        if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &lhs.kind;
+        if let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind;
         if cx.typeck_results().expr_ty(self_arg).is_floating_point();
         if path.ident.name.as_str() == "exp";
         then {
@@ -421,7 +429,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
                 "consider using",
                 format!(
                     "{}.exp_m1()",
-                    Sugg::hir(cx, self_arg, "..")
+                    Sugg::hir(cx, self_arg, "..").maybe_par()
                 ),
                 Applicability::MachineApplicable,
             );
@@ -446,24 +454,42 @@ fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'
 fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
     if let ExprKind::Binary(
         Spanned {
-            node: BinOpKind::Add, ..
+            node: op @ (BinOpKind::Add | BinOpKind::Sub),
+            ..
         },
         lhs,
         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() {
+            if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = parent.kind {
+                if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() {
                     return;
                 }
             }
         }
 
+        let maybe_neg_sugg = |expr| {
+            let sugg = Sugg::hir(cx, expr, "..");
+            if let BinOpKind::Sub = op {
+                format!("-{sugg}")
+            } else {
+                sugg.to_string()
+            }
+        };
+
         let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
-            (inner_lhs, inner_rhs, rhs)
+            (
+                inner_lhs,
+                Sugg::hir(cx, inner_rhs, "..").to_string(),
+                maybe_neg_sugg(rhs),
+            )
         } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
-            (inner_lhs, inner_rhs, lhs)
+            (
+                inner_lhs,
+                maybe_neg_sugg(inner_rhs),
+                Sugg::hir(cx, lhs, "..").to_string(),
+            )
         } else {
             return;
         };
@@ -474,12 +500,7 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
             expr.span,
             "multiply and add expressions can be calculated more efficiently and accurately",
             "consider using",
-            format!(
-                "{}.mul_add({}, {})",
-                prepare_receiver_sugg(cx, recv),
-                Sugg::hir(cx, arg1, ".."),
-                Sugg::hir(cx, arg2, ".."),
-            ),
+            format!("{}.mul_add({arg1}, {arg2})", prepare_receiver_sugg(cx, recv)),
             Applicability::MachineApplicable,
         );
     }
@@ -553,11 +574,11 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
         then {
             let positive_abs_sugg = (
                 "manual implementation of `abs` method",
-                format!("{}.abs()", Sugg::hir(cx, body, "..")),
+                format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
             );
             let negative_abs_sugg = (
                 "manual implementation of negation of `abs` method",
-                format!("-{}.abs()", Sugg::hir(cx, body, "..")),
+                format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
             );
             let sugg = if is_testing_positive(cx, cond, body) {
                 if if_expr_positive {
@@ -595,8 +616,8 @@ fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>
             return method_name_a.as_str() == method_name_b.as_str() &&
                 args_a.len() == args_b.len() &&
                 (
-                    ["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) ||
-                    method_name_a.as_str() == "log" && args_a.len() == 2 && eq_expr_value(cx, &args_a[1], &args_b[1])
+                    ["ln", "log2", "log10"].contains(&method_name_a.as_str()) ||
+                    method_name_a.as_str() == "log" && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0])
                 );
         }
     }
@@ -615,8 +636,8 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
             rhs,
         ) = &expr.kind;
         if are_same_base_logs(cx, lhs, rhs);
-        if let ExprKind::MethodCall(_, _, [largs_self, ..], _) = &lhs.kind;
-        if let ExprKind::MethodCall(_, _, [rargs_self, ..], _) = &rhs.kind;
+        if let ExprKind::MethodCall(_, largs_self, ..) = &lhs.kind;
+        if let ExprKind::MethodCall(_, rargs_self, ..) = &rhs.kind;
         then {
             span_lint_and_sugg(
                 cx,
@@ -624,7 +645,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
                 expr.span,
                 "log base can be expressed more clearly",
                 "consider using",
-                format!("{}.log({})", Sugg::hir(cx, largs_self, ".."), Sugg::hir(cx, rargs_self, ".."),),
+                format!("{}.log({})", Sugg::hir(cx, largs_self, "..").maybe_par(), Sugg::hir(cx, rargs_self, ".."),),
                 Applicability::MachineApplicable,
             );
         }
@@ -654,7 +675,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
             if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
                (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
             {
-                let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
+                let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
                 if_chain! {
                     if let ExprKind::Lit(ref literal) = mul_lhs.kind;
                     if let ast::LitKind::Float(ref value, float_type) = literal.node;
@@ -680,7 +701,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
                 (F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
                 (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
             {
-                let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
+                let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
                 if_chain! {
                     if let ExprKind::Lit(ref literal) = mul_lhs.kind;
                     if let ast::LitKind::Float(ref value, float_type) = literal.node;
@@ -714,16 +735,16 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             return;
         }
 
-        if let ExprKind::MethodCall(path, _, args, _) = &expr.kind {
-            let recv_ty = cx.typeck_results().expr_ty(&args[0]);
+        if let ExprKind::MethodCall(path, receiver, args, _) = &expr.kind {
+            let recv_ty = cx.typeck_results().expr_ty(receiver);
 
             if recv_ty.is_floating_point() {
-                match &*path.ident.name.as_str() {
-                    "ln" => check_ln1p(cx, expr, args),
-                    "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),
+                match path.ident.name.as_str() {
+                    "ln" => check_ln1p(cx, expr, receiver),
+                    "log" => check_log_base(cx, expr, receiver, args),
+                    "powf" => check_powf(cx, expr, receiver, args),
+                    "powi" => check_powi(cx, expr, receiver, args),
+                    "sqrt" => check_hypot(cx, expr, receiver),
                     _ => {},
                 }
             }