-
Notifications
You must be signed in to change notification settings - Fork 13.3k
/
Copy pathcheck_unnecessary_transmutes.rs
100 lines (92 loc) · 4.16 KB
/
check_unnecessary_transmutes.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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`.
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,
) -> 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})")),
// u32 → char
(Uint(UintTy::U32), Char) => Error {
sugg: format!("char::from_u32_unchecked({arg})"),
help: Some("consider `char::from_u32(…).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 → uNN
(Float(ty), Uint(..)) => err(format!("{}::to_bits({arg})", ty.name_str())),
// uNN → fNN
(Uint(_), Float(ty)) => err(format!("{}::from_bits({arg})", ty.name_str())),
// bool → { x8 }
(Bool, Int(..) | Uint(..)) => err(format!("({arg}) as {}", fn_sig.output())),
// u8 → bool
(Uint(_), Bool) => err(format!("({arg} == 1)")),
_ => 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 Some(lint) = self.is_unnecessary_transmute(
func,
self.tcx.sess.source_map().span_to_snippet(arg).expect("ok"),
span,
)
&& 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);
}
}
}