about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/casts/cast_lossless.rs87
-rw-r--r--clippy_lints/src/casts/mod.rs79
2 files changed, 92 insertions, 74 deletions
diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs
new file mode 100644
index 00000000000..478832a5164
--- /dev/null
+++ b/clippy_lints/src/casts/cast_lossless.rs
@@ -0,0 +1,87 @@
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, FloatTy, Ty};
+
+use crate::utils::{in_constant, is_isize_or_usize, snippet_opt, span_lint_and_sugg};
+
+use super::{utils, CAST_LOSSLESS};
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
+    if !should_lint(cx, expr, cast_from, cast_to) {
+        return;
+    }
+
+    // The suggestion is to use a function call, so if the original expression
+    // has parens on the outside, they are no longer needed.
+    let mut applicability = Applicability::MachineApplicable;
+    let opt = snippet_opt(cx, cast_op.span);
+    let sugg = opt.as_ref().map_or_else(
+        || {
+            applicability = Applicability::HasPlaceholders;
+            ".."
+        },
+        |snip| {
+            if should_strip_parens(cast_op, snip) {
+                &snip[1..snip.len() - 1]
+            } else {
+                snip.as_str()
+            }
+        },
+    );
+
+    span_lint_and_sugg(
+        cx,
+        CAST_LOSSLESS,
+        expr.span,
+        &format!(
+            "casting `{}` to `{}` may become silently lossy if you later change the type",
+            cast_from, cast_to
+        ),
+        "try",
+        format!("{}::from({})", cast_to, sugg),
+        applicability,
+    );
+}
+
+fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) -> bool {
+    // Do not suggest using From in consts/statics until it is valid to do so (see #2267).
+    if in_constant(cx, expr.hir_id) {
+        return false;
+    }
+
+    match (cast_from.is_integral(), cast_to.is_integral()) {
+        (true, true) => {
+            let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed();
+            let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
+            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
+            !is_isize_or_usize(cast_from)
+                && !is_isize_or_usize(cast_to)
+                && from_nbits < to_nbits
+                && !cast_signed_to_unsigned
+        },
+
+        (true, false) => {
+            let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
+            let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() {
+                32
+            } else {
+                64
+            };
+            from_nbits < to_nbits
+        },
+
+        (_, _) => {
+            matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
+        },
+    }
+}
+
+fn should_strip_parens(cast_expr: &Expr<'_>, snip: &str) -> bool {
+    if let ExprKind::Binary(_, _, _) = cast_expr.kind {
+        if snip.starts_with('(') && snip.ends_with(')') {
+            return true;
+        }
+    }
+    false
+}
diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs
index 7eb35aa8290..6c2464e0090 100644
--- a/clippy_lints/src/casts/mod.rs
+++ b/clippy_lints/src/casts/mod.rs
@@ -1,3 +1,4 @@
+mod cast_lossless;
 mod cast_precision_loss;
 mod utils;
 
@@ -18,9 +19,8 @@ use rustc_target::abi::LayoutOf;
 use crate::consts::{constant, Constant};
 use crate::utils::sugg::Sugg;
 use crate::utils::{
-    in_constant, is_hir_ty_cfg_dependant, is_isize_or_usize, meets_msrv, method_chain_args,
-    numeric_literal::NumericLiteral, sext, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_sugg,
-    span_lint_and_then,
+    is_hir_ty_cfg_dependant, is_isize_or_usize, meets_msrv, method_chain_args, numeric_literal::NumericLiteral, sext,
+    snippet_opt, snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then,
 };
 
 use utils::int_ty_to_nbits;
@@ -254,52 +254,6 @@ declare_clippy_lint! {
     "casting a function pointer to a numeric type not wide enough to store the address"
 }
 
-fn should_strip_parens(op: &Expr<'_>, snip: &str) -> bool {
-    if let ExprKind::Binary(_, _, _) = op.kind {
-        if snip.starts_with('(') && snip.ends_with(')') {
-            return true;
-        }
-    }
-    false
-}
-
-fn span_lossless_lint(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
-    // Do not suggest using From in consts/statics until it is valid to do so (see #2267).
-    if in_constant(cx, expr.hir_id) {
-        return;
-    }
-    // The suggestion is to use a function call, so if the original expression
-    // has parens on the outside, they are no longer needed.
-    let mut applicability = Applicability::MachineApplicable;
-    let opt = snippet_opt(cx, op.span);
-    let sugg = opt.as_ref().map_or_else(
-        || {
-            applicability = Applicability::HasPlaceholders;
-            ".."
-        },
-        |snip| {
-            if should_strip_parens(op, snip) {
-                &snip[1..snip.len() - 1]
-            } else {
-                snip.as_str()
-            }
-        },
-    );
-
-    span_lint_and_sugg(
-        cx,
-        CAST_LOSSLESS,
-        expr.span,
-        &format!(
-            "casting `{}` to `{}` may become silently lossy if you later change the type",
-            cast_from, cast_to
-        ),
-        "try",
-        format!("{}::from({})", cast_to, sugg),
-        applicability,
-    );
-}
-
 enum ArchSuffix {
     _32,
     _64,
@@ -423,16 +377,6 @@ fn check_truncation_and_wrapping(cx: &LateContext<'_>, expr: &Expr<'_>, cast_fro
     }
 }
 
-fn check_lossless(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
-    let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed();
-    let from_nbits = int_ty_to_nbits(cast_from, cx.tcx);
-    let to_nbits = int_ty_to_nbits(cast_to, cx.tcx);
-    if !is_isize_or_usize(cast_from) && !is_isize_or_usize(cast_to) && from_nbits < to_nbits && !cast_signed_to_unsigned
-    {
-        span_lossless_lint(cx, expr, op, cast_from, cast_to);
-    }
-}
-
 declare_lint_pass!(Casts => [
     CAST_PRECISION_LOSS,
     CAST_SIGN_LOSS,
@@ -584,18 +528,8 @@ fn lint_numeric_casts<'tcx>(
     cast_to: Ty<'tcx>,
 ) {
     cast_precision_loss::check(cx, expr, cast_from, cast_to);
+    cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to);
     match (cast_from.is_integral(), cast_to.is_integral()) {
-        (true, false) => {
-            let from_nbits = int_ty_to_nbits(cast_from, cx.tcx);
-            let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() {
-                32
-            } else {
-                64
-            };
-            if from_nbits < to_nbits {
-                span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to);
-            }
-        },
         (false, true) => {
             span_lint(
                 cx,
@@ -618,7 +552,6 @@ fn lint_numeric_casts<'tcx>(
         (true, true) => {
             check_loss_of_sign(cx, expr, cast_expr, cast_from, cast_to);
             check_truncation_and_wrapping(cx, expr, cast_from, cast_to);
-            check_lossless(cx, expr, cast_expr, cast_from, cast_to);
         },
         (false, false) => {
             if let (&ty::Float(FloatTy::F64), &ty::Float(FloatTy::F32)) = (&cast_from.kind(), &cast_to.kind()) {
@@ -629,10 +562,8 @@ fn lint_numeric_casts<'tcx>(
                     "casting `f64` to `f32` may truncate the value",
                 );
             }
-            if let (&ty::Float(FloatTy::F32), &ty::Float(FloatTy::F64)) = (&cast_from.kind(), &cast_to.kind()) {
-                span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to);
-            }
         },
+        (_, _) => {},
     }
 }