about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/floating_point_arithmetic.rs66
-rw-r--r--clippy_utils/src/hir_utils.rs82
-rw-r--r--clippy_utils/src/lib.rs3
-rw-r--r--clippy_utils/src/visitors.rs10
-rw-r--r--tests/ui/floating_point_mul_add.fixed10
-rw-r--r--tests/ui/floating_point_mul_add.rs10
-rw-r--r--tests/ui/floating_point_mul_add.stderr18
7 files changed, 112 insertions, 87 deletions
diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs
index 39b8e8c54fa..d5abaa547e8 100644
--- a/clippy_lints/src/floating_point_arithmetic.rs
+++ b/clippy_lints/src/floating_point_arithmetic.rs
@@ -2,13 +2,12 @@ use clippy_utils::consts::Constant::{F32, F64, Int};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::{
-    eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate,
-    numeric_literal, peel_blocks, sugg, sym,
+    eq_expr_value, get_parent_expr, has_ambiguous_literal_in_expr, higher, is_in_const_context,
+    is_inherent_method_call, is_no_std_crate, numeric_literal, peel_blocks, sugg, sym,
 };
 use rustc_ast::ast;
 use rustc_errors::Applicability;
-use rustc_hir::def::Res;
-use rustc_hir::{BinOpKind, Expr, ExprKind, Node, PathSegment, UnOp};
+use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
@@ -456,61 +455,6 @@ fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'
     None
 }
 
-// Check if any variable in an expression has an ambiguous type (could be f32 or f64)
-fn has_ambiguous_float_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    match &expr.kind {
-        ExprKind::Path(qpath) => {
-            if let Res::Local(hir_id) = cx.qpath_res(qpath, expr.hir_id) {
-                if let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id) {
-                    // If the local has no type annotation, check if the initializer has ambiguous float literals
-                    if local.ty.is_none() {
-                        if let Some(init) = local.init {
-                            return has_ambiguous_float_literal_in_expr(cx, init);
-                        }
-                    }
-                }
-            }
-            false
-        },
-        _ => false, // only check path
-    }
-}
-
-// Recursively check if an expression contains any unsuffixed float literals
-fn has_ambiguous_float_literal_in_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    match &expr.kind {
-        ExprKind::Lit(lit) => {
-            if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node {
-                return true;
-            }
-            false
-        },
-        ExprKind::Binary(_, lhs, rhs) => {
-            has_ambiguous_float_literal_in_expr(cx, lhs) || has_ambiguous_float_literal_in_expr(cx, rhs)
-        },
-        ExprKind::Unary(_, expr) => has_ambiguous_float_literal_in_expr(cx, expr),
-        ExprKind::If(_, then, else_) => {
-            has_ambiguous_float_literal_in_expr(cx, then)
-                || else_
-                    .as_ref()
-                    .map_or(false, |else_expr| has_ambiguous_float_literal_in_expr(cx, else_expr))
-        },
-        ExprKind::Block(block, _) => block
-            .expr
-            .as_ref()
-            .map_or(false, |expr| has_ambiguous_float_literal_in_expr(cx, expr)),
-        ExprKind::MethodCall(_, receiver, args, _) => {
-            has_ambiguous_float_literal_in_expr(cx, receiver)
-                || args.iter().any(|arg| has_ambiguous_float_literal_in_expr(cx, arg))
-        },
-        ExprKind::Call(func, args) => {
-            has_ambiguous_float_literal_in_expr(cx, func)
-                || args.iter().any(|arg| has_ambiguous_float_literal_in_expr(cx, arg))
-        },
-        _ => false,
-    }
-}
-
 fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
     if let ExprKind::Binary(
         Spanned {
@@ -548,7 +492,9 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
 
         // Check if any variable in the expression has an ambiguous type (could be f32 or f64)
         // see: https://github.com/rust-lang/rust-clippy/issues/14897
-        if has_ambiguous_float_type(cx, recv) {
+        if (matches!(recv.kind, ExprKind::Path(_)) || matches!(recv.kind, ExprKind::Call(_, _)))
+            && has_ambiguous_literal_in_expr(cx, recv)
+        {
             return;
         }
 
diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs
index c37231d0931..e249f87058d 100644
--- a/clippy_utils/src/hir_utils.rs
+++ b/clippy_utils/src/hir_utils.rs
@@ -2,6 +2,7 @@ use crate::consts::ConstEvalCtxt;
 use crate::macros::macro_backtrace;
 use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context};
 use crate::tokenize_with_text;
+use rustc_ast::ast;
 use rustc_ast::ast::InlineAsmTemplatePiece;
 use rustc_data_structures::fx::FxHasher;
 use rustc_hir::MatchSource::TryDesugar;
@@ -9,8 +10,8 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{
     AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField,
     ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeKind,
-    Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr,
-    TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind,
+    Node, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
+    StructTailExpr, TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind,
 };
 use rustc_lexer::{TokenKind, tokenize};
 use rustc_lint::LateContext;
@@ -1004,8 +1005,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                     self.hash_expr(e);
                 }
             },
-            ExprKind::Match(e, arms, s) => {
-                self.hash_expr(e);
+            ExprKind::Match(scrutinee, arms, _) => {
+                self.hash_expr(scrutinee);
 
                 for arm in *arms {
                     self.hash_pat(arm.pat);
@@ -1014,8 +1015,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                     }
                     self.hash_expr(arm.body);
                 }
-
-                s.hash(&mut self.s);
             },
             ExprKind::MethodCall(path, receiver, args, _fn_span) => {
                 self.hash_name(path.ident.name);
@@ -1058,8 +1057,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
             ExprKind::Use(expr, _) => {
                 self.hash_expr(expr);
             },
-            ExprKind::Unary(lop, le) => {
-                std::mem::discriminant(lop).hash(&mut self.s);
+            ExprKind::Unary(l_op, le) => {
+                std::mem::discriminant(l_op).hash(&mut self.s);
                 self.hash_expr(le);
             },
             ExprKind::UnsafeBinderCast(kind, expr, ty) => {
@@ -1394,3 +1393,70 @@ fn eq_span_tokens(
     }
     f(cx, left.into_range(), right.into_range(), pred)
 }
+
+/// Returns true if the expression contains ambiguous literals (unsuffixed float or int literals)
+/// that could be interpreted as either f32/f64 or i32/i64 depending on context.
+pub fn has_ambiguous_literal_in_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    match expr.kind {
+        ExprKind::Path(ref qpath) => {
+            if let Res::Local(hir_id) = cx.qpath_res(qpath, expr.hir_id)
+                && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
+                && local.ty.is_none()
+                && let Some(init) = local.init
+            {
+                return has_ambiguous_literal_in_expr(cx, init);
+            }
+            false
+        },
+        ExprKind::Lit(lit) => matches!(
+            lit.node,
+            ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) | ast::LitKind::Int(_, ast::LitIntType::Unsuffixed)
+        ),
+
+        ExprKind::Array(exprs) | ExprKind::Tup(exprs) => exprs.iter().any(|e| has_ambiguous_literal_in_expr(cx, e)),
+
+        ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Binary(_, lhs, rhs) => {
+            has_ambiguous_literal_in_expr(cx, lhs) || has_ambiguous_literal_in_expr(cx, rhs)
+        },
+
+        ExprKind::Unary(_, e)
+        | ExprKind::Cast(e, _)
+        | ExprKind::Type(e, _)
+        | ExprKind::DropTemps(e)
+        | ExprKind::AddrOf(_, _, e)
+        | ExprKind::Field(e, _)
+        | ExprKind::Index(e, _, _)
+        | ExprKind::Yield(e, _) => has_ambiguous_literal_in_expr(cx, e),
+
+        ExprKind::MethodCall(_, receiver, args, _) | ExprKind::Call(receiver, args) => {
+            has_ambiguous_literal_in_expr(cx, receiver) || args.iter().any(|e| has_ambiguous_literal_in_expr(cx, e))
+        },
+
+        ExprKind::Closure(Closure { body, .. }) => {
+            let body = cx.tcx.hir_body(*body);
+            let closure_expr = crate::peel_blocks(body.value);
+            has_ambiguous_literal_in_expr(cx, closure_expr)
+        },
+
+        ExprKind::Block(blk, _) => blk.expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)),
+
+        ExprKind::If(cond, then_expr, else_expr) => {
+            has_ambiguous_literal_in_expr(cx, cond)
+                || has_ambiguous_literal_in_expr(cx, then_expr)
+                || else_expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e))
+        },
+
+        ExprKind::Match(scrutinee, arms, _) => {
+            has_ambiguous_literal_in_expr(cx, scrutinee)
+                || arms.iter().any(|arm| has_ambiguous_literal_in_expr(cx, arm.body))
+        },
+
+        ExprKind::Loop(body, ..) => body.expr.is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)),
+
+        ExprKind::Ret(opt_expr) | ExprKind::Break(_, opt_expr) => {
+            opt_expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e))
+        },
+
+        _ => false,
+    }
+}
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 7fa52229fef..ade839cd441 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -77,7 +77,8 @@ pub mod visitors;
 pub use self::attrs::*;
 pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
 pub use self::hir_utils::{
-    HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over,
+    HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr,
+    hash_stmt, is_bool, over,
 };
 
 use core::mem;
diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs
index fc6e30a9804..48f1c36117c 100644
--- a/clippy_utils/src/visitors.rs
+++ b/clippy_utils/src/visitors.rs
@@ -353,7 +353,7 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) ->
                 ExprKind::Binary(_, lhs, rhs)
                     if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
                         && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
-                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (),
+                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => (),
                 ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (),
                 ExprKind::Index(base, _, _)
                     if matches!(
@@ -388,7 +388,8 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) ->
                 | ExprKind::Repeat(..)
                 | ExprKind::Struct(..)
                 | ExprKind::Tup(_)
-                | ExprKind::Type(..) => (),
+                | ExprKind::Type(..)
+                | ExprKind::UnsafeBinderCast(..) => (),
 
                 _ => {
                     return ControlFlow::Break(());
@@ -676,10 +677,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
                     helper(typeck, true, else_expr, f)?;
                 }
             },
-            ExprKind::Type(e, _) => {
-                helper(typeck, consume, e, f)?;
-            },
-            ExprKind::UnsafeBinderCast(_, e, _) => {
+            ExprKind::Type(e, _) | ExprKind::UnsafeBinderCast(_, e, _) => {
                 helper(typeck, consume, e, f)?;
             },
 
diff --git a/tests/ui/floating_point_mul_add.fixed b/tests/ui/floating_point_mul_add.fixed
index b07f3a6cc69..884bae00432 100644
--- a/tests/ui/floating_point_mul_add.fixed
+++ b/tests/ui/floating_point_mul_add.fixed
@@ -89,6 +89,16 @@ fn _issue14897() {
     let x = 2.4 + 1.2;
     let _ = 0.5 + x * 1.2; // should not suggest mul_add
 
+    let f = || 4.0;
+    let x = f();
+    let _ = 0.5 + f() * 1.2; // should not suggest mul_add
+    let _ = 0.5 + x * 1.2; // should not suggest mul_add
+
+    let x = 0.1;
+    let y = x;
+    let z = y;
+    let _ = 0.5 + z * 1.2; // should not suggest mul_add
+
     let _ = 2.0f64.mul_add(x, 0.5);
     //~^ suboptimal_flops
     let _ = 2.0f64.mul_add(x, 0.5);
diff --git a/tests/ui/floating_point_mul_add.rs b/tests/ui/floating_point_mul_add.rs
index 8af9c51f271..9ceb2ec9606 100644
--- a/tests/ui/floating_point_mul_add.rs
+++ b/tests/ui/floating_point_mul_add.rs
@@ -89,6 +89,16 @@ fn _issue14897() {
     let x = 2.4 + 1.2;
     let _ = 0.5 + x * 1.2; // should not suggest mul_add
 
+    let f = || 4.0;
+    let x = f();
+    let _ = 0.5 + f() * 1.2; // should not suggest mul_add
+    let _ = 0.5 + x * 1.2; // should not suggest mul_add
+
+    let x = 0.1;
+    let y = x;
+    let z = y;
+    let _ = 0.5 + z * 1.2; // should not suggest mul_add
+
     let _ = 0.5 + 2.0 * x;
     //~^ suboptimal_flops
     let _ = 2.0 * x + 0.5;
diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr
index 80d08c7de73..dad65ddf2ec 100644
--- a/tests/ui/floating_point_mul_add.stderr
+++ b/tests/ui/floating_point_mul_add.stderr
@@ -80,40 +80,34 @@ LL |     let _ = a - (b * u as f64);
    |             ^^^^^^^^^^^^^^^^^^ help: consider using: `b.mul_add(-(u as f64), a)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:85:13
-   |
-LL |     let _ = 0.5 + x * 1.2; // should not suggest mul_add
-   |             ^^^^^^^^^^^^^ help: consider using: `x.mul_add(1.2, 0.5)`
-
-error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:96:13
+  --> tests/ui/floating_point_mul_add.rs:102:13
    |
 LL |     let _ = 0.5 + 2.0 * x;
    |             ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:98:13
+  --> tests/ui/floating_point_mul_add.rs:104:13
    |
 LL |     let _ = 2.0 * x + 0.5;
    |             ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:101:13
+  --> tests/ui/floating_point_mul_add.rs:107:13
    |
 LL |     let _ = x + 2.0 * 4.0;
    |             ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, x)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:105:13
+  --> tests/ui/floating_point_mul_add.rs:111:13
    |
 LL |     let _ = y * 2.0 + 0.5;
    |             ^^^^^^^^^^^^^ help: consider using: `y.mul_add(2.0, 0.5)`
 
 error: multiply and add expressions can be calculated more efficiently and accurately
-  --> tests/ui/floating_point_mul_add.rs:107:13
+  --> tests/ui/floating_point_mul_add.rs:113:13
    |
 LL |     let _ = 1.0 * 2.0 + 0.5;
    |             ^^^^^^^^^^^^^^^ help: consider using: `1.0f64.mul_add(2.0, 0.5)`
 
-error: aborting due to 19 previous errors
+error: aborting due to 18 previous errors