2 is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg,
3 span_lint_and_then, sugg,
5 use if_chain::if_chain;
7 use rustc_errors::Applicability;
8 use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, TyKind, UnOp};
9 use rustc_lint::{LateContext, LateLintPass};
10 use rustc_middle::ty::{self, cast::CastKind, Ty};
11 use rustc_span::DUMMY_SP;
12 use rustc_session::{declare_lint_pass, declare_tool_lint};
13 use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited};
16 declare_clippy_lint! {
17 /// **What it does:** Checks for transmutes that can't ever be correct on any
20 /// **Why is this bad?** It's basically guaranteed to be undefined behaviour.
22 /// **Known problems:** When accessing C, users might want to store pointer
23 /// sized objects in `extradata` arguments to save an allocation.
27 /// let ptr: *const T = core::intrinsics::transmute('x')
31 "transmutes that are confusing at best, undefined behaviour at worst and always useless"
34 // FIXME: Move this to `complexity` again, after #5343 is fixed
35 declare_clippy_lint! {
36 /// **What it does:** Checks for transmutes to the original type of the object
37 /// and transmutes that could be a cast.
39 /// **Why is this bad?** Readability. The code tricks people into thinking that
40 /// something complex is going on.
42 /// **Known problems:** None.
46 /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
48 pub USELESS_TRANSMUTE,
50 "transmutes that have the same to and from types or could be a cast/coercion"
53 declare_clippy_lint! {
54 /// **What it does:** Checks for transmutes between a type `T` and `*T`.
56 /// **Why is this bad?** It's easy to mistakenly transmute between a type and a
57 /// pointer to that type.
59 /// **Known problems:** None.
63 /// core::intrinsics::transmute(t) // where the result type is the same as
66 pub CROSSPOINTER_TRANSMUTE,
68 "transmutes that have to or from types that are a pointer to the other"
71 declare_clippy_lint! {
72 /// **What it does:** Checks for transmutes from a pointer to a reference.
74 /// **Why is this bad?** This can always be rewritten with `&` and `*`.
76 /// **Known problems:** None.
81 /// let _: &T = std::mem::transmute(p); // where p: *const T
84 /// // can be written:
87 pub TRANSMUTE_PTR_TO_REF,
89 "transmutes from a pointer to a reference type"
92 declare_clippy_lint! {
93 /// **What it does:** Checks for transmutes from an integer to a `char`.
95 /// **Why is this bad?** Not every integer is a Unicode scalar value.
97 /// **Known problems:**
98 /// - [`from_u32`] which this lint suggests using is slower than `transmute`
99 /// as it needs to validate the input.
100 /// If you are certain that the input is always a valid Unicode scalar value,
101 /// use [`from_u32_unchecked`] which is as fast as `transmute`
102 /// but has a semantically meaningful name.
103 /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.
105 /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html
106 /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html
112 /// let _: char = std::mem::transmute(x); // where x: u32
116 /// let _ = std::char::from_u32(x).unwrap();
118 pub TRANSMUTE_INT_TO_CHAR,
120 "transmutes from an integer to a `char`"
123 declare_clippy_lint! {
124 /// **What it does:** Checks for transmutes from a `&[u8]` to a `&str`.
126 /// **Why is this bad?** Not every byte slice is a valid UTF-8 string.
128 /// **Known problems:**
129 /// - [`from_utf8`] which this lint suggests using is slower than `transmute`
130 /// as it needs to validate the input.
131 /// If you are certain that the input is always a valid UTF-8,
132 /// use [`from_utf8_unchecked`] which is as fast as `transmute`
133 /// but has a semantically meaningful name.
134 /// - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.
136 /// [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html
137 /// [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html
141 /// let b: &[u8] = &[1_u8, 2_u8];
143 /// let _: &str = std::mem::transmute(b); // where b: &[u8]
147 /// let _ = std::str::from_utf8(b).unwrap();
149 pub TRANSMUTE_BYTES_TO_STR,
151 "transmutes from a `&[u8]` to a `&str`"
154 declare_clippy_lint! {
155 /// **What it does:** Checks for transmutes from an integer to a `bool`.
157 /// **Why is this bad?** This might result in an invalid in-memory representation of a `bool`.
159 /// **Known problems:** None.
165 /// let _: bool = std::mem::transmute(x); // where x: u8
169 /// let _: bool = x != 0;
171 pub TRANSMUTE_INT_TO_BOOL,
173 "transmutes from an integer to a `bool`"
176 declare_clippy_lint! {
177 /// **What it does:** Checks for transmutes from an integer to a float.
179 /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive
182 /// **Known problems:** None.
187 /// let _: f32 = std::mem::transmute(1_u32); // where x: u32
191 /// let _: f32 = f32::from_bits(1_u32);
193 pub TRANSMUTE_INT_TO_FLOAT,
195 "transmutes from an integer to a float"
198 declare_clippy_lint! {
199 /// **What it does:** Checks for transmutes from a float to an integer.
201 /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive
204 /// **Known problems:** None.
209 /// let _: u32 = std::mem::transmute(1f32);
213 /// let _: u32 = 1f32.to_bits();
215 pub TRANSMUTE_FLOAT_TO_INT,
217 "transmutes from a float to an integer"
220 declare_clippy_lint! {
221 /// **What it does:** Checks for transmutes from a pointer to a pointer, or
222 /// from a reference to a reference.
224 /// **Why is this bad?** Transmutes are dangerous, and these can instead be
225 /// written as casts.
227 /// **Known problems:** None.
231 /// let ptr = &1u32 as *const u32;
233 /// // pointer-to-pointer transmute
234 /// let _: *const f32 = std::mem::transmute(ptr);
235 /// // ref-ref transmute
236 /// let _: &f32 = std::mem::transmute(&1u32);
238 /// // These can be respectively written:
239 /// let _ = ptr as *const f32;
240 /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
242 pub TRANSMUTE_PTR_TO_PTR,
244 "transmutes from a pointer to a pointer / a reference to a reference"
247 declare_clippy_lint! {
248 /// **What it does:** Checks for transmutes between collections whose
249 /// types have different ABI, size or alignment.
251 /// **Why is this bad?** This is undefined behavior.
253 /// **Known problems:** Currently, we cannot know whether a type is a
254 /// collection, so we just lint the ones that come with `std`.
258 /// // different size, therefore likely out-of-bounds memory access
259 /// // You absolutely do not want this in your code!
261 /// std::mem::transmute::<_, Vec<u32>>(vec![2_u16])
265 /// You must always iterate, map and collect the values:
268 /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
270 pub UNSOUND_COLLECTION_TRANSMUTE,
272 "transmute between collections of layout-incompatible types"
275 declare_clippy_lint! {
276 /// **What it does:**
278 /// **Why is this bad?**
280 /// **Known problems:** None.
285 /// // example code where clippy issues a warning
289 /// // example code which does not raise clippy warning
291 pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
293 "default lint description"
296 declare_lint_pass!(Transmute => [
297 CROSSPOINTER_TRANSMUTE,
298 TRANSMUTE_PTR_TO_REF,
299 TRANSMUTE_PTR_TO_PTR,
302 TRANSMUTE_INT_TO_CHAR,
303 TRANSMUTE_BYTES_TO_STR,
304 TRANSMUTE_INT_TO_BOOL,
305 TRANSMUTE_INT_TO_FLOAT,
306 TRANSMUTE_FLOAT_TO_INT,
307 UNSOUND_COLLECTION_TRANSMUTE,
308 TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
311 // used to check for UNSOUND_COLLECTION_TRANSMUTE
312 static COLLECTIONS: &[&[&str]] = &[
321 impl<'tcx> LateLintPass<'tcx> for Transmute {
322 #[allow(clippy::similar_names, clippy::too_many_lines)]
323 fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
325 if let ExprKind::Call(ref path_expr, ref args) = e.kind;
326 if let ExprKind::Path(ref qpath) = path_expr.kind;
327 if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
328 if match_def_path(cx, def_id, &paths::TRANSMUTE);
330 let from_ty = cx.typeck_results().expr_ty(&args[0]);
331 let to_ty = cx.typeck_results().expr_ty(e);
333 match (&from_ty.kind, &to_ty.kind) {
334 _ if from_ty == to_ty => span_lint(
338 &format!("transmute from a type (`{}`) to itself", from_ty),
340 (ty::Ref(_, rty, rty_mutbl), ty::RawPtr(ptr_ty)) => span_lint_and_then(
344 "transmute from a reference to a pointer",
346 if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
347 let rty_and_mut = ty::TypeAndMut {
352 let sugg = if *ptr_ty == rty_and_mut {
355 arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty)
358 diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified);
362 (ty::Int(_) | ty::Uint(_), ty::RawPtr(_)) => span_lint_and_then(
366 "transmute from an integer to a pointer",
368 if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
369 diag.span_suggestion(
372 arg.as_ty(&to_ty.to_string()).to_string(),
373 Applicability::Unspecified,
378 (ty::Float(_) | ty::Char, ty::Ref(..) | ty::RawPtr(_)) => span_lint(
382 &format!("transmute from a `{}` to a pointer", from_ty),
384 (ty::RawPtr(from_ptr), _) if from_ptr.ty == to_ty => span_lint(
386 CROSSPOINTER_TRANSMUTE,
389 "transmute from a type (`{}`) to the type that it points to (`{}`)",
393 (_, ty::RawPtr(to_ptr)) if to_ptr.ty == from_ty => span_lint(
395 CROSSPOINTER_TRANSMUTE,
398 "transmute from a type (`{}`) to a pointer to that type (`{}`)",
402 (ty::RawPtr(from_pty), ty::Ref(_, to_ref_ty, mutbl)) => span_lint_and_then(
404 TRANSMUTE_PTR_TO_REF,
407 "transmute from a pointer type (`{}`) to a reference type \
412 let arg = sugg::Sugg::hir(cx, &args[0], "..");
413 let (deref, cast) = if *mutbl == Mutability::Mut {
419 let arg = if from_pty.ty == *to_ref_ty {
422 arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_ref_ty)))
425 diag.span_suggestion(
428 sugg::make_unop(deref, arg).to_string(),
429 Applicability::Unspecified,
433 (ty::Int(ast::IntTy::I32) | ty::Uint(ast::UintTy::U32), &ty::Char) => {
436 TRANSMUTE_INT_TO_CHAR,
438 &format!("transmute from a `{}` to a `char`", from_ty),
440 let arg = sugg::Sugg::hir(cx, &args[0], "..");
441 let arg = if let ty::Int(_) = from_ty.kind {
442 arg.as_ty(ast::UintTy::U32.name_str())
446 diag.span_suggestion(
449 format!("std::char::from_u32({}).unwrap()", arg.to_string()),
450 Applicability::Unspecified,
455 (ty::Ref(_, ty_from, from_mutbl), ty::Ref(_, ty_to, to_mutbl)) => {
457 if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind, &ty_to.kind);
458 if let ty::Uint(ast::UintTy::U8) = slice_ty.kind;
459 if from_mutbl == to_mutbl;
461 let postfix = if *from_mutbl == Mutability::Mut {
469 TRANSMUTE_BYTES_TO_STR,
471 &format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
474 "std::str::from_utf8{}({}).unwrap()",
476 snippet(cx, args[0].span, ".."),
478 Applicability::Unspecified,
481 if cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty) {
484 TRANSMUTE_PTR_TO_PTR,
486 "transmute from a reference to a reference",
487 |diag| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
488 let ty_from_and_mut = ty::TypeAndMut {
492 let ty_to_and_mut = ty::TypeAndMut { ty: ty_to, mutbl: *to_mutbl };
494 .as_ty(cx.tcx.mk_ptr(ty_from_and_mut))
495 .as_ty(cx.tcx.mk_ptr(ty_to_and_mut));
496 let sugg = if *to_mutbl == Mutability::Mut {
497 sugg_paren.mut_addr_deref()
499 sugg_paren.addr_deref()
501 diag.span_suggestion(
505 Applicability::Unspecified,
513 (ty::RawPtr(_), ty::RawPtr(to_ty)) => span_lint_and_then(
515 TRANSMUTE_PTR_TO_PTR,
517 "transmute from a pointer to a pointer",
519 if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
520 let sugg = arg.as_ty(cx.tcx.mk_ptr(*to_ty));
521 diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified);
525 (ty::Int(ast::IntTy::I8) | ty::Uint(ast::UintTy::U8), ty::Bool) => {
528 TRANSMUTE_INT_TO_BOOL,
530 &format!("transmute from a `{}` to a `bool`", from_ty),
532 let arg = sugg::Sugg::hir(cx, &args[0], "..");
533 let zero = sugg::Sugg::NonParen(Cow::from("0"));
534 diag.span_suggestion(
537 sugg::make_binop(ast::BinOpKind::Ne, &arg, &zero).to_string(),
538 Applicability::Unspecified,
543 (ty::Int(_) | ty::Uint(_), ty::Float(_)) => span_lint_and_then(
545 TRANSMUTE_INT_TO_FLOAT,
547 &format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
549 let arg = sugg::Sugg::hir(cx, &args[0], "..");
550 let arg = if let ty::Int(int_ty) = from_ty.kind {
553 int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string())
558 diag.span_suggestion(
561 format!("{}::from_bits({})", to_ty, arg.to_string()),
562 Applicability::Unspecified,
566 (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) => span_lint_and_then(
568 TRANSMUTE_FLOAT_TO_INT,
570 &format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
572 let mut expr = &args[0];
573 let mut arg = sugg::Sugg::hir(cx, expr, "..");
575 if let ExprKind::Unary(UnOp::UnNeg, inner_expr) = &expr.kind {
580 // if the expression is a float literal and it is unsuffixed then
581 // add a suffix so the suggestion is valid and unambiguous
582 let op = format!("{}{}", arg, float_ty.name_str()).into();
583 if let ExprKind::Lit(lit) = &expr.kind;
584 if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node;
587 sugg::Sugg::MaybeParen(_) => arg = sugg::Sugg::MaybeParen(op),
588 _ => arg = sugg::Sugg::NonParen(op)
593 arg = sugg::Sugg::NonParen(format!("{}.to_bits()", arg.maybe_par()).into());
595 // cast the result of `to_bits` if `to_ty` is signed
596 arg = if let ty::Int(int_ty) = to_ty.kind {
597 arg.as_ty(int_ty.name_str().to_string())
602 diag.span_suggestion(
606 Applicability::Unspecified,
610 (ty::Adt(from_adt, from_substs), ty::Adt(to_adt, to_substs)) => {
611 if from_adt.did != to_adt.did ||
612 !COLLECTIONS.iter().any(|path| match_def_path(cx, to_adt.did, path)) {
615 if from_substs.types().zip(to_substs.types())
616 .any(|(from_ty, to_ty)| is_layout_incompatible(cx, from_ty, to_ty)) {
619 UNSOUND_COLLECTION_TRANSMUTE,
622 "transmute from `{}` to `{}` with mismatched layout is unsound",
629 (_, _) if can_be_expressed_as_pointer_cast(cx, e, from_ty, to_ty) => {
632 TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
635 "transmute from `{}` to `{}` which could be expressed as a pointer cast instead",
650 /// Gets the snippet of `Bar` in `…::transmute<Foo, &Bar>`. If that snippet is
651 /// not available , use
652 /// the type's `ToString` implementation. In weird cases it could lead to types
653 /// with invalid `'_`
654 /// lifetime, but it should be rare.
655 fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String {
656 let seg = last_path_segment(path);
658 if let Some(ref params) = seg.args;
659 if !params.parenthesized;
660 if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg {
661 GenericArg::Type(ty) => Some(ty),
664 if let TyKind::Rptr(_, ref to_ty) = to_ty.kind;
666 return snippet(cx, to_ty.ty.span, &to_ref_ty.to_string()).to_string();
670 to_ref_ty.to_string()
673 // check if the component types of the transmuted collection and the result have different ABI,
675 fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
676 let empty_param_env = ty::ParamEnv::empty();
677 // check if `from` and `to` are normalizable to avoid ICE (#4968)
678 if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) {
681 let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from));
682 let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to));
683 if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) {
684 from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi
686 // no idea about layout, so don't lint
691 /// Check if the the type conversion can be expressed as a pointer cast, instead of a transmute.
692 fn can_be_expressed_as_pointer_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool {
695 check_cast(cx, e, from_ty, to_ty),
707 /// If a cast from from_ty to to_ty is valid, returns an Ok containing the kind of the cast.
708 fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option<CastKind> {
709 let hir_id = e.hir_id;
710 let local_def_id = hir_id.owner;
712 Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
713 let fn_ctxt = FnCtxt::new(
715 // TODO should we try to get the correct ParamEnv?
716 ty::ParamEnv::empty(),
720 // If we already have errors, we can't be sure we can pointer cast.
721 if fn_ctxt.errors_reported_since_creation() {
725 if let Ok(check) = CastCheck::new(
730 // We won't show any error to the user, so we don't care what the span is here.
734 check.do_check(&fn_ctxt)
736 // do_check's documentation says that it might return Ok and create
737 // errors in the fcx instead of returing Err in some cases.
738 .filter(|_| !fn_ctxt.errors_reported_since_creation())