diff --git a/CHANGELOG.md b/CHANGELOG.md index ca0eb65ca3d8..8dd5ebdd4015 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1224,6 +1224,7 @@ Released 2018-09-13 [`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name [`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization [`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix +[`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute [`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect diff --git a/README.md b/README.md index f944e716e9aa..2f7032af87d0 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are 317 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are 318 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ed47b6a18576..4b5e81ff9a80 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -889,6 +889,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con transmute::TRANSMUTE_INT_TO_FLOAT, transmute::TRANSMUTE_PTR_TO_PTR, transmute::TRANSMUTE_PTR_TO_REF, + transmute::UNSOUND_COLLECTION_TRANSMUTE, transmute::USELESS_TRANSMUTE, transmute::WRONG_TRANSMUTE, transmuting_null::TRANSMUTING_NULL, @@ -1143,6 +1144,7 @@ pub fn register_plugins(reg: &mut rustc_driver::plugin::Registry<'_>, conf: &Con suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, swap::ALMOST_SWAPPED, + transmute::UNSOUND_COLLECTION_TRANSMUTE, transmute::WRONG_TRANSMUTE, transmuting_null::TRANSMUTING_NULL, types::ABSURD_EXTREME_COMPARISONS, diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index 1f029491df42..d8e5cbebba0d 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -216,6 +216,33 @@ declare_clippy_lint! { "transmutes from a pointer to a pointer / a reference to a reference" } +declare_clippy_lint! { + /// **What it does:** Checks for transmutes between collections whose it + /// types have different ABI, size or alignment. + /// + /// **Why is this bad?** This is undefined behavior. + /// + /// **Known problems:** Currently, we cannot know whether a type is a + /// collection, so we just lint the ones that come with `std`. + /// + /// **Example:** + /// ```rust + /// // different size, therefore likely out-of-bounds memory access + /// // You absolutely do not want this in your code! + /// unsafe { + /// std::mem::transmute::<_, Vec>(vec![2_u16]) + /// }; + /// ``` + /// + /// You must always iterate, map and collect the values: + /// + /// ```rust + /// vec![2_u16].into_iter().map(u32::from).collect::>(); + /// ``` + pub UNSOUND_COLLECTION_TRANSMUTE, + correctness, + "transmute between collections of layout-incompatible types" +} declare_lint_pass!(Transmute => [ CROSSPOINTER_TRANSMUTE, TRANSMUTE_PTR_TO_REF, @@ -226,263 +253,293 @@ declare_lint_pass!(Transmute => [ TRANSMUTE_BYTES_TO_STR, TRANSMUTE_INT_TO_BOOL, TRANSMUTE_INT_TO_FLOAT, + UNSOUND_COLLECTION_TRANSMUTE, ]); +// used to check for UNSOUND_COLLECTION_TRANSMUTE +static COLLECTIONS: &[&[&str]] = &[ + &paths::VEC, + &paths::VEC_DEQUE, + &paths::BINARY_HEAP, + &paths::BTREESET, + &paths::BTREEMAP, + &paths::HASHSET, + &paths::HASHMAP, +]; impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) { - if let ExprKind::Call(ref path_expr, ref args) = e.kind { - if let ExprKind::Path(ref qpath) = path_expr.kind { - if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id() { - if match_def_path(cx, def_id, &paths::TRANSMUTE) { - let from_ty = cx.tables.expr_ty(&args[0]); - let to_ty = cx.tables.expr_ty(e); + if_chain! { + if let ExprKind::Call(ref path_expr, ref args) = e.kind; + if let ExprKind::Path(ref qpath) = path_expr.kind; + if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::TRANSMUTE); + then { + let from_ty = cx.tables.expr_ty(&args[0]); + let to_ty = cx.tables.expr_ty(e); - match (&from_ty.kind, &to_ty.kind) { - _ if from_ty == to_ty => span_lint( - cx, - USELESS_TRANSMUTE, - e.span, - &format!("transmute from a type (`{}`) to itself", from_ty), - ), - (&ty::Ref(_, rty, rty_mutbl), &ty::RawPtr(ptr_ty)) => span_lint_and_then( - cx, - USELESS_TRANSMUTE, - e.span, - "transmute from a reference to a pointer", - |db| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { - let rty_and_mut = ty::TypeAndMut { - ty: rty, - mutbl: rty_mutbl, - }; + match (&from_ty.kind, &to_ty.kind) { + _ if from_ty == to_ty => span_lint( + cx, + USELESS_TRANSMUTE, + e.span, + &format!("transmute from a type (`{}`) to itself", from_ty), + ), + (&ty::Ref(_, rty, rty_mutbl), &ty::RawPtr(ptr_ty)) => span_lint_and_then( + cx, + USELESS_TRANSMUTE, + e.span, + "transmute from a reference to a pointer", + |db| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { + let rty_and_mut = ty::TypeAndMut { + ty: rty, + mutbl: rty_mutbl, + }; - let sugg = if ptr_ty == rty_and_mut { - arg.as_ty(to_ty) - } else { - arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty) - }; + let sugg = if ptr_ty == rty_and_mut { + arg.as_ty(to_ty) + } else { + arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty) + }; - db.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); - } - }, - ), - (&ty::Int(_), &ty::RawPtr(_)) | (&ty::Uint(_), &ty::RawPtr(_)) => span_lint_and_then( - cx, - USELESS_TRANSMUTE, - e.span, - "transmute from an integer to a pointer", - |db| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { - db.span_suggestion( - e.span, - "try", - arg.as_ty(&to_ty.to_string()).to_string(), - Applicability::Unspecified, - ); - } - }, - ), - (&ty::Float(_), &ty::Ref(..)) - | (&ty::Float(_), &ty::RawPtr(_)) - | (&ty::Char, &ty::Ref(..)) - | (&ty::Char, &ty::RawPtr(_)) => span_lint( - cx, - WRONG_TRANSMUTE, - e.span, - &format!("transmute from a `{}` to a pointer", from_ty), - ), - (&ty::RawPtr(from_ptr), _) if from_ptr.ty == to_ty => span_lint( - cx, - CROSSPOINTER_TRANSMUTE, - e.span, - &format!( - "transmute from a type (`{}`) to the type that it points to (`{}`)", - from_ty, to_ty - ), - ), - (_, &ty::RawPtr(to_ptr)) if to_ptr.ty == from_ty => span_lint( - cx, - CROSSPOINTER_TRANSMUTE, - e.span, - &format!( - "transmute from a type (`{}`) to a pointer to that type (`{}`)", - from_ty, to_ty - ), - ), - (&ty::RawPtr(from_pty), &ty::Ref(_, to_ref_ty, mutbl)) => span_lint_and_then( - cx, - TRANSMUTE_PTR_TO_REF, - e.span, - &format!( - "transmute from a pointer type (`{}`) to a reference type \ - (`{}`)", - from_ty, to_ty - ), - |db| { - let arg = sugg::Sugg::hir(cx, &args[0], ".."); - let (deref, cast) = if mutbl == Mutability::MutMutable { - ("&mut *", "*mut") - } else { - ("&*", "*const") - }; + db.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); + } + }, + ), + (&ty::Int(_), &ty::RawPtr(_)) | (&ty::Uint(_), &ty::RawPtr(_)) => span_lint_and_then( + cx, + USELESS_TRANSMUTE, + e.span, + "transmute from an integer to a pointer", + |db| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { + db.span_suggestion( + e.span, + "try", + arg.as_ty(&to_ty.to_string()).to_string(), + Applicability::Unspecified, + ); + } + }, + ), + (&ty::Float(_), &ty::Ref(..)) + | (&ty::Float(_), &ty::RawPtr(_)) + | (&ty::Char, &ty::Ref(..)) + | (&ty::Char, &ty::RawPtr(_)) => span_lint( + cx, + WRONG_TRANSMUTE, + e.span, + &format!("transmute from a `{}` to a pointer", from_ty), + ), + (&ty::RawPtr(from_ptr), _) if from_ptr.ty == to_ty => span_lint( + cx, + CROSSPOINTER_TRANSMUTE, + e.span, + &format!( + "transmute from a type (`{}`) to the type that it points to (`{}`)", + from_ty, to_ty + ), + ), + (_, &ty::RawPtr(to_ptr)) if to_ptr.ty == from_ty => span_lint( + cx, + CROSSPOINTER_TRANSMUTE, + e.span, + &format!( + "transmute from a type (`{}`) to a pointer to that type (`{}`)", + from_ty, to_ty + ), + ), + (&ty::RawPtr(from_pty), &ty::Ref(_, to_ref_ty, mutbl)) => span_lint_and_then( + cx, + TRANSMUTE_PTR_TO_REF, + e.span, + &format!( + "transmute from a pointer type (`{}`) to a reference type \ + (`{}`)", + from_ty, to_ty + ), + |db| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + let (deref, cast) = if mutbl == Mutability::MutMutable { + ("&mut *", "*mut") + } else { + ("&*", "*const") + }; - let arg = if from_pty.ty == to_ref_ty { - arg - } else { - arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_ref_ty))) - }; + let arg = if from_pty.ty == to_ref_ty { + arg + } else { + arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_ref_ty))) + }; - db.span_suggestion( - e.span, - "try", - sugg::make_unop(deref, arg).to_string(), - Applicability::Unspecified, - ); - }, - ), - (&ty::Int(ast::IntTy::I32), &ty::Char) | (&ty::Uint(ast::UintTy::U32), &ty::Char) => { - span_lint_and_then( - cx, - TRANSMUTE_INT_TO_CHAR, + db.span_suggestion( + e.span, + "try", + sugg::make_unop(deref, arg).to_string(), + Applicability::Unspecified, + ); + }, + ), + (&ty::Int(ast::IntTy::I32), &ty::Char) | (&ty::Uint(ast::UintTy::U32), &ty::Char) => { + span_lint_and_then( + cx, + TRANSMUTE_INT_TO_CHAR, + e.span, + &format!("transmute from a `{}` to a `char`", from_ty), + |db| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + let arg = if let ty::Int(_) = from_ty.kind { + arg.as_ty(ast::UintTy::U32) + } else { + arg + }; + db.span_suggestion( e.span, - &format!("transmute from a `{}` to a `char`", from_ty), - |db| { - let arg = sugg::Sugg::hir(cx, &args[0], ".."); - let arg = if let ty::Int(_) = from_ty.kind { - arg.as_ty(ast::UintTy::U32) - } else { - arg - }; - db.span_suggestion( - e.span, - "consider using", - format!("std::char::from_u32({}).unwrap()", arg.to_string()), - Applicability::Unspecified, - ); - }, - ) + "consider using", + format!("std::char::from_u32({}).unwrap()", arg.to_string()), + Applicability::Unspecified, + ); }, - (&ty::Ref(_, ty_from, from_mutbl), &ty::Ref(_, ty_to, to_mutbl)) => { - if_chain! { - if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind, &ty_to.kind); - if let ty::Uint(ast::UintTy::U8) = slice_ty.kind; - if from_mutbl == to_mutbl; - then { - let postfix = if from_mutbl == Mutability::MutMutable { - "_mut" - } else { - "" - }; + ) + }, + (&ty::Ref(_, ty_from, from_mutbl), &ty::Ref(_, ty_to, to_mutbl)) => { + if_chain! { + if let (&ty::Slice(slice_ty), &ty::Str) = (&ty_from.kind, &ty_to.kind); + if let ty::Uint(ast::UintTy::U8) = slice_ty.kind; + if from_mutbl == to_mutbl; + then { + let postfix = if from_mutbl == Mutability::MutMutable { + "_mut" + } else { + "" + }; - span_lint_and_then( - cx, - TRANSMUTE_BYTES_TO_STR, - e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), - |db| { - db.span_suggestion( - e.span, - "consider using", - format!( - "std::str::from_utf8{}({}).unwrap()", - postfix, - snippet(cx, args[0].span, ".."), - ), - Applicability::Unspecified, - ); - } - ) - } else { - if cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty) { - span_lint_and_then( - cx, - TRANSMUTE_PTR_TO_PTR, - e.span, - "transmute from a reference to a reference", - |db| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { - let ty_from_and_mut = ty::TypeAndMut { - ty: ty_from, - mutbl: from_mutbl - }; - let ty_to_and_mut = ty::TypeAndMut { ty: ty_to, mutbl: to_mutbl }; - let sugg_paren = arg - .as_ty(cx.tcx.mk_ptr(ty_from_and_mut)) - .as_ty(cx.tcx.mk_ptr(ty_to_and_mut)); - let sugg = if to_mutbl == Mutability::MutMutable { - sugg_paren.mut_addr_deref() - } else { - sugg_paren.addr_deref() - }; - db.span_suggestion( - e.span, - "try", - sugg.to_string(), - Applicability::Unspecified, - ); - }, - ) - } - } - } - }, - (&ty::RawPtr(_), &ty::RawPtr(to_ty)) => span_lint_and_then( - cx, - TRANSMUTE_PTR_TO_PTR, - e.span, - "transmute from a pointer to a pointer", - |db| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { - let sugg = arg.as_ty(cx.tcx.mk_ptr(to_ty)); - db.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); - } - }, - ), - (&ty::Int(ast::IntTy::I8), &ty::Bool) | (&ty::Uint(ast::UintTy::U8), &ty::Bool) => { span_lint_and_then( cx, - TRANSMUTE_INT_TO_BOOL, + TRANSMUTE_BYTES_TO_STR, e.span, - &format!("transmute from a `{}` to a `bool`", from_ty), + &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), |db| { - let arg = sugg::Sugg::hir(cx, &args[0], ".."); - let zero = sugg::Sugg::NonParen(Cow::from("0")); db.span_suggestion( e.span, "consider using", - sugg::make_binop(ast::BinOpKind::Ne, &arg, &zero).to_string(), + format!( + "std::str::from_utf8{}({}).unwrap()", + postfix, + snippet(cx, args[0].span, ".."), + ), Applicability::Unspecified, ); - }, + } ) + } else { + if cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty) { + span_lint_and_then( + cx, + TRANSMUTE_PTR_TO_PTR, + e.span, + "transmute from a reference to a reference", + |db| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { + let ty_from_and_mut = ty::TypeAndMut { + ty: ty_from, + mutbl: from_mutbl + }; + let ty_to_and_mut = ty::TypeAndMut { ty: ty_to, mutbl: to_mutbl }; + let sugg_paren = arg + .as_ty(cx.tcx.mk_ptr(ty_from_and_mut)) + .as_ty(cx.tcx.mk_ptr(ty_to_and_mut)); + let sugg = if to_mutbl == Mutability::MutMutable { + sugg_paren.mut_addr_deref() + } else { + sugg_paren.addr_deref() + }; + db.span_suggestion( + e.span, + "try", + sugg.to_string(), + Applicability::Unspecified, + ); + }, + ) + } + } + } + }, + (&ty::RawPtr(_), &ty::RawPtr(to_ty)) => span_lint_and_then( + cx, + TRANSMUTE_PTR_TO_PTR, + e.span, + "transmute from a pointer to a pointer", + |db| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) { + let sugg = arg.as_ty(cx.tcx.mk_ptr(to_ty)); + db.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); + } + }, + ), + (&ty::Int(ast::IntTy::I8), &ty::Bool) | (&ty::Uint(ast::UintTy::U8), &ty::Bool) => { + span_lint_and_then( + cx, + TRANSMUTE_INT_TO_BOOL, + e.span, + &format!("transmute from a `{}` to a `bool`", from_ty), + |db| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + let zero = sugg::Sugg::NonParen(Cow::from("0")); + db.span_suggestion( + e.span, + "consider using", + sugg::make_binop(ast::BinOpKind::Ne, &arg, &zero).to_string(), + Applicability::Unspecified, + ); }, - (&ty::Int(_), &ty::Float(_)) | (&ty::Uint(_), &ty::Float(_)) => span_lint_and_then( + ) + }, + (&ty::Int(_), &ty::Float(_)) | (&ty::Uint(_), &ty::Float(_)) => span_lint_and_then( + cx, + TRANSMUTE_INT_TO_FLOAT, + e.span, + &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + |db| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + let arg = if let ty::Int(int_ty) = from_ty.kind { + arg.as_ty(format!( + "u{}", + int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string()) + )) + } else { + arg + }; + db.span_suggestion( + e.span, + "consider using", + format!("{}::from_bits({})", to_ty, arg.to_string()), + Applicability::Unspecified, + ); + }, + ), + (&ty::Adt(ref from_adt, ref from_substs), &ty::Adt(ref to_adt, ref to_substs)) => { + if from_adt.did != to_adt.did || + !COLLECTIONS.iter().any(|path| match_def_path(cx, to_adt.did, path)) { + return; + } + if from_substs.types().zip(to_substs.types()) + .any(|(from_ty, to_ty)| is_layout_incompatible(cx, from_ty, to_ty)) { + span_lint( cx, - TRANSMUTE_INT_TO_FLOAT, + UNSOUND_COLLECTION_TRANSMUTE, e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), - |db| { - let arg = sugg::Sugg::hir(cx, &args[0], ".."); - let arg = if let ty::Int(int_ty) = from_ty.kind { - arg.as_ty(format!( - "u{}", - int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string()) - )) - } else { - arg - }; - db.span_suggestion( - e.span, - "consider using", - format!("{}::from_bits({})", to_ty, arg.to_string()), - Applicability::Unspecified, - ); - }, - ), - _ => return, - }; - } - } + &format!( + "transmute from `{}` to `{}` with mismatched layout is unsound", + from_ty, + to_ty + ) + ); + } + }, + _ => return, + }; } } } @@ -510,3 +567,16 @@ fn get_type_snippet(cx: &LateContext<'_, '_>, path: &QPath, to_ref_ty: Ty<'_>) - to_ref_ty.to_string() } + +// check if the component types of the transmuted collection and the result have different ABI, +// size or alignment +fn is_layout_incompatible<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { + let from_ty_layout = cx.tcx.layout_of(ty::ParamEnv::empty().and(from)); + let to_ty_layout = cx.tcx.layout_of(ty::ParamEnv::empty().and(to)); + if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) { + from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi + } else { + // no idea about layout, so don't lint + false + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1b838b17f7f7..1402b0eedbdf 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -6,7 +6,7 @@ pub use lint::Lint; pub use lint::LINT_LEVELS; // begin lint list, do not remove this comment, it’s used in `update_lints` -pub const ALL_LINTS: [Lint; 317] = [ +pub const ALL_LINTS: [Lint; 318] = [ Lint { name: "absurd_extreme_comparisons", group: "correctness", @@ -2023,6 +2023,13 @@ pub const ALL_LINTS: [Lint; 317] = [ deprecation: None, module: "misc_early", }, + Lint { + name: "unsound_collection_transmute", + group: "correctness", + desc: "transmute between collections of layout-incompatible types", + deprecation: None, + module: "transmute", + }, Lint { name: "unused_io_amount", group: "correctness", diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 6ec7697c0d6e..4f0c2f5a895c 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -80,12 +80,6 @@ fn useless() { let _: Vec = my_transmute(my_vec()); - let _: Vec = core::intrinsics::transmute(my_vec()); - let _: Vec = core::mem::transmute(my_vec()); - let _: Vec = std::intrinsics::transmute(my_vec()); - let _: Vec = std::mem::transmute(my_vec()); - let _: Vec = my_transmute(my_vec()); - let _: *const usize = std::mem::transmute(5_isize); let _ = 5_isize as *const usize; diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index ceee86d224db..f73d72f20bb3 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -117,19 +117,19 @@ LL | let _: Vec = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> $DIR/transmute.rs:89:31 + --> $DIR/transmute.rs:83:31 | LL | let _: *const usize = std::mem::transmute(5_isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` error: transmute from an integer to a pointer - --> $DIR/transmute.rs:93:31 + --> $DIR/transmute.rs:87:31 | LL | let _: *const usize = std::mem::transmute(1 + 1usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:108:24 + --> $DIR/transmute.rs:102:24 | LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -137,25 +137,25 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); = note: `-D clippy::crosspointer-transmute` implied by `-D warnings` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:110:24 + --> $DIR/transmute.rs:104:24 | LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> $DIR/transmute.rs:112:31 + --> $DIR/transmute.rs:106:31 | LL | let _: *const Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> $DIR/transmute.rs:114:29 + --> $DIR/transmute.rs:108:29 | LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u32` to a `char` - --> $DIR/transmute.rs:120:28 + --> $DIR/transmute.rs:114:28 | LL | let _: char = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` @@ -163,13 +163,13 @@ LL | let _: char = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` error: transmute from a `i32` to a `char` - --> $DIR/transmute.rs:121:28 + --> $DIR/transmute.rs:115:28 | LL | let _: char = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` error: transmute from a `u8` to a `bool` - --> $DIR/transmute.rs:126:28 + --> $DIR/transmute.rs:120:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -177,7 +177,7 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` error: transmute from a `u32` to a `f32` - --> $DIR/transmute.rs:131:27 + --> $DIR/transmute.rs:125:27 | LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` @@ -185,13 +185,13 @@ LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` error: transmute from a `i32` to a `f32` - --> $DIR/transmute.rs:132:27 + --> $DIR/transmute.rs:126:27 | LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:136:28 + --> $DIR/transmute.rs:130:28 | LL | let _: &str = unsafe { std::mem::transmute(b) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` @@ -199,13 +199,13 @@ LL | let _: &str = unsafe { std::mem::transmute(b) }; = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:137:32 + --> $DIR/transmute.rs:131:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a pointer to a pointer - --> $DIR/transmute.rs:169:29 + --> $DIR/transmute.rs:163:29 | LL | let _: *const f32 = std::mem::transmute(ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr as *const f32` @@ -213,31 +213,31 @@ LL | let _: *const f32 = std::mem::transmute(ptr); = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmute.rs:170:27 + --> $DIR/transmute.rs:164:27 | LL | let _: *mut f32 = std::mem::transmute(mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32` error: transmute from a reference to a reference - --> $DIR/transmute.rs:172:23 + --> $DIR/transmute.rs:166:23 | LL | let _: &f32 = std::mem::transmute(&1u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)` error: transmute from a reference to a reference - --> $DIR/transmute.rs:173:23 + --> $DIR/transmute.rs:167:23 | LL | let _: &f64 = std::mem::transmute(&1f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f32 as *const f32 as *const f64)` error: transmute from a reference to a reference - --> $DIR/transmute.rs:176:27 + --> $DIR/transmute.rs:170:27 | LL | let _: &mut f32 = std::mem::transmute(&mut 1u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)` error: transmute from a reference to a reference - --> $DIR/transmute.rs:177:37 + --> $DIR/transmute.rs:171:37 | LL | let _: &GenericParam = std::mem::transmute(&GenericParam { t: 1u32 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam as *const GenericParam)` diff --git a/tests/ui/transmute_collection.rs b/tests/ui/transmute_collection.rs new file mode 100644 index 000000000000..cd5a7127791a --- /dev/null +++ b/tests/ui/transmute_collection.rs @@ -0,0 +1,47 @@ +#![warn(clippy::unsound_collection_transmute)] + +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; +use std::mem::transmute; + +fn main() { + unsafe { + // wrong size + let _ = transmute::<_, Vec>(vec![0u8]); + // wrong layout + let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]); + + // wrong size + let _ = transmute::<_, VecDeque>(VecDeque::::new()); + // wrong layout + let _ = transmute::<_, VecDeque>(VecDeque::<[u8; 4]>::new()); + + // wrong size + let _ = transmute::<_, BinaryHeap>(BinaryHeap::::new()); + // wrong layout + let _ = transmute::<_, BinaryHeap>(BinaryHeap::<[u8; 4]>::new()); + + // wrong size + let _ = transmute::<_, BTreeSet>(BTreeSet::::new()); + // wrong layout + let _ = transmute::<_, BTreeSet>(BTreeSet::<[u8; 4]>::new()); + + // wrong size + let _ = transmute::<_, HashSet>(HashSet::::new()); + // wrong layout + let _ = transmute::<_, HashSet>(HashSet::<[u8; 4]>::new()); + + // wrong size + let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + // wrong layout + let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + let _ = transmute::<_, BTreeMap>(BTreeMap::<[u8; 4], u32>::new()); + + // wrong size + let _ = transmute::<_, HashMap>(HashMap::::new()); + let _ = transmute::<_, HashMap>(HashMap::::new()); + // wrong layout + let _ = transmute::<_, HashMap>(HashMap::::new()); + let _ = transmute::<_, HashMap>(HashMap::<[u8; 4], u32>::new()); + } +} diff --git a/tests/ui/transmute_collection.stderr b/tests/ui/transmute_collection.stderr new file mode 100644 index 000000000000..ebc05c402abf --- /dev/null +++ b/tests/ui/transmute_collection.stderr @@ -0,0 +1,112 @@ +error: transmute from `std::vec::Vec` to `std::vec::Vec` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:9:17 + | +LL | let _ = transmute::<_, Vec>(vec![0u8]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unsound-collection-transmute` implied by `-D warnings` + +error: transmute from `std::vec::Vec` to `std::vec::Vec<[u8; 4]>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:11:17 + | +LL | let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::VecDeque` to `std::collections::VecDeque` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:14:17 + | +LL | let _ = transmute::<_, VecDeque>(VecDeque::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::VecDeque<[u8; 4]>` to `std::collections::VecDeque` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:16:17 + | +LL | let _ = transmute::<_, VecDeque>(VecDeque::<[u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BinaryHeap` to `std::collections::BinaryHeap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:19:17 + | +LL | let _ = transmute::<_, BinaryHeap>(BinaryHeap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BinaryHeap<[u8; 4]>` to `std::collections::BinaryHeap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:21:17 + | +LL | let _ = transmute::<_, BinaryHeap>(BinaryHeap::<[u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeSet` to `std::collections::BTreeSet` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:24:17 + | +LL | let _ = transmute::<_, BTreeSet>(BTreeSet::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeSet<[u8; 4]>` to `std::collections::BTreeSet` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:26:17 + | +LL | let _ = transmute::<_, BTreeSet>(BTreeSet::<[u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashSet` to `std::collections::HashSet` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:29:17 + | +LL | let _ = transmute::<_, HashSet>(HashSet::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashSet<[u8; 4]>` to `std::collections::HashSet` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:31:17 + | +LL | let _ = transmute::<_, HashSet>(HashSet::<[u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:34:17 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:35:17 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:37:17 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeMap<[u8; 4], u32>` to `std::collections::BTreeMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:38:17 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::<[u8; 4], u32>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashMap` to `std::collections::HashMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:41:17 + | +LL | let _ = transmute::<_, HashMap>(HashMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashMap` to `std::collections::HashMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:42:17 + | +LL | let _ = transmute::<_, HashMap>(HashMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashMap` to `std::collections::HashMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:44:17 + | +LL | let _ = transmute::<_, HashMap>(HashMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashMap<[u8; 4], u32>` to `std::collections::HashMap` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:45:17 + | +LL | let _ = transmute::<_, HashMap>(HashMap::<[u8; 4], u32>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 18 previous errors +