about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2025-05-25 15:57:10 +0000
committerMichael Goulet <michael@errs.io>2025-05-25 15:57:48 +0000
commit295a8d56f5a7d200599d587fb52bf217b9aee363 (patch)
treea27cea9b8aa90423d0b15615c41ad49371aec5fc
parent5370c5753f3f769d27832f81ceefd4141ba1ee0c (diff)
downloadrust-295a8d56f5a7d200599d587fb52bf217b9aee363.tar.gz
rust-295a8d56f5a7d200599d587fb52bf217b9aee363.zip
Make UNNECESSARY_TRANSMUTES into a HIR lint
-rw-r--r--compiler/rustc_lint/src/transmute.rs232
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs25
-rw-r--r--compiler/rustc_mir_transform/messages.ftl1
-rw-r--r--compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs136
-rw-r--r--compiler/rustc_mir_transform/src/errors.rs20
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs2
-rw-r--r--tests/ui/transmute/unnecessary-transmutation.stderr226
7 files changed, 374 insertions, 268 deletions
diff --git a/compiler/rustc_lint/src/transmute.rs b/compiler/rustc_lint/src/transmute.rs
index 8395bcf7cad..bc1d4587d07 100644
--- a/compiler/rustc_lint/src/transmute.rs
+++ b/compiler/rustc_lint/src/transmute.rs
@@ -1,6 +1,9 @@
+use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::LocalDefId;
 use rustc_hir::{self as hir};
 use rustc_macros::LintDiagnostic;
+use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_lint, impl_lint_pass};
 use rustc_span::sym;
 
@@ -40,13 +43,37 @@ declare_lint! {
     "detects pointer to integer transmutes in const functions and associated constants",
 }
 
+declare_lint! {
+    /// The `unnecessary_transmutes` lint detects transmutations that have safer alternatives.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// fn bytes_at_home(x: [u8; 4]) -> u32 {
+    ///   unsafe { std::mem::transmute(x) }
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Using an explicit method is preferable over calls to
+    /// [`transmute`](https://doc.rust-lang.org/std/mem/fn.transmute.html) as
+    /// they more clearly communicate the intent, are easier to review, and
+    /// are less likely to accidentally result in unsoundness.
+    pub UNNECESSARY_TRANSMUTES,
+    Warn,
+    "detects transmutes that can also be achieved by other operations"
+}
+
 pub(crate) struct CheckTransmutes;
 
-impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS]);
+impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, UNNECESSARY_TRANSMUTES]);
 
 impl<'tcx> LateLintPass<'tcx> for CheckTransmutes {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
-        let hir::ExprKind::Call(callee, _) = expr.kind else {
+        let hir::ExprKind::Call(callee, [arg]) = expr.kind else {
             return;
         };
         let hir::ExprKind::Path(qpath) = callee.kind else {
@@ -59,41 +86,190 @@ impl<'tcx> LateLintPass<'tcx> for CheckTransmutes {
             return;
         };
         let body_owner_def_id = cx.tcx.hir_enclosing_body_owner(expr.hir_id);
-        let Some(context) = cx.tcx.hir_body_const_context(body_owner_def_id) else {
-            return;
-        };
+        let const_context = cx.tcx.hir_body_const_context(body_owner_def_id);
         let args = cx.typeck_results().node_args(callee.hir_id);
 
         let src = args.type_at(0);
         let dst = args.type_at(1);
 
-        // Check for transmutes that exhibit undefined behavior.
-        // For example, transmuting pointers to integers in a const context.
-        //
-        // Why do we consider const functions and associated constants only?
-        //
-        // Generally, undefined behavior in const items are handled by the evaluator.
-        // But, const functions and associated constants are evaluated only when referenced.
-        // This can result in undefined behavior in a library going unnoticed until
-        // the function or constant is actually used.
-        //
-        // Therefore, we only consider const functions and associated constants here and leave
-        // other const items to be handled by the evaluator.
-        if matches!(context, hir::ConstContext::ConstFn)
-            || matches!(cx.tcx.def_kind(body_owner_def_id), DefKind::AssocConst)
-        {
-            if src.is_raw_ptr() && dst.is_integral() {
-                cx.tcx.emit_node_span_lint(
-                    PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS,
-                    expr.hir_id,
-                    expr.span,
-                    UndefinedTransmuteLint,
-                );
-            }
+        check_ptr_transmute_in_const(cx, expr, body_owner_def_id, const_context, src, dst);
+        check_unnecessary_transmute(cx, expr, callee, arg, const_context, src, dst);
+    }
+}
+
+/// Check for transmutes that exhibit undefined behavior.
+/// For example, transmuting pointers to integers in a const context.
+///
+/// Why do we consider const functions and associated constants only?
+///
+/// Generally, undefined behavior in const items are handled by the evaluator.
+/// But, const functions and associated constants are evaluated only when referenced.
+/// This can result in undefined behavior in a library going unnoticed until
+/// the function or constant is actually used.
+///
+/// Therefore, we only consider const functions and associated constants here and leave
+/// other const items to be handled by the evaluator.
+fn check_ptr_transmute_in_const<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'tcx>,
+    body_owner_def_id: LocalDefId,
+    const_context: Option<hir::ConstContext>,
+    src: Ty<'tcx>,
+    dst: Ty<'tcx>,
+) {
+    if matches!(const_context, Some(hir::ConstContext::ConstFn))
+        || matches!(cx.tcx.def_kind(body_owner_def_id), DefKind::AssocConst)
+    {
+        if src.is_raw_ptr() && dst.is_integral() {
+            cx.tcx.emit_node_span_lint(
+                PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS,
+                expr.hir_id,
+                expr.span,
+                UndefinedTransmuteLint,
+            );
         }
     }
 }
 
+/// Check for transmutes that overlap with stdlib methods.
+/// For example, transmuting `[u8; 4]` to `u32`.
+///
+/// We chose not to lint u8 -> bool transmutes, see #140431.
+fn check_unnecessary_transmute<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'tcx>,
+    callee: &'tcx hir::Expr<'tcx>,
+    arg: &'tcx hir::Expr<'tcx>,
+    const_context: Option<hir::ConstContext>,
+    src: Ty<'tcx>,
+    dst: Ty<'tcx>,
+) {
+    let callee_span = callee.span.find_ancestor_inside(expr.span).unwrap_or(callee.span);
+    let (sugg, help) = match (src.kind(), dst.kind()) {
+        // dont check the length; transmute does that for us.
+        // [u8; _] => primitive
+        (ty::Array(t, _), ty::Uint(_) | ty::Float(_) | ty::Int(_))
+            if *t.kind() == ty::Uint(ty::UintTy::U8) =>
+        {
+            (
+                Some(vec![(callee_span, format!("{dst}::from_ne_bytes"))]),
+                Some(
+                    "there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order",
+                ),
+            )
+        }
+        // primitive => [u8; _]
+        (ty::Uint(_) | ty::Float(_) | ty::Int(_), ty::Array(t, _))
+            if *t.kind() == ty::Uint(ty::UintTy::U8) =>
+        {
+            (
+                Some(vec![(callee_span, format!("{src}::to_ne_bytes"))]),
+                Some(
+                    "there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order",
+                ),
+            )
+        }
+        // char → u32
+        (ty::Char, ty::Uint(ty::UintTy::U32)) => {
+            (Some(vec![(callee_span, "u32::from".to_string())]), None)
+        }
+        // char (→ u32) → i32
+        (ty::Char, ty::Int(ty::IntTy::I32)) => (
+            Some(vec![
+                (callee_span, "u32::from".to_string()),
+                (expr.span.shrink_to_hi(), ".cast_signed()".to_string()),
+            ]),
+            None,
+        ),
+        // u32 → char
+        (ty::Uint(ty::UintTy::U32), ty::Char) => (
+            Some(vec![(callee_span, "char::from_u32_unchecked".to_string())]),
+            Some("consider using `char::from_u32(…).unwrap()`"),
+        ),
+        // i32 → char
+        (ty::Int(ty::IntTy::I32), ty::Char) => (
+            Some(vec![
+                (callee_span, "char::from_u32_unchecked(i32::cast_unsigned".to_string()),
+                (expr.span.shrink_to_hi(), ")".to_string()),
+            ]),
+            Some("consider using `char::from_u32(i32::cast_unsigned(…)).unwrap()`"),
+        ),
+        // uNN → iNN
+        (ty::Uint(_), ty::Int(_)) => {
+            (Some(vec![(callee_span, format!("{src}::cast_signed"))]), None)
+        }
+        // iNN → uNN
+        (ty::Int(_), ty::Uint(_)) => {
+            (Some(vec![(callee_span, format!("{src}::cast_unsigned"))]), None)
+        }
+        // fNN → usize, isize
+        (ty::Float(_), ty::Uint(ty::UintTy::Usize) | ty::Int(ty::IntTy::Isize)) => (
+            Some(vec![
+                (callee_span, format!("{src}::to_bits")),
+                (expr.span.shrink_to_hi(), format!(" as {dst}")),
+            ]),
+            None,
+        ),
+        // fNN (→ uNN) → iNN
+        (ty::Float(_), ty::Int(..)) => (
+            Some(vec![
+                (callee_span, format!("{src}::to_bits")),
+                (expr.span.shrink_to_hi(), ".cast_signed()".to_string()),
+            ]),
+            None,
+        ),
+        // fNN → uNN
+        (ty::Float(_), ty::Uint(..)) => {
+            (Some(vec![(callee_span, format!("{src}::to_bits"))]), None)
+        }
+        // xsize → fNN
+        (ty::Uint(ty::UintTy::Usize) | ty::Int(ty::IntTy::Isize), ty::Float(_)) => (
+            Some(vec![
+                (callee_span, format!("{dst}::from_bits")),
+                (arg.span.shrink_to_hi(), " as _".to_string()),
+            ]),
+            None,
+        ),
+        // iNN (→ uNN) → fNN
+        (ty::Int(_), ty::Float(_)) => (
+            Some(vec![
+                (callee_span, format!("{dst}::from_bits({src}::cast_unsigned")),
+                (expr.span.shrink_to_hi(), ")".to_string()),
+            ]),
+            None,
+        ),
+        // uNN → fNN
+        (ty::Uint(_), ty::Float(_)) => {
+            (Some(vec![(callee_span, format!("{dst}::from_bits"))]), None)
+        }
+        // bool → x8 in const context since `From::from` is not const yet
+        // FIXME: Consider arg expr's precedence to avoid parentheses.
+        // FIXME(const_traits): Remove this when `From::from` is constified.
+        (ty::Bool, ty::Int(..) | ty::Uint(..)) if const_context.is_some() => (
+            Some(vec![
+                (callee_span, "".to_string()),
+                (expr.span.shrink_to_hi(), format!(" as {dst}")),
+            ]),
+            None,
+        ),
+        // bool → x8 using `x8::from`
+        (ty::Bool, ty::Int(..) | ty::Uint(..)) => {
+            (Some(vec![(callee_span, format!("{dst}::from"))]), None)
+        }
+        _ => return,
+    };
+
+    cx.tcx.node_span_lint(UNNECESSARY_TRANSMUTES, expr.hir_id, expr.span, |diag| {
+        diag.primary_message("unnecessary transmute");
+        if let Some(sugg) = sugg {
+            diag.multipart_suggestion("replace this with", sugg, Applicability::MachineApplicable);
+        }
+        if let Some(help) = help {
+            diag.help(help);
+        }
+    });
+}
+
 #[derive(LintDiagnostic)]
 #[diag(lint_undefined_transmute)]
 #[note]
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 2e1aaeedba1..abf4840a026 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -117,7 +117,6 @@ declare_lint_pass! {
         UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
         UNNAMEABLE_TEST_ITEMS,
         UNNAMEABLE_TYPES,
-        UNNECESSARY_TRANSMUTES,
         UNREACHABLE_CODE,
         UNREACHABLE_PATTERNS,
         UNSAFE_ATTR_OUTSIDE_UNSAFE,
@@ -4851,30 +4850,6 @@ declare_lint! {
 }
 
 declare_lint! {
-    /// The `unnecessary_transmutes` lint detects transmutations that have safer alternatives.
-    ///
-    /// ### Example
-    ///
-    /// ```rust
-    /// fn bytes_at_home(x: [u8; 4]) -> u32 {
-    ///   unsafe { std::mem::transmute(x) }
-    /// }
-    /// ```
-    ///
-    /// {{produces}}
-    ///
-    /// ### Explanation
-    ///
-    /// Using an explicit method is preferable over calls to
-    /// [`transmute`](https://doc.rust-lang.org/std/mem/fn.transmute.html) as
-    /// they more clearly communicate the intent, are easier to review, and
-    /// are less likely to accidentally result in unsoundness.
-    pub UNNECESSARY_TRANSMUTES,
-    Warn,
-    "detects transmutes that are shadowed by std methods"
-}
-
-declare_lint! {
     /// The `tail_expr_drop_order` lint looks for those values generated at the tail expression location,
     /// that runs a custom `Drop` destructor.
     /// Some of them may be dropped earlier in Edition 2024 that they used to in Edition 2021 and prior.
diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl
index ada705a5163..ae3062f07de 100644
--- a/compiler/rustc_mir_transform/messages.ftl
+++ b/compiler/rustc_mir_transform/messages.ftl
@@ -79,4 +79,3 @@ mir_transform_unconditional_recursion = function cannot return without recursing
 mir_transform_unconditional_recursion_call_site_label = recursive call site
 
 mir_transform_unknown_pass_name = MIR pass `{$name}` is unknown and will be ignored
-mir_transform_unnecessary_transmute = unnecessary transmute
diff --git a/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs b/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs
deleted file mode 100644
index 1a3715465ad..00000000000
--- a/compiler/rustc_mir_transform/src/check_unnecessary_transmutes.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-use rustc_middle::mir::visit::Visitor;
-use rustc_middle::mir::{Body, Location, Operand, Terminator, TerminatorKind};
-use rustc_middle::ty::*;
-use rustc_session::lint::builtin::UNNECESSARY_TRANSMUTES;
-use rustc_span::source_map::Spanned;
-use rustc_span::{Span, sym};
-
-use crate::errors::UnnecessaryTransmute as Error;
-
-/// Check for transmutes that overlap with stdlib methods.
-/// For example, transmuting `[u8; 4]` to `u32`.
-/// We chose not to lint u8 -> bool transmutes, see #140431
-pub(super) struct CheckUnnecessaryTransmutes;
-
-impl<'tcx> crate::MirLint<'tcx> for CheckUnnecessaryTransmutes {
-    fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
-        let mut checker = UnnecessaryTransmuteChecker { body, tcx };
-        checker.visit_body(body);
-    }
-}
-
-struct UnnecessaryTransmuteChecker<'a, 'tcx> {
-    body: &'a Body<'tcx>,
-    tcx: TyCtxt<'tcx>,
-}
-
-impl<'a, 'tcx> UnnecessaryTransmuteChecker<'a, 'tcx> {
-    fn is_unnecessary_transmute(
-        &self,
-        function: &Operand<'tcx>,
-        arg: String,
-        span: Span,
-        is_in_const: bool,
-    ) -> Option<Error> {
-        let fn_sig = function.ty(self.body, self.tcx).fn_sig(self.tcx).skip_binder();
-        let [input] = fn_sig.inputs() else { return None };
-
-        let err = |sugg| Error { span, sugg, help: None };
-
-        Some(match (input.kind(), fn_sig.output().kind()) {
-            // dont check the length; transmute does that for us.
-            // [u8; _] => primitive
-            (Array(t, _), Uint(_) | Float(_) | Int(_)) if *t.kind() == Uint(UintTy::U8) => Error {
-                sugg: format!("{}::from_ne_bytes({arg})", fn_sig.output()),
-                help: Some(
-                    "there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order",
-                ),
-                span,
-            },
-            // primitive => [u8; _]
-            (Uint(_) | Float(_) | Int(_), Array(t, _)) if *t.kind() == Uint(UintTy::U8) => Error {
-                sugg: format!("{input}::to_ne_bytes({arg})"),
-                help: Some(
-                    "there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order",
-                ),
-                span,
-            },
-            // char → u32
-            (Char, Uint(UintTy::U32)) => err(format!("u32::from({arg})")),
-            // char (→ u32) → i32
-            (Char, Int(IntTy::I32)) => err(format!("u32::from({arg}).cast_signed()")),
-            // u32 → char
-            (Uint(UintTy::U32), Char) => Error {
-                sugg: format!("char::from_u32_unchecked({arg})"),
-                help: Some("consider `char::from_u32(…).unwrap()`"),
-                span,
-            },
-            // i32 → char
-            (Int(IntTy::I32), Char) => Error {
-                sugg: format!("char::from_u32_unchecked(i32::cast_unsigned({arg}))"),
-                help: Some("consider `char::from_u32(i32::cast_unsigned(…)).unwrap()`"),
-                span,
-            },
-            // uNN → iNN
-            (Uint(ty), Int(_)) => err(format!("{}::cast_signed({arg})", ty.name_str())),
-            // iNN → uNN
-            (Int(ty), Uint(_)) => err(format!("{}::cast_unsigned({arg})", ty.name_str())),
-            // fNN → xsize
-            (Float(ty), Uint(UintTy::Usize)) => {
-                err(format!("{}::to_bits({arg}) as usize", ty.name_str()))
-            }
-            (Float(ty), Int(IntTy::Isize)) => {
-                err(format!("{}::to_bits({arg}) as isize", ty.name_str()))
-            }
-            // fNN (→ uNN) → iNN
-            (Float(ty), Int(..)) => err(format!("{}::to_bits({arg}).cast_signed()", ty.name_str())),
-            // fNN → uNN
-            (Float(ty), Uint(..)) => err(format!("{}::to_bits({arg})", ty.name_str())),
-            // xsize → fNN
-            (Uint(UintTy::Usize) | Int(IntTy::Isize), Float(ty)) => {
-                err(format!("{}::from_bits({arg} as _)", ty.name_str(),))
-            }
-            // iNN (→ uNN) → fNN
-            (Int(int_ty), Float(ty)) => err(format!(
-                "{}::from_bits({}::cast_unsigned({arg}))",
-                ty.name_str(),
-                int_ty.name_str()
-            )),
-            // uNN → fNN
-            (Uint(_), Float(ty)) => err(format!("{}::from_bits({arg})", ty.name_str())),
-            // bool → { x8 } in const context since `From::from` is not const yet
-            // FIXME: is it possible to know when the parentheses arent necessary?
-            // FIXME(const_traits): Remove this when From::from is constified?
-            (Bool, Int(..) | Uint(..)) if is_in_const => {
-                err(format!("({arg}) as {}", fn_sig.output()))
-            }
-            // " using `x8::from`
-            (Bool, Int(..) | Uint(..)) => err(format!("{}::from({arg})", fn_sig.output())),
-            _ => return None,
-        })
-    }
-}
-
-impl<'tcx> Visitor<'tcx> for UnnecessaryTransmuteChecker<'_, 'tcx> {
-    // Check each block's terminator for calls to pointer to integer transmutes
-    // in const functions or associated constants and emit a lint.
-    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
-        if let TerminatorKind::Call { func, args, .. } = &terminator.kind
-            && let [Spanned { span: arg, .. }] = **args
-            && let Some((func_def_id, _)) = func.const_fn_def()
-            && self.tcx.is_intrinsic(func_def_id, sym::transmute)
-            && let span = self.body.source_info(location).span
-            && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(arg)
-            && let def_id = self.body.source.def_id()
-            && let Some(lint) = self.is_unnecessary_transmute(
-                func,
-                snippet,
-                span,
-                self.tcx.hir_body_const_context(def_id.expect_local()).is_some(),
-            )
-            && let Some(hir_id) = terminator.source_info.scope.lint_root(&self.body.source_scopes)
-        {
-            self.tcx.emit_node_span_lint(UNNECESSARY_TRANSMUTES, hir_id, span, lint);
-        }
-    }
-}
diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs
index 9777cb56f13..cffa0183fa7 100644
--- a/compiler/rustc_mir_transform/src/errors.rs
+++ b/compiler/rustc_mir_transform/src/errors.rs
@@ -158,26 +158,6 @@ pub(crate) struct MustNotSuspendReason {
     pub reason: String,
 }
 
-pub(crate) struct UnnecessaryTransmute {
-    pub span: Span,
-    pub sugg: String,
-    pub help: Option<&'static str>,
-}
-
-// Needed for def_path_str
-impl<'a> LintDiagnostic<'a, ()> for UnnecessaryTransmute {
-    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) {
-        diag.primary_message(fluent::mir_transform_unnecessary_transmute);
-        diag.span_suggestion(
-            self.span,
-            "replace this with",
-            self.sugg,
-            lint::Applicability::MachineApplicable,
-        );
-        self.help.map(|help| diag.help(help));
-    }
-}
-
 #[derive(Diagnostic)]
 #[diag(mir_transform_force_inline)]
 #[note]
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 8d4e9e30f4f..d26e4468715 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -123,7 +123,6 @@ declare_passes! {
     mod check_const_item_mutation : CheckConstItemMutation;
     mod check_null : CheckNull;
     mod check_packed_ref : CheckPackedRef;
-    mod check_unnecessary_transmutes: CheckUnnecessaryTransmutes;
     // This pass is public to allow external drivers to perform MIR cleanup
     pub mod cleanup_post_borrowck : CleanupPostBorrowck;
 
@@ -389,7 +388,6 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> {
             &Lint(check_packed_ref::CheckPackedRef),
             &Lint(check_const_item_mutation::CheckConstItemMutation),
             &Lint(function_item_references::FunctionItemReferences),
-            &Lint(check_unnecessary_transmutes::CheckUnnecessaryTransmutes),
             // What we need to do constant evaluation.
             &simplify::SimplifyCfg::Initial,
             &Lint(sanity_check::SanityCheck),
diff --git a/tests/ui/transmute/unnecessary-transmutation.stderr b/tests/ui/transmute/unnecessary-transmutation.stderr
index 602e964f5b2..0132ac4776b 100644
--- a/tests/ui/transmute/unnecessary-transmutation.stderr
+++ b/tests/ui/transmute/unnecessary-transmutation.stderr
@@ -1,9 +1,12 @@
 error: unnecessary transmute
-  --> $DIR/unnecessary-transmutation.rs:16:29
+  --> $DIR/unnecessary-transmutation.rs:7:14
    |
-LL | pub static X: u8 = unsafe { transmute(true) };
-   |                             ^^^^^^^^^^^^^^^ help: replace this with: `(true) as u8`
+LL |     unsafe { transmute(x) }
+   |              ---------^^^
+   |              |
+   |              help: replace this with: `u32::to_ne_bytes`
    |
+   = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order
 note: the lint level is defined here
   --> $DIR/unnecessary-transmutation.rs:2:9
    |
@@ -11,36 +14,72 @@ LL | #![deny(unnecessary_transmutes)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
 
 error: unnecessary transmute
-  --> $DIR/unnecessary-transmutation.rs:18:28
+  --> $DIR/unnecessary-transmutation.rs:12:14
+   |
+LL |     unsafe { transmute(from) }
+   |              ^^^^^^^^^^^^^^^
+   |
+help: replace this with
+   |
+LL -     unsafe { transmute(from) }
+LL +     unsafe { (from) as u8 }
    |
-LL | pub const Y: u8 = unsafe { transmute(true) };
-   |                            ^^^^^^^^^^^^^^^ help: replace this with: `(true) as u8`
 
 error: unnecessary transmute
-  --> $DIR/unnecessary-transmutation.rs:7:14
+  --> $DIR/unnecessary-transmutation.rs:16:29
    |
-LL |     unsafe { transmute(x) }
-   |              ^^^^^^^^^^^^ help: replace this with: `u32::to_ne_bytes(x)`
+LL | pub static X: u8 = unsafe { transmute(true) };
+   |                             ^^^^^^^^^^^^^^^
+   |
+help: replace this with
+   |
+LL - pub static X: u8 = unsafe { transmute(true) };
+LL + pub static X: u8 = unsafe { (true) as u8 };
    |
-   = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order
 
 error: unnecessary transmute
-  --> $DIR/unnecessary-transmutation.rs:12:14
+  --> $DIR/unnecessary-transmutation.rs:18:28
+   |
+LL | pub const Y: u8 = unsafe { transmute(true) };
+   |                            ^^^^^^^^^^^^^^^
+   |
+help: replace this with
+   |
+LL - pub const Y: u8 = unsafe { transmute(true) };
+LL + pub const Y: u8 = unsafe { (true) as u8 };
    |
-LL |     unsafe { transmute(from) }
-   |              ^^^^^^^^^^^^^^^ help: replace this with: `(from) as u8`
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:24:18
    |
 LL |         unsafe { transmute(x) }
-   |                  ^^^^^^^^^^^^ help: replace this with: `(x) as u8`
+   |                  ^^^^^^^^^^^^
+   |
+help: replace this with
+   |
+LL -         unsafe { transmute(x) }
+LL +         unsafe { (x) as u8 }
+   |
+
+error: unnecessary transmute
+  --> $DIR/unnecessary-transmutation.rs:30:22
+   |
+LL |     const { unsafe { transmute::<_, u8>(true) } };
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: replace this with
+   |
+LL -     const { unsafe { transmute::<_, u8>(true) } };
+LL +     const { unsafe { (true) as u8 } };
+   |
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:33:22
    |
 LL |         let x: u16 = transmute(*b"01");
-   |                      ^^^^^^^^^^^^^^^^^ help: replace this with: `u16::from_ne_bytes(*b"01")`
+   |                      ---------^^^^^^^^
+   |                      |
+   |                      help: replace this with: `u16::from_ne_bytes`
    |
    = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order
 
@@ -48,7 +87,9 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:35:26
    |
 LL |         let x: [u8; 2] = transmute(x);
-   |                          ^^^^^^^^^^^^ help: replace this with: `u16::to_ne_bytes(x)`
+   |                          ---------^^^
+   |                          |
+   |                          help: replace this with: `u16::to_ne_bytes`
    |
    = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order
 
@@ -56,7 +97,9 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:37:22
    |
 LL |         let x: u32 = transmute(*b"0123");
-   |                      ^^^^^^^^^^^^^^^^^^^ help: replace this with: `u32::from_ne_bytes(*b"0123")`
+   |                      ---------^^^^^^^^^^
+   |                      |
+   |                      help: replace this with: `u32::from_ne_bytes`
    |
    = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order
 
@@ -64,7 +107,9 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:39:26
    |
 LL |         let x: [u8; 4] = transmute(x);
-   |                          ^^^^^^^^^^^^ help: replace this with: `u32::to_ne_bytes(x)`
+   |                          ---------^^^
+   |                          |
+   |                          help: replace this with: `u32::to_ne_bytes`
    |
    = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order
 
@@ -72,7 +117,9 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:41:22
    |
 LL |         let x: u64 = transmute(*b"feriscat");
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `u64::from_ne_bytes(*b"feriscat")`
+   |                      ---------^^^^^^^^^^^^^^
+   |                      |
+   |                      help: replace this with: `u64::from_ne_bytes`
    |
    = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order
 
@@ -80,7 +127,9 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:43:26
    |
 LL |         let x: [u8; 8] = transmute(x);
-   |                          ^^^^^^^^^^^^ help: replace this with: `u64::to_ne_bytes(x)`
+   |                          ---------^^^
+   |                          |
+   |                          help: replace this with: `u64::to_ne_bytes`
    |
    = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order
 
@@ -88,7 +137,9 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:46:22
    |
 LL |         let y: i16 = transmute(*b"01");
-   |                      ^^^^^^^^^^^^^^^^^ help: replace this with: `i16::from_ne_bytes(*b"01")`
+   |                      ---------^^^^^^^^
+   |                      |
+   |                      help: replace this with: `i16::from_ne_bytes`
    |
    = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order
 
@@ -96,7 +147,9 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:48:26
    |
 LL |         let y: [u8; 2] = transmute(y);
-   |                          ^^^^^^^^^^^^ help: replace this with: `i16::to_ne_bytes(y)`
+   |                          ---------^^^
+   |                          |
+   |                          help: replace this with: `i16::to_ne_bytes`
    |
    = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order
 
@@ -104,7 +157,9 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:50:22
    |
 LL |         let y: i32 = transmute(*b"0123");
-   |                      ^^^^^^^^^^^^^^^^^^^ help: replace this with: `i32::from_ne_bytes(*b"0123")`
+   |                      ---------^^^^^^^^^^
+   |                      |
+   |                      help: replace this with: `i32::from_ne_bytes`
    |
    = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order
 
@@ -112,7 +167,9 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:52:26
    |
 LL |         let y: [u8; 4] = transmute(y);
-   |                          ^^^^^^^^^^^^ help: replace this with: `i32::to_ne_bytes(y)`
+   |                          ---------^^^
+   |                          |
+   |                          help: replace this with: `i32::to_ne_bytes`
    |
    = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order
 
@@ -120,7 +177,9 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:54:22
    |
 LL |         let y: i64 = transmute(*b"feriscat");
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `i64::from_ne_bytes(*b"feriscat")`
+   |                      ---------^^^^^^^^^^^^^^
+   |                      |
+   |                      help: replace this with: `i64::from_ne_bytes`
    |
    = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order
 
@@ -128,7 +187,9 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:56:26
    |
 LL |         let y: [u8; 8] = transmute(y);
-   |                          ^^^^^^^^^^^^ help: replace this with: `i64::to_ne_bytes(y)`
+   |                          ---------^^^
+   |                          |
+   |                          help: replace this with: `i64::to_ne_bytes`
    |
    = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order
 
@@ -136,7 +197,9 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:59:22
    |
 LL |         let z: f32 = transmute(*b"0123");
-   |                      ^^^^^^^^^^^^^^^^^^^ help: replace this with: `f32::from_ne_bytes(*b"0123")`
+   |                      ---------^^^^^^^^^^
+   |                      |
+   |                      help: replace this with: `f32::from_ne_bytes`
    |
    = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order
 
@@ -144,7 +207,9 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:61:26
    |
 LL |         let z: [u8; 4] = transmute(z);
-   |                          ^^^^^^^^^^^^ help: replace this with: `f32::to_ne_bytes(z)`
+   |                          ---------^^^
+   |                          |
+   |                          help: replace this with: `f32::to_ne_bytes`
    |
    = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order
 
@@ -152,7 +217,9 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:63:22
    |
 LL |         let z: f64 = transmute(*b"feriscat");
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `f64::from_ne_bytes(*b"feriscat")`
+   |                      ---------^^^^^^^^^^^^^^
+   |                      |
+   |                      help: replace this with: `f64::from_ne_bytes`
    |
    = help: there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order
 
@@ -160,7 +227,9 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:65:26
    |
 LL |         let z: [u8; 8] = transmute(z);
-   |                          ^^^^^^^^^^^^ help: replace this with: `f64::to_ne_bytes(z)`
+   |                          ---------^^^
+   |                          |
+   |                          help: replace this with: `f64::to_ne_bytes`
    |
    = help: there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order
 
@@ -168,119 +237,164 @@ error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:68:22
    |
 LL |         let y: u32 = transmute('🦀');
-   |                      ^^^^^^^^^^^^^^^ help: replace this with: `u32::from('🦀')`
+   |                      ---------^^^^^^
+   |                      |
+   |                      help: replace this with: `u32::from`
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:70:23
    |
 LL |         let y: char = transmute(y);
-   |                       ^^^^^^^^^^^^ help: replace this with: `char::from_u32_unchecked(y)`
+   |                       ---------^^^
+   |                       |
+   |                       help: replace this with: `char::from_u32_unchecked`
    |
-   = help: consider `char::from_u32(…).unwrap()`
+   = help: consider using `char::from_u32(…).unwrap()`
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:72:22
    |
 LL |         let y: i32 = transmute('🐱');
-   |                      ^^^^^^^^^^^^^^^ help: replace this with: `u32::from('🐱').cast_signed()`
+   |                      ^^^^^^^^^^^^^^^
+   |
+help: replace this with
+   |
+LL -         let y: i32 = transmute('🐱');
+LL +         let y: i32 = u32::from('🐱').cast_signed();
+   |
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:74:23
    |
 LL |         let y: char = transmute(y);
-   |                       ^^^^^^^^^^^^ help: replace this with: `char::from_u32_unchecked(i32::cast_unsigned(y))`
+   |                       ^^^^^^^^^^^^
+   |
+   = help: consider using `char::from_u32(i32::cast_unsigned(…)).unwrap()`
+help: replace this with
+   |
+LL -         let y: char = transmute(y);
+LL +         let y: char = char::from_u32_unchecked(i32::cast_unsigned(y));
    |
-   = help: consider `char::from_u32(i32::cast_unsigned(…)).unwrap()`
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:77:22
    |
 LL |         let x: u16 = transmute(8i16);
-   |                      ^^^^^^^^^^^^^^^ help: replace this with: `i16::cast_unsigned(8i16)`
+   |                      ---------^^^^^^
+   |                      |
+   |                      help: replace this with: `i16::cast_unsigned`
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:79:22
    |
 LL |         let x: i16 = transmute(x);
-   |                      ^^^^^^^^^^^^ help: replace this with: `u16::cast_signed(x)`
+   |                      ---------^^^
+   |                      |
+   |                      help: replace this with: `u16::cast_signed`
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:81:22
    |
 LL |         let x: u32 = transmute(4i32);
-   |                      ^^^^^^^^^^^^^^^ help: replace this with: `i32::cast_unsigned(4i32)`
+   |                      ---------^^^^^^
+   |                      |
+   |                      help: replace this with: `i32::cast_unsigned`
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:83:22
    |
 LL |         let x: i32 = transmute(x);
-   |                      ^^^^^^^^^^^^ help: replace this with: `u32::cast_signed(x)`
+   |                      ---------^^^
+   |                      |
+   |                      help: replace this with: `u32::cast_signed`
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:85:22
    |
 LL |         let x: u64 = transmute(7i64);
-   |                      ^^^^^^^^^^^^^^^ help: replace this with: `i64::cast_unsigned(7i64)`
+   |                      ---------^^^^^^
+   |                      |
+   |                      help: replace this with: `i64::cast_unsigned`
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:87:22
    |
 LL |         let x: i64 = transmute(x);
-   |                      ^^^^^^^^^^^^ help: replace this with: `u64::cast_signed(x)`
+   |                      ---------^^^
+   |                      |
+   |                      help: replace this with: `u64::cast_signed`
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:90:22
    |
 LL |         let y: f32 = transmute(1u32);
-   |                      ^^^^^^^^^^^^^^^ help: replace this with: `f32::from_bits(1u32)`
+   |                      ---------^^^^^^
+   |                      |
+   |                      help: replace this with: `f32::from_bits`
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:92:22
    |
 LL |         let y: u32 = transmute(y);
-   |                      ^^^^^^^^^^^^ help: replace this with: `f32::to_bits(y)`
+   |                      ---------^^^
+   |                      |
+   |                      help: replace this with: `f32::to_bits`
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:94:22
    |
 LL |         let y: f64 = transmute(3u64);
-   |                      ^^^^^^^^^^^^^^^ help: replace this with: `f64::from_bits(3u64)`
+   |                      ---------^^^^^^
+   |                      |
+   |                      help: replace this with: `f64::from_bits`
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:96:22
    |
 LL |         let y: u64 = transmute(2.0);
-   |                      ^^^^^^^^^^^^^^ help: replace this with: `f64::to_bits(2.0)`
+   |                      ---------^^^^^
+   |                      |
+   |                      help: replace this with: `f64::to_bits`
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:99:22
    |
 LL |         let y: f64 = transmute(1i64);
-   |                      ^^^^^^^^^^^^^^^ help: replace this with: `f64::from_bits(i64::cast_unsigned(1i64))`
+   |                      ^^^^^^^^^^^^^^^
+   |
+help: replace this with
+   |
+LL -         let y: f64 = transmute(1i64);
+LL +         let y: f64 = f64::from_bits(i64::cast_unsigned(1i64));
+   |
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:101:22
    |
 LL |         let y: i64 = transmute(1f64);
-   |                      ^^^^^^^^^^^^^^^ help: replace this with: `f64::to_bits(1f64).cast_signed()`
+   |                      ^^^^^^^^^^^^^^^
+   |
+help: replace this with
+   |
+LL -         let y: i64 = transmute(1f64);
+LL +         let y: i64 = f64::to_bits(1f64).cast_signed();
+   |
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:106:21
    |
 LL |         let z: u8 = transmute(z);
-   |                     ^^^^^^^^^^^^ help: replace this with: `u8::from(z)`
+   |                     ---------^^^
+   |                     |
+   |                     help: replace this with: `u8::from`
 
 error: unnecessary transmute
   --> $DIR/unnecessary-transmutation.rs:111:21
    |
 LL |         let z: i8 = transmute(z);
-   |                     ^^^^^^^^^^^^ help: replace this with: `i8::from(z)`
-
-error: unnecessary transmute
-  --> $DIR/unnecessary-transmutation.rs:30:22
-   |
-LL |     const { unsafe { transmute::<_, u8>(true) } };
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `(true) as u8`
+   |                     ---------^^^
+   |                     |
+   |                     help: replace this with: `i8::from`
 
 error: aborting due to 40 previous errors