1use rustc_middle::mir::visit::Visitor;
2use rustc_middle::mir::{Body, Location, Operand, Terminator, TerminatorKind};
3use rustc_middle::ty::*;
4use rustc_session::lint::builtin::UNNECESSARY_TRANSMUTES;
5use rustc_span::source_map::Spanned;
6use rustc_span::{Span, sym};
7
8use crate::errors::UnnecessaryTransmute as Error;
9
10pub(super) struct CheckUnnecessaryTransmutes;
13
14impl<'tcx> crate::MirLint<'tcx> for CheckUnnecessaryTransmutes {
15 fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
16 let mut checker = UnnecessaryTransmuteChecker { body, tcx };
17 checker.visit_body(body);
18 }
19}
20
21struct UnnecessaryTransmuteChecker<'a, 'tcx> {
22 body: &'a Body<'tcx>,
23 tcx: TyCtxt<'tcx>,
24}
25
26impl<'a, 'tcx> UnnecessaryTransmuteChecker<'a, 'tcx> {
27 fn is_unnecessary_transmute(
28 &self,
29 function: &Operand<'tcx>,
30 arg: String,
31 span: Span,
32 ) -> Option<Error> {
33 let fn_sig = function.ty(self.body, self.tcx).fn_sig(self.tcx).skip_binder();
34 let [input] = fn_sig.inputs() else { return None };
35
36 let err = |sugg| Error { span, sugg, help: None };
37
38 Some(match (input.kind(), fn_sig.output().kind()) {
39 (Array(t, _), Uint(_) | Float(_) | Int(_)) if *t.kind() == Uint(UintTy::U8) => Error {
42 sugg: format!("{}::from_ne_bytes({arg})", fn_sig.output()),
43 help: Some(
44 "there's also `from_le_bytes` and `from_be_bytes` if you expect a particular byte order",
45 ),
46 span,
47 },
48 (Uint(_) | Float(_) | Int(_), Array(t, _)) if *t.kind() == Uint(UintTy::U8) => Error {
50 sugg: format!("{input}::to_ne_bytes({arg})"),
51 help: Some(
52 "there's also `to_le_bytes` and `to_be_bytes` if you expect a particular byte order",
53 ),
54 span,
55 },
56 (Char, Uint(UintTy::U32)) => err(format!("u32::from({arg})")),
58 (Char, Int(IntTy::I32)) => err(format!("u32::from({arg}).cast_signed()")),
60 (Uint(UintTy::U32), Char) => Error {
62 sugg: format!("char::from_u32_unchecked({arg})"),
63 help: Some("consider `char::from_u32(…).unwrap()`"),
64 span,
65 },
66 (Int(IntTy::I32), Char) => Error {
68 sugg: format!("char::from_u32_unchecked(i32::cast_unsigned({arg}))"),
69 help: Some("consider `char::from_u32(i32::cast_unsigned(…)).unwrap()`"),
70 span,
71 },
72 (Uint(ty), Int(_)) => err(format!("{}::cast_signed({arg})", ty.name_str())),
74 (Int(ty), Uint(_)) => err(format!("{}::cast_unsigned({arg})", ty.name_str())),
76 (Float(ty), Uint(UintTy::Usize)) => {
78 err(format!("{}::to_bits({arg}) as usize", ty.name_str()))
79 }
80 (Float(ty), Int(IntTy::Isize)) => {
81 err(format!("{}::to_bits({arg}) as isize", ty.name_str()))
82 }
83 (Float(ty), Int(..)) => err(format!("{}::to_bits({arg}).cast_signed()", ty.name_str())),
85 (Float(ty), Uint(..)) => err(format!("{}::to_bits({arg})", ty.name_str())),
87 (Uint(UintTy::Usize) | Int(IntTy::Isize), Float(ty)) => {
89 err(format!("{}::from_bits({arg} as _)", ty.name_str(),))
90 }
91 (Int(int_ty), Float(ty)) => err(format!(
93 "{}::from_bits({}::cast_unsigned({arg}))",
94 ty.name_str(),
95 int_ty.name_str()
96 )),
97 (Uint(_), Float(ty)) => err(format!("{}::from_bits({arg})", ty.name_str())),
99 (Bool, Int(..) | Uint(..)) => err(format!("({arg}) as {}", fn_sig.output())),
101 (Uint(_), Bool) => err(format!("({arg} == 1)")),
103 _ => return None,
104 })
105 }
106}
107
108impl<'tcx> Visitor<'tcx> for UnnecessaryTransmuteChecker<'_, 'tcx> {
109 fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
112 if let TerminatorKind::Call { func, args, .. } = &terminator.kind
113 && let [Spanned { span: arg, .. }] = **args
114 && let Some((func_def_id, _)) = func.const_fn_def()
115 && self.tcx.is_intrinsic(func_def_id, sym::transmute)
116 && let span = self.body.source_info(location).span
117 && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(arg)
118 && let Some(lint) = self.is_unnecessary_transmute(func, snippet, span)
119 && let Some(hir_id) = terminator.source_info.scope.lint_root(&self.body.source_scopes)
120 {
121 self.tcx.emit_node_span_lint(UNNECESSARY_TRANSMUTES, hir_id, span, lint);
122 }
123 }
124}