Make UNNECESSARY_TRANSMUTES into a HIR lint

This commit is contained in:
Michael Goulet 2025-05-25 15:57:10 +00:00
parent 5370c5753f
commit 295a8d56f5
7 changed files with 379 additions and 273 deletions

View file

@ -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]

View file

@ -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,
@ -4850,30 +4849,6 @@ declare_lint! {
@feature_gate = supertrait_item_shadowing;
}
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.

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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]

View file

@ -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),

View file

@ -1,46 +1,85 @@
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
|
LL | #![deny(unnecessary_transmutes)]
| ^^^^^^^^^^^^^^^^^^^^^^
error: unnecessary transmute
--> $DIR/unnecessary-transmutation.rs:18:28
|
LL | pub const Y: u8 = unsafe { transmute(true) };
| ^^^^^^^^^^^^^^^ help: replace this with: `(true) as u8`
error: unnecessary transmute
--> $DIR/unnecessary-transmutation.rs:7:14
|
LL | unsafe { transmute(x) }
| ^^^^^^^^^^^^ help: replace this with: `u32::to_ne_bytes(x)`
|
= 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
|
LL | unsafe { transmute(from) }
| ^^^^^^^^^^^^^^^ help: replace this with: `(from) as u8`
| ^^^^^^^^^^^^^^^
|
help: replace this with
|
LL - unsafe { transmute(from) }
LL + unsafe { (from) as u8 }
|
error: unnecessary transmute
--> $DIR/unnecessary-transmutation.rs:16:29
|
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 };
|
error: unnecessary transmute
--> $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 };
|
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