about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAndre Bogus <bogusandre@gmail.com>2019-09-27 19:19:26 +0200
committerAndre Bogus <bogusandre@gmail.com>2019-10-02 21:18:00 +0200
commit27fa2b7944d79c1fa16445628fef268bdb34f625 (patch)
treec8a22793735ff3e6ff56cfa0025401b47042421d
parent737f0a6bb508706b75e21194e3010aa3865e779a (diff)
downloadrust-27fa2b7944d79c1fa16445628fef268bdb34f625.tar.gz
rust-27fa2b7944d79c1fa16445628fef268bdb34f625.zip
New lint: unsound_collection_transmute
-rw-r--r--CHANGELOG.md1
-rw-r--r--README.md2
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/transmute.rs530
-rw-r--r--src/lintlist/mod.rs9
-rw-r--r--tests/ui/transmute.rs6
-rw-r--r--tests/ui/transmute.stderr38
-rw-r--r--tests/ui/transmute_collection.rs47
-rw-r--r--tests/ui/transmute_collection.stderr112
9 files changed, 490 insertions, 257 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c4e04586530..4fe97097fda 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1225,6 +1225,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 2f7032af87d..9a75d2ced08 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 318 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
+[There are 319 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 10a14f3b906..1414b4ad87d 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -890,6 +890,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,
@@ -1145,6 +1146,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 1f029491df4..1339555f9ce 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
+    /// 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<u32>>(vec![2_u16])
+    /// };
+    /// ```
+    ///
+    /// You must always iterate, map and collect the values:
+    ///
+    /// ```rust
+    /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
+    /// ```
+    pub UNSOUND_COLLECTION_TRANSMUTE,
+    correctness,
+    "transmute between collections of layout-incompatible types"
+}
 declare_lint_pass!(Transmute => [
     CROSSPOINTER_TRANSMUTE,
     TRANSMUTE_PTR_TO_REF,
@@ -226,262 +253,292 @@ 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 383be8b1da4..002709ac0d4 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; 318] = [
+pub const ALL_LINTS: [Lint; 319] = [
     Lint {
         name: "absurd_extreme_comparisons",
         group: "correctness",
@@ -2031,6 +2031,13 @@ pub const ALL_LINTS: [Lint; 318] = [
         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",
         desc: "unused written/read amount",
diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs
index 6ec7697c0d6..4f0c2f5a895 100644
--- a/tests/ui/transmute.rs
+++ b/tests/ui/transmute.rs
@@ -80,12 +80,6 @@ fn useless() {
 
         let _: Vec<i32> = my_transmute(my_vec());
 
-        let _: Vec<u32> = core::intrinsics::transmute(my_vec());
-        let _: Vec<u32> = core::mem::transmute(my_vec());
-        let _: Vec<u32> = std::intrinsics::transmute(my_vec());
-        let _: Vec<u32> = std::mem::transmute(my_vec());
-        let _: Vec<u32> = 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 ceee86d224d..f73d72f20bb 100644
--- a/tests/ui/transmute.stderr
+++ b/tests/ui/transmute.stderr
@@ -117,19 +117,19 @@ LL |         let _: Vec<i32> = 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<f32> = std::mem::transmute(&GenericParam { t: 1u32 });
    |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)`
diff --git a/tests/ui/transmute_collection.rs b/tests/ui/transmute_collection.rs
new file mode 100644
index 00000000000..cd5a7127791
--- /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<u32>>(vec![0u8]);
+        // wrong layout
+        let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]);
+
+        // wrong size
+        let _ = transmute::<_, VecDeque<u32>>(VecDeque::<u8>::new());
+        // wrong layout
+        let _ = transmute::<_, VecDeque<u32>>(VecDeque::<[u8; 4]>::new());
+
+        // wrong size
+        let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<u8>::new());
+        // wrong layout
+        let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<[u8; 4]>::new());
+
+        // wrong size
+        let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<u8>::new());
+        // wrong layout
+        let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<[u8; 4]>::new());
+
+        // wrong size
+        let _ = transmute::<_, HashSet<u32>>(HashSet::<u8>::new());
+        // wrong layout
+        let _ = transmute::<_, HashSet<u32>>(HashSet::<[u8; 4]>::new());
+
+        // wrong size
+        let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, u8>::new());
+        let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u32, u32>::new());
+        // wrong layout
+        let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, [u8; 4]>::new());
+        let _ = transmute::<_, BTreeMap<u32, u32>>(BTreeMap::<[u8; 4], u32>::new());
+
+        // wrong size
+        let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, u8>::new());
+        let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u32, u32>::new());
+        // wrong layout
+        let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, [u8; 4]>::new());
+        let _ = transmute::<_, HashMap<u32, u32>>(HashMap::<[u8; 4], u32>::new());
+    }
+}
diff --git a/tests/ui/transmute_collection.stderr b/tests/ui/transmute_collection.stderr
new file mode 100644
index 00000000000..ebc05c402ab
--- /dev/null
+++ b/tests/ui/transmute_collection.stderr
@@ -0,0 +1,112 @@
+error: transmute from `std::vec::Vec<u8>` to `std::vec::Vec<u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:9:17
+   |
+LL |         let _ = transmute::<_, Vec<u32>>(vec![0u8]);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::unsound-collection-transmute` implied by `-D warnings`
+
+error: transmute from `std::vec::Vec<u32>` 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<u8>` to `std::collections::VecDeque<u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:14:17
+   |
+LL |         let _ = transmute::<_, VecDeque<u32>>(VecDeque::<u8>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::VecDeque<[u8; 4]>` to `std::collections::VecDeque<u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:16:17
+   |
+LL |         let _ = transmute::<_, VecDeque<u32>>(VecDeque::<[u8; 4]>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BinaryHeap<u8>` to `std::collections::BinaryHeap<u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:19:17
+   |
+LL |         let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<u8>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BinaryHeap<[u8; 4]>` to `std::collections::BinaryHeap<u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:21:17
+   |
+LL |         let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<[u8; 4]>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BTreeSet<u8>` to `std::collections::BTreeSet<u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:24:17
+   |
+LL |         let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<u8>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BTreeSet<[u8; 4]>` to `std::collections::BTreeSet<u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:26:17
+   |
+LL |         let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<[u8; 4]>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::HashSet<u8>` to `std::collections::HashSet<u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:29:17
+   |
+LL |         let _ = transmute::<_, HashSet<u32>>(HashSet::<u8>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::HashSet<[u8; 4]>` to `std::collections::HashSet<u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:31:17
+   |
+LL |         let _ = transmute::<_, HashSet<u32>>(HashSet::<[u8; 4]>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BTreeMap<u8, u8>` to `std::collections::BTreeMap<u8, u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:34:17
+   |
+LL |         let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, u8>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BTreeMap<u32, u32>` to `std::collections::BTreeMap<u8, u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:35:17
+   |
+LL |         let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u32, u32>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BTreeMap<u8, [u8; 4]>` to `std::collections::BTreeMap<u8, u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:37:17
+   |
+LL |         let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, [u8; 4]>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::BTreeMap<[u8; 4], u32>` to `std::collections::BTreeMap<u32, u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:38:17
+   |
+LL |         let _ = transmute::<_, BTreeMap<u32, u32>>(BTreeMap::<[u8; 4], u32>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::HashMap<u8, u8>` to `std::collections::HashMap<u8, u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:41:17
+   |
+LL |         let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, u8>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::HashMap<u32, u32>` to `std::collections::HashMap<u8, u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:42:17
+   |
+LL |         let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u32, u32>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::HashMap<u8, [u8; 4]>` to `std::collections::HashMap<u8, u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:44:17
+   |
+LL |         let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, [u8; 4]>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `std::collections::HashMap<[u8; 4], u32>` to `std::collections::HashMap<u32, u32>` with mismatched layout is unsound
+  --> $DIR/transmute_collection.rs:45:17
+   |
+LL |         let _ = transmute::<_, HashMap<u32, u32>>(HashMap::<[u8; 4], u32>::new());
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 18 previous errors
+