Auto merge of #8398 - Jarcho:unordered_transmute, r=llogiq
Add lint `transmute_undefined_repr` Partially implements #3999 and #546 This doesn't consider `enum`s at all right now as those are going to be a pain to deal with. This also allows `#[repr(Rust)]` structs with only one non-zero sized fields. I think those are technically undefined when transmuted. changelog: Add lint `transmute_undefined_repr`
This commit is contained in:
commit
093e32052e
@ -3475,6 +3475,7 @@ Released 2018-09-13
|
|||||||
[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
|
[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
|
||||||
[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
|
[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
|
||||||
[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
|
[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
|
||||||
|
[`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr
|
||||||
[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
|
[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
|
||||||
[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
|
[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
|
||||||
[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
|
[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
|
||||||
|
@ -277,6 +277,7 @@
|
|||||||
LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
|
LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
|
||||||
LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
|
LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
|
||||||
LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
|
LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
|
||||||
|
LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
|
||||||
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
|
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
|
||||||
LintId::of(transmute::WRONG_TRANSMUTE),
|
LintId::of(transmute::WRONG_TRANSMUTE),
|
||||||
LintId::of(transmuting_null::TRANSMUTING_NULL),
|
LintId::of(transmuting_null::TRANSMUTING_NULL),
|
||||||
|
@ -58,6 +58,7 @@
|
|||||||
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
|
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
|
||||||
LintId::of(swap::ALMOST_SWAPPED),
|
LintId::of(swap::ALMOST_SWAPPED),
|
||||||
LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY),
|
LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY),
|
||||||
|
LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
|
||||||
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
|
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
|
||||||
LintId::of(transmute::WRONG_TRANSMUTE),
|
LintId::of(transmute::WRONG_TRANSMUTE),
|
||||||
LintId::of(transmuting_null::TRANSMUTING_NULL),
|
LintId::of(transmuting_null::TRANSMUTING_NULL),
|
||||||
|
@ -473,6 +473,7 @@
|
|||||||
transmute::TRANSMUTE_NUM_TO_BYTES,
|
transmute::TRANSMUTE_NUM_TO_BYTES,
|
||||||
transmute::TRANSMUTE_PTR_TO_PTR,
|
transmute::TRANSMUTE_PTR_TO_PTR,
|
||||||
transmute::TRANSMUTE_PTR_TO_REF,
|
transmute::TRANSMUTE_PTR_TO_REF,
|
||||||
|
transmute::TRANSMUTE_UNDEFINED_REPR,
|
||||||
transmute::UNSOUND_COLLECTION_TRANSMUTE,
|
transmute::UNSOUND_COLLECTION_TRANSMUTE,
|
||||||
transmute::USELESS_TRANSMUTE,
|
transmute::USELESS_TRANSMUTE,
|
||||||
transmute::WRONG_TRANSMUTE,
|
transmute::WRONG_TRANSMUTE,
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#![feature(control_flow_enum)]
|
#![feature(control_flow_enum)]
|
||||||
#![feature(drain_filter)]
|
#![feature(drain_filter)]
|
||||||
#![feature(iter_intersperse)]
|
#![feature(iter_intersperse)]
|
||||||
|
#![feature(let_chains)]
|
||||||
#![feature(let_else)]
|
#![feature(let_else)]
|
||||||
#![feature(once_cell)]
|
#![feature(once_cell)]
|
||||||
#![feature(rustc_private)]
|
#![feature(rustc_private)]
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
mod transmute_ptr_to_ptr;
|
mod transmute_ptr_to_ptr;
|
||||||
mod transmute_ptr_to_ref;
|
mod transmute_ptr_to_ref;
|
||||||
mod transmute_ref_to_ref;
|
mod transmute_ref_to_ref;
|
||||||
|
mod transmute_undefined_repr;
|
||||||
mod transmutes_expressible_as_ptr_casts;
|
mod transmutes_expressible_as_ptr_casts;
|
||||||
mod unsound_collection_transmute;
|
mod unsound_collection_transmute;
|
||||||
mod useless_transmute;
|
mod useless_transmute;
|
||||||
@ -355,6 +356,30 @@
|
|||||||
"transmute between collections of layout-incompatible types"
|
"transmute between collections of layout-incompatible types"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for transmutes either to or from a type which does not have a defined representation.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// The results of such a transmute are not defined.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust
|
||||||
|
/// struct Foo<T>(u32, T);
|
||||||
|
/// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// #[repr(C)]
|
||||||
|
/// struct Foo<T>(u32, T);
|
||||||
|
/// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.60.0"]
|
||||||
|
pub TRANSMUTE_UNDEFINED_REPR,
|
||||||
|
correctness,
|
||||||
|
"transmute to or from a type with an undefined representation"
|
||||||
|
}
|
||||||
|
|
||||||
declare_lint_pass!(Transmute => [
|
declare_lint_pass!(Transmute => [
|
||||||
CROSSPOINTER_TRANSMUTE,
|
CROSSPOINTER_TRANSMUTE,
|
||||||
TRANSMUTE_PTR_TO_REF,
|
TRANSMUTE_PTR_TO_REF,
|
||||||
@ -369,13 +394,13 @@
|
|||||||
TRANSMUTE_NUM_TO_BYTES,
|
TRANSMUTE_NUM_TO_BYTES,
|
||||||
UNSOUND_COLLECTION_TRANSMUTE,
|
UNSOUND_COLLECTION_TRANSMUTE,
|
||||||
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
|
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
|
||||||
|
TRANSMUTE_UNDEFINED_REPR,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for Transmute {
|
impl<'tcx> LateLintPass<'tcx> for Transmute {
|
||||||
#[allow(clippy::similar_names, clippy::too_many_lines)]
|
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let ExprKind::Call(path_expr, args) = e.kind;
|
if let ExprKind::Call(path_expr, [arg]) = e.kind;
|
||||||
if let ExprKind::Path(ref qpath) = path_expr.kind;
|
if let ExprKind::Path(ref qpath) = path_expr.kind;
|
||||||
if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
|
if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
|
||||||
if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
|
if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
|
||||||
@ -385,28 +410,31 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
|||||||
// And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers.
|
// And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers.
|
||||||
let const_context = in_constant(cx, e.hir_id);
|
let const_context = in_constant(cx, e.hir_id);
|
||||||
|
|
||||||
let from_ty = cx.typeck_results().expr_ty(&args[0]);
|
let from_ty = cx.typeck_results().expr_ty(arg);
|
||||||
let to_ty = cx.typeck_results().expr_ty(e);
|
let to_ty = cx.typeck_results().expr_ty(e);
|
||||||
|
|
||||||
// If useless_transmute is triggered, the other lints can be skipped.
|
// If useless_transmute is triggered, the other lints can be skipped.
|
||||||
if useless_transmute::check(cx, e, from_ty, to_ty, args) {
|
if useless_transmute::check(cx, e, from_ty, to_ty, arg) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut linted = wrong_transmute::check(cx, e, from_ty, to_ty);
|
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
|
||||||
linted |= crosspointer_transmute::check(cx, e, from_ty, to_ty);
|
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
|
||||||
linted |= transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, args, qpath);
|
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, qpath)
|
||||||
linted |= transmute_int_to_char::check(cx, e, from_ty, to_ty, args);
|
| transmute_int_to_char::check(cx, e, from_ty, to_ty, arg)
|
||||||
linted |= transmute_ref_to_ref::check(cx, e, from_ty, to_ty, args, const_context);
|
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||||
linted |= transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, args);
|
| transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
|
||||||
linted |= transmute_int_to_bool::check(cx, e, from_ty, to_ty, args);
|
| transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
|
||||||
linted |= transmute_int_to_float::check(cx, e, from_ty, to_ty, args, const_context);
|
| transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||||
linted |= transmute_float_to_int::check(cx, e, from_ty, to_ty, args, const_context);
|
| transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||||
linted |= transmute_num_to_bytes::check(cx, e, from_ty, to_ty, args, const_context);
|
| transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||||
linted |= unsound_collection_transmute::check(cx, e, from_ty, to_ty);
|
| (
|
||||||
|
unsound_collection_transmute::check(cx, e, from_ty, to_ty)
|
||||||
|
|| transmute_undefined_repr::check(cx, e, from_ty, to_ty)
|
||||||
|
);
|
||||||
|
|
||||||
if !linted {
|
if !linted {
|
||||||
transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, args);
|
transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e: &'tcx Expr<'_>,
|
e: &'tcx Expr<'_>,
|
||||||
from_ty: Ty<'tcx>,
|
from_ty: Ty<'tcx>,
|
||||||
to_ty: Ty<'tcx>,
|
to_ty: Ty<'tcx>,
|
||||||
args: &'tcx [Expr<'_>],
|
mut arg: &'tcx Expr<'_>,
|
||||||
const_context: bool,
|
const_context: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match (&from_ty.kind(), &to_ty.kind()) {
|
match (&from_ty.kind(), &to_ty.kind()) {
|
||||||
@ -26,37 +26,36 @@ pub(super) fn check<'tcx>(
|
|||||||
e.span,
|
e.span,
|
||||||
&format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
|
&format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
|
||||||
|diag| {
|
|diag| {
|
||||||
let mut expr = &args[0];
|
let mut sugg = sugg::Sugg::hir(cx, arg, "..");
|
||||||
let mut arg = sugg::Sugg::hir(cx, expr, "..");
|
|
||||||
|
|
||||||
if let ExprKind::Unary(UnOp::Neg, inner_expr) = &expr.kind {
|
if let ExprKind::Unary(UnOp::Neg, inner_expr) = &arg.kind {
|
||||||
expr = inner_expr;
|
arg = inner_expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if_chain! {
|
if_chain! {
|
||||||
// if the expression is a float literal and it is unsuffixed then
|
// if the expression is a float literal and it is unsuffixed then
|
||||||
// add a suffix so the suggestion is valid and unambiguous
|
// add a suffix so the suggestion is valid and unambiguous
|
||||||
if let ExprKind::Lit(lit) = &expr.kind;
|
if let ExprKind::Lit(lit) = &arg.kind;
|
||||||
if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node;
|
if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node;
|
||||||
then {
|
then {
|
||||||
let op = format!("{}{}", arg, float_ty.name_str()).into();
|
let op = format!("{}{}", sugg, float_ty.name_str()).into();
|
||||||
match arg {
|
match sugg {
|
||||||
sugg::Sugg::MaybeParen(_) => arg = sugg::Sugg::MaybeParen(op),
|
sugg::Sugg::MaybeParen(_) => sugg = sugg::Sugg::MaybeParen(op),
|
||||||
_ => arg = sugg::Sugg::NonParen(op)
|
_ => sugg = sugg::Sugg::NonParen(op)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
arg = sugg::Sugg::NonParen(format!("{}.to_bits()", arg.maybe_par()).into());
|
sugg = sugg::Sugg::NonParen(format!("{}.to_bits()", sugg.maybe_par()).into());
|
||||||
|
|
||||||
// cast the result of `to_bits` if `to_ty` is signed
|
// cast the result of `to_bits` if `to_ty` is signed
|
||||||
arg = if let ty::Int(int_ty) = to_ty.kind() {
|
sugg = if let ty::Int(int_ty) = to_ty.kind() {
|
||||||
arg.as_ty(int_ty.name_str().to_string())
|
sugg.as_ty(int_ty.name_str().to_string())
|
||||||
} else {
|
} else {
|
||||||
arg
|
sugg
|
||||||
};
|
};
|
||||||
|
|
||||||
diag.span_suggestion(e.span, "consider using", arg.to_string(), Applicability::Unspecified);
|
diag.span_suggestion(e.span, "consider using", sugg.to_string(), Applicability::Unspecified);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
true
|
true
|
||||||
|
@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e: &'tcx Expr<'_>,
|
e: &'tcx Expr<'_>,
|
||||||
from_ty: Ty<'tcx>,
|
from_ty: Ty<'tcx>,
|
||||||
to_ty: Ty<'tcx>,
|
to_ty: Ty<'tcx>,
|
||||||
args: &'tcx [Expr<'_>],
|
arg: &'tcx Expr<'_>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match (&from_ty.kind(), &to_ty.kind()) {
|
match (&from_ty.kind(), &to_ty.kind()) {
|
||||||
(ty::Int(ty::IntTy::I8) | ty::Uint(ty::UintTy::U8), ty::Bool) => {
|
(ty::Int(ty::IntTy::I8) | ty::Uint(ty::UintTy::U8), ty::Bool) => {
|
||||||
@ -25,7 +25,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e.span,
|
e.span,
|
||||||
&format!("transmute from a `{}` to a `bool`", from_ty),
|
&format!("transmute from a `{}` to a `bool`", from_ty),
|
||||||
|diag| {
|
|diag| {
|
||||||
let arg = sugg::Sugg::hir(cx, &args[0], "..");
|
let arg = sugg::Sugg::hir(cx, arg, "..");
|
||||||
let zero = sugg::Sugg::NonParen(Cow::from("0"));
|
let zero = sugg::Sugg::NonParen(Cow::from("0"));
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(
|
||||||
e.span,
|
e.span,
|
||||||
|
@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e: &'tcx Expr<'_>,
|
e: &'tcx Expr<'_>,
|
||||||
from_ty: Ty<'tcx>,
|
from_ty: Ty<'tcx>,
|
||||||
to_ty: Ty<'tcx>,
|
to_ty: Ty<'tcx>,
|
||||||
args: &'tcx [Expr<'_>],
|
arg: &'tcx Expr<'_>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match (&from_ty.kind(), &to_ty.kind()) {
|
match (&from_ty.kind(), &to_ty.kind()) {
|
||||||
(ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) => {
|
(ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) => {
|
||||||
@ -24,7 +24,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e.span,
|
e.span,
|
||||||
&format!("transmute from a `{}` to a `char`", from_ty),
|
&format!("transmute from a `{}` to a `char`", from_ty),
|
||||||
|diag| {
|
|diag| {
|
||||||
let arg = sugg::Sugg::hir(cx, &args[0], "..");
|
let arg = sugg::Sugg::hir(cx, arg, "..");
|
||||||
let arg = if let ty::Int(_) = from_ty.kind() {
|
let arg = if let ty::Int(_) = from_ty.kind() {
|
||||||
arg.as_ty(ast::UintTy::U32.name_str())
|
arg.as_ty(ast::UintTy::U32.name_str())
|
||||||
} else {
|
} else {
|
||||||
|
@ -13,7 +13,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e: &'tcx Expr<'_>,
|
e: &'tcx Expr<'_>,
|
||||||
from_ty: Ty<'tcx>,
|
from_ty: Ty<'tcx>,
|
||||||
to_ty: Ty<'tcx>,
|
to_ty: Ty<'tcx>,
|
||||||
args: &'tcx [Expr<'_>],
|
arg: &'tcx Expr<'_>,
|
||||||
const_context: bool,
|
const_context: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match (&from_ty.kind(), &to_ty.kind()) {
|
match (&from_ty.kind(), &to_ty.kind()) {
|
||||||
@ -24,7 +24,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e.span,
|
e.span,
|
||||||
&format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
|
&format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
|
||||||
|diag| {
|
|diag| {
|
||||||
let arg = sugg::Sugg::hir(cx, &args[0], "..");
|
let arg = sugg::Sugg::hir(cx, arg, "..");
|
||||||
let arg = if let ty::Int(int_ty) = from_ty.kind() {
|
let arg = if let ty::Int(int_ty) = from_ty.kind() {
|
||||||
arg.as_ty(format!(
|
arg.as_ty(format!(
|
||||||
"u{}",
|
"u{}",
|
||||||
|
@ -13,7 +13,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e: &'tcx Expr<'_>,
|
e: &'tcx Expr<'_>,
|
||||||
from_ty: Ty<'tcx>,
|
from_ty: Ty<'tcx>,
|
||||||
to_ty: Ty<'tcx>,
|
to_ty: Ty<'tcx>,
|
||||||
args: &'tcx [Expr<'_>],
|
arg: &'tcx Expr<'_>,
|
||||||
const_context: bool,
|
const_context: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match (&from_ty.kind(), &to_ty.kind()) {
|
match (&from_ty.kind(), &to_ty.kind()) {
|
||||||
@ -33,7 +33,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e.span,
|
e.span,
|
||||||
&format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
|
&format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
|
||||||
|diag| {
|
|diag| {
|
||||||
let arg = sugg::Sugg::hir(cx, &args[0], "..");
|
let arg = sugg::Sugg::hir(cx, arg, "..");
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(
|
||||||
e.span,
|
e.span,
|
||||||
"consider using `to_ne_bytes()`",
|
"consider using `to_ne_bytes()`",
|
||||||
|
@ -13,7 +13,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e: &'tcx Expr<'_>,
|
e: &'tcx Expr<'_>,
|
||||||
from_ty: Ty<'tcx>,
|
from_ty: Ty<'tcx>,
|
||||||
to_ty: Ty<'tcx>,
|
to_ty: Ty<'tcx>,
|
||||||
args: &'tcx [Expr<'_>],
|
arg: &'tcx Expr<'_>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match (&from_ty.kind(), &to_ty.kind()) {
|
match (&from_ty.kind(), &to_ty.kind()) {
|
||||||
(ty::RawPtr(_), ty::RawPtr(to_ty)) => {
|
(ty::RawPtr(_), ty::RawPtr(to_ty)) => {
|
||||||
@ -23,7 +23,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e.span,
|
e.span,
|
||||||
"transmute from a pointer to a pointer",
|
"transmute from a pointer to a pointer",
|
||||||
|diag| {
|
|diag| {
|
||||||
if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
|
if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
|
||||||
let sugg = arg.as_ty(cx.tcx.mk_ptr(*to_ty));
|
let sugg = arg.as_ty(cx.tcx.mk_ptr(*to_ty));
|
||||||
diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified);
|
diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e: &'tcx Expr<'_>,
|
e: &'tcx Expr<'_>,
|
||||||
from_ty: Ty<'tcx>,
|
from_ty: Ty<'tcx>,
|
||||||
to_ty: Ty<'tcx>,
|
to_ty: Ty<'tcx>,
|
||||||
args: &'tcx [Expr<'_>],
|
arg: &'tcx Expr<'_>,
|
||||||
qpath: &'tcx QPath<'_>,
|
qpath: &'tcx QPath<'_>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match (&from_ty.kind(), &to_ty.kind()) {
|
match (&from_ty.kind(), &to_ty.kind()) {
|
||||||
@ -28,7 +28,7 @@ pub(super) fn check<'tcx>(
|
|||||||
from_ty, to_ty
|
from_ty, to_ty
|
||||||
),
|
),
|
||||||
|diag| {
|
|diag| {
|
||||||
let arg = sugg::Sugg::hir(cx, &args[0], "..");
|
let arg = sugg::Sugg::hir(cx, arg, "..");
|
||||||
let (deref, cast) = if *mutbl == Mutability::Mut {
|
let (deref, cast) = if *mutbl == Mutability::Mut {
|
||||||
("&mut *", "*mut")
|
("&mut *", "*mut")
|
||||||
} else {
|
} else {
|
||||||
|
@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e: &'tcx Expr<'_>,
|
e: &'tcx Expr<'_>,
|
||||||
from_ty: Ty<'tcx>,
|
from_ty: Ty<'tcx>,
|
||||||
to_ty: Ty<'tcx>,
|
to_ty: Ty<'tcx>,
|
||||||
args: &'tcx [Expr<'_>],
|
arg: &'tcx Expr<'_>,
|
||||||
const_context: bool,
|
const_context: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut triggered = false;
|
let mut triggered = false;
|
||||||
@ -41,7 +41,7 @@ pub(super) fn check<'tcx>(
|
|||||||
format!(
|
format!(
|
||||||
"std::str::from_utf8{}({}).unwrap()",
|
"std::str::from_utf8{}({}).unwrap()",
|
||||||
postfix,
|
postfix,
|
||||||
snippet(cx, args[0].span, ".."),
|
snippet(cx, arg.span, ".."),
|
||||||
),
|
),
|
||||||
Applicability::Unspecified,
|
Applicability::Unspecified,
|
||||||
);
|
);
|
||||||
@ -54,7 +54,7 @@ pub(super) fn check<'tcx>(
|
|||||||
TRANSMUTE_PTR_TO_PTR,
|
TRANSMUTE_PTR_TO_PTR,
|
||||||
e.span,
|
e.span,
|
||||||
"transmute from a reference to a reference",
|
"transmute from a reference to a reference",
|
||||||
|diag| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
|
|diag| if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
|
||||||
let ty_from_and_mut = ty::TypeAndMut {
|
let ty_from_and_mut = ty::TypeAndMut {
|
||||||
ty: ty_from,
|
ty: ty_from,
|
||||||
mutbl: *from_mutbl
|
mutbl: *from_mutbl
|
||||||
|
291
clippy_lints/src/transmute/transmute_undefined_repr.rs
Normal file
291
clippy_lints/src/transmute/transmute_undefined_repr.rs
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
use super::TRANSMUTE_UNDEFINED_REPR;
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
|
use rustc_hir::Expr;
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_middle::ty::subst::{GenericArg, Subst};
|
||||||
|
use rustc_middle::ty::{self, Ty, TyS, TypeAndMut};
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
pub(super) fn check<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
e: &'tcx Expr<'_>,
|
||||||
|
from_ty_orig: Ty<'tcx>,
|
||||||
|
to_ty_orig: Ty<'tcx>,
|
||||||
|
) -> bool {
|
||||||
|
let mut from_ty = cx.tcx.erase_regions(from_ty_orig);
|
||||||
|
let mut to_ty = cx.tcx.erase_regions(to_ty_orig);
|
||||||
|
|
||||||
|
while !TyS::same_type(from_ty, to_ty) {
|
||||||
|
match reduce_refs(cx, e.span, from_ty, to_ty) {
|
||||||
|
ReducedTys::FromFatPtr { unsized_ty, .. } => {
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
TRANSMUTE_UNDEFINED_REPR,
|
||||||
|
e.span,
|
||||||
|
&format!("transmute from `{}` which has an undefined layout", from_ty_orig),
|
||||||
|
|diag| {
|
||||||
|
if !TyS::same_type(from_ty_orig.peel_refs(), unsized_ty) {
|
||||||
|
diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
ReducedTys::ToFatPtr { unsized_ty, .. } => {
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
TRANSMUTE_UNDEFINED_REPR,
|
||||||
|
e.span,
|
||||||
|
&format!("transmute to `{}` which has an undefined layout", to_ty_orig),
|
||||||
|
|diag| {
|
||||||
|
if !TyS::same_type(to_ty_orig.peel_refs(), unsized_ty) {
|
||||||
|
diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
ReducedTys::ToPtr {
|
||||||
|
from_ty: from_sub_ty,
|
||||||
|
to_ty: to_sub_ty,
|
||||||
|
} => match reduce_ty(cx, from_sub_ty) {
|
||||||
|
ReducedTy::UnorderedFields(from_ty) => {
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
TRANSMUTE_UNDEFINED_REPR,
|
||||||
|
e.span,
|
||||||
|
&format!("transmute from `{}` which has an undefined layout", from_ty_orig),
|
||||||
|
|diag| {
|
||||||
|
if !TyS::same_type(from_ty_orig.peel_refs(), from_ty) {
|
||||||
|
diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
ReducedTy::Ref(from_sub_ty) => {
|
||||||
|
from_ty = from_sub_ty;
|
||||||
|
to_ty = to_sub_ty;
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
_ => break,
|
||||||
|
},
|
||||||
|
ReducedTys::FromPtr {
|
||||||
|
from_ty: from_sub_ty,
|
||||||
|
to_ty: to_sub_ty,
|
||||||
|
} => match reduce_ty(cx, to_sub_ty) {
|
||||||
|
ReducedTy::UnorderedFields(to_ty) => {
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
TRANSMUTE_UNDEFINED_REPR,
|
||||||
|
e.span,
|
||||||
|
&format!("transmute to `{}` which has an undefined layout", to_ty_orig),
|
||||||
|
|diag| {
|
||||||
|
if !TyS::same_type(to_ty_orig.peel_refs(), to_ty) {
|
||||||
|
diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
ReducedTy::Ref(to_sub_ty) => {
|
||||||
|
from_ty = from_sub_ty;
|
||||||
|
to_ty = to_sub_ty;
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
_ => break,
|
||||||
|
},
|
||||||
|
ReducedTys::Other {
|
||||||
|
from_ty: from_sub_ty,
|
||||||
|
to_ty: to_sub_ty,
|
||||||
|
} => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) {
|
||||||
|
(ReducedTy::IntArray, _) | (_, ReducedTy::IntArray) => return false,
|
||||||
|
(ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty))
|
||||||
|
if !TyS::same_type(from_ty, to_ty) =>
|
||||||
|
{
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
TRANSMUTE_UNDEFINED_REPR,
|
||||||
|
e.span,
|
||||||
|
&format!(
|
||||||
|
"transmute from `{}` to `{}`, both of which have an undefined layout",
|
||||||
|
from_ty_orig, to_ty_orig
|
||||||
|
),
|
||||||
|
|diag| {
|
||||||
|
if let (Some(from_def), Some(to_def)) = (from_ty.ty_adt_def(), to_ty.ty_adt_def())
|
||||||
|
&& from_def == to_def
|
||||||
|
{
|
||||||
|
diag.note(&format!(
|
||||||
|
"two instances of the same generic type (`{}`) may have different layouts",
|
||||||
|
cx.tcx.item_name(from_def.did)
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
if !TyS::same_type(from_ty_orig.peel_refs(), from_ty) {
|
||||||
|
diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
|
||||||
|
}
|
||||||
|
if !TyS::same_type(to_ty_orig.peel_refs(), to_ty) {
|
||||||
|
diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
(
|
||||||
|
ReducedTy::UnorderedFields(from_ty),
|
||||||
|
ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
|
||||||
|
) => {
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
TRANSMUTE_UNDEFINED_REPR,
|
||||||
|
e.span,
|
||||||
|
&format!("transmute from `{}` which has an undefined layout", from_ty_orig),
|
||||||
|
|diag| {
|
||||||
|
if !TyS::same_type(from_ty_orig.peel_refs(), from_ty) {
|
||||||
|
diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
(
|
||||||
|
ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
|
||||||
|
ReducedTy::UnorderedFields(to_ty),
|
||||||
|
) => {
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
TRANSMUTE_UNDEFINED_REPR,
|
||||||
|
e.span,
|
||||||
|
&format!("transmute into `{}` which has an undefined layout", to_ty_orig),
|
||||||
|
|diag| {
|
||||||
|
if !TyS::same_type(to_ty_orig.peel_refs(), to_ty) {
|
||||||
|
diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
(ReducedTy::Ref(from_sub_ty), ReducedTy::Ref(to_sub_ty)) => {
|
||||||
|
from_ty = from_sub_ty;
|
||||||
|
to_ty = to_sub_ty;
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
(
|
||||||
|
ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
|
||||||
|
ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
|
||||||
|
)
|
||||||
|
| (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => break,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ReducedTys<'tcx> {
|
||||||
|
FromFatPtr { unsized_ty: Ty<'tcx> },
|
||||||
|
ToFatPtr { unsized_ty: Ty<'tcx> },
|
||||||
|
ToPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
|
||||||
|
FromPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
|
||||||
|
Other { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reduce_refs<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
span: Span,
|
||||||
|
mut from_ty: Ty<'tcx>,
|
||||||
|
mut to_ty: Ty<'tcx>,
|
||||||
|
) -> ReducedTys<'tcx> {
|
||||||
|
loop {
|
||||||
|
return match (from_ty.kind(), to_ty.kind()) {
|
||||||
|
(
|
||||||
|
ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. }),
|
||||||
|
ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. }),
|
||||||
|
) => {
|
||||||
|
from_ty = from_sub_ty;
|
||||||
|
to_ty = to_sub_ty;
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }), _)
|
||||||
|
if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
|
||||||
|
{
|
||||||
|
ReducedTys::FromFatPtr { unsized_ty }
|
||||||
|
},
|
||||||
|
(_, ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }))
|
||||||
|
if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
|
||||||
|
{
|
||||||
|
ReducedTys::ToFatPtr { unsized_ty }
|
||||||
|
},
|
||||||
|
(ty::Ref(_, from_ty, _) | ty::RawPtr(TypeAndMut { ty: from_ty, .. }), _) => {
|
||||||
|
ReducedTys::FromPtr { from_ty, to_ty }
|
||||||
|
},
|
||||||
|
(_, ty::Ref(_, to_ty, _) | ty::RawPtr(TypeAndMut { ty: to_ty, .. })) => {
|
||||||
|
ReducedTys::ToPtr { from_ty, to_ty }
|
||||||
|
},
|
||||||
|
_ => ReducedTys::Other { from_ty, to_ty },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ReducedTy<'tcx> {
|
||||||
|
OrderedFields(Ty<'tcx>),
|
||||||
|
UnorderedFields(Ty<'tcx>),
|
||||||
|
Ref(Ty<'tcx>),
|
||||||
|
Other(Ty<'tcx>),
|
||||||
|
IntArray,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> {
|
||||||
|
loop {
|
||||||
|
ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
|
||||||
|
return match *ty.kind() {
|
||||||
|
ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::IntArray,
|
||||||
|
ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
|
||||||
|
ty = sub_ty;
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
ty::Tuple(args) => {
|
||||||
|
let mut iter = args.iter().map(GenericArg::expect_ty);
|
||||||
|
let Some(sized_ty) = iter.find(|ty| !is_zero_sized_ty(cx, ty)) else {
|
||||||
|
return ReducedTy::OrderedFields(ty);
|
||||||
|
};
|
||||||
|
if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
|
||||||
|
ty = sized_ty;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ReducedTy::UnorderedFields(ty)
|
||||||
|
},
|
||||||
|
ty::Adt(def, substs) if def.is_struct() => {
|
||||||
|
if def.repr.inhibit_struct_field_reordering_opt() {
|
||||||
|
return ReducedTy::OrderedFields(ty);
|
||||||
|
}
|
||||||
|
let mut iter = def
|
||||||
|
.non_enum_variant()
|
||||||
|
.fields
|
||||||
|
.iter()
|
||||||
|
.map(|f| cx.tcx.type_of(f.did).subst(cx.tcx, substs));
|
||||||
|
let Some(sized_ty) = iter.find(|ty| !is_zero_sized_ty(cx, ty)) else {
|
||||||
|
return ReducedTy::OrderedFields(ty);
|
||||||
|
};
|
||||||
|
if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
|
||||||
|
ty = sized_ty;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ReducedTy::UnorderedFields(ty)
|
||||||
|
},
|
||||||
|
ty::Ref(..) | ty::RawPtr(_) => ReducedTy::Ref(ty),
|
||||||
|
_ => ReducedTy::Other(ty),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||||
|
if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty)
|
||||||
|
&& let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty))
|
||||||
|
{
|
||||||
|
layout.layout.size.bytes() == 0
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e: &'tcx Expr<'_>,
|
e: &'tcx Expr<'_>,
|
||||||
from_ty: Ty<'tcx>,
|
from_ty: Ty<'tcx>,
|
||||||
to_ty: Ty<'tcx>,
|
to_ty: Ty<'tcx>,
|
||||||
args: &'tcx [Expr<'_>],
|
arg: &'tcx Expr<'_>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if can_be_expressed_as_pointer_cast(cx, e, from_ty, to_ty) {
|
if can_be_expressed_as_pointer_cast(cx, e, from_ty, to_ty) {
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
@ -26,7 +26,7 @@ pub(super) fn check<'tcx>(
|
|||||||
from_ty, to_ty
|
from_ty, to_ty
|
||||||
),
|
),
|
||||||
|diag| {
|
|diag| {
|
||||||
if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
|
if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
|
||||||
let sugg = arg.as_ty(&to_ty.to_string()).to_string();
|
let sugg = arg.as_ty(&to_ty.to_string()).to_string();
|
||||||
diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable);
|
diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e: &'tcx Expr<'_>,
|
e: &'tcx Expr<'_>,
|
||||||
from_ty: Ty<'tcx>,
|
from_ty: Ty<'tcx>,
|
||||||
to_ty: Ty<'tcx>,
|
to_ty: Ty<'tcx>,
|
||||||
args: &'tcx [Expr<'_>],
|
arg: &'tcx Expr<'_>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match (&from_ty.kind(), &to_ty.kind()) {
|
match (&from_ty.kind(), &to_ty.kind()) {
|
||||||
_ if from_ty == to_ty => {
|
_ if from_ty == to_ty => {
|
||||||
@ -32,7 +32,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e.span,
|
e.span,
|
||||||
"transmute from a reference to a pointer",
|
"transmute from a reference to a pointer",
|
||||||
|diag| {
|
|diag| {
|
||||||
if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
|
if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
|
||||||
let rty_and_mut = ty::TypeAndMut {
|
let rty_and_mut = ty::TypeAndMut {
|
||||||
ty: rty,
|
ty: rty,
|
||||||
mutbl: *rty_mutbl,
|
mutbl: *rty_mutbl,
|
||||||
@ -57,7 +57,7 @@ pub(super) fn check<'tcx>(
|
|||||||
e.span,
|
e.span,
|
||||||
"transmute from an integer to a pointer",
|
"transmute from an integer to a pointer",
|
||||||
|diag| {
|
|diag| {
|
||||||
if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
|
if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(
|
||||||
e.span,
|
e.span,
|
||||||
"try",
|
"try",
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
// Test for https://github.com/rust-lang/rust-clippy/issues/4968
|
// Test for https://github.com/rust-lang/rust-clippy/issues/4968
|
||||||
|
|
||||||
#![warn(clippy::unsound_collection_transmute)]
|
#![warn(clippy::unsound_collection_transmute)]
|
||||||
|
#![allow(clippy::transmute_undefined_repr)]
|
||||||
|
|
||||||
trait Trait {
|
trait Trait {
|
||||||
type Assoc;
|
type Assoc;
|
||||||
|
44
tests/ui/transmute_undefined_repr.rs
Normal file
44
tests/ui/transmute_undefined_repr.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#![warn(clippy::transmute_undefined_repr)]
|
||||||
|
#![allow(clippy::unit_arg)]
|
||||||
|
|
||||||
|
fn value<T>() -> T {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Empty;
|
||||||
|
struct Ty<T>(T);
|
||||||
|
struct Ty2<T, U>(T, U);
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct Ty2C<T, U>(T, U);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
unsafe {
|
||||||
|
let _: () = core::mem::transmute(value::<Empty>());
|
||||||
|
let _: Empty = core::mem::transmute(value::<()>());
|
||||||
|
|
||||||
|
let _: Ty<u32> = core::mem::transmute(value::<u32>());
|
||||||
|
let _: Ty<u32> = core::mem::transmute(value::<u32>());
|
||||||
|
|
||||||
|
let _: Ty2C<u32, i32> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
|
||||||
|
let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
|
||||||
|
|
||||||
|
let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty<Ty2<u32, i32>>>()); // Ok, Ty2 types are the same
|
||||||
|
let _: Ty<Ty2<u32, i32>> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
|
||||||
|
|
||||||
|
let _: Ty2<u32, f32> = core::mem::transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
|
||||||
|
let _: Ty<Ty2<u32, i32>> = core::mem::transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
|
||||||
|
|
||||||
|
let _: Ty<&()> = core::mem::transmute(value::<&()>());
|
||||||
|
let _: &() = core::mem::transmute(value::<Ty<&()>>());
|
||||||
|
|
||||||
|
let _: &Ty2<u32, f32> = core::mem::transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
|
||||||
|
let _: Ty<&Ty2<u32, i32>> = core::mem::transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
|
||||||
|
|
||||||
|
let _: Ty<usize> = core::mem::transmute(value::<&Ty2<u32, i32>>()); // Ok, pointer to usize conversion
|
||||||
|
let _: &Ty2<u32, i32> = core::mem::transmute(value::<Ty<usize>>()); // Ok, pointer to usize conversion
|
||||||
|
|
||||||
|
let _: Ty<[u8; 8]> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Ok, transmute to byte array
|
||||||
|
let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty<[u8; 8]>>()); // Ok, transmute from byte array
|
||||||
|
}
|
||||||
|
}
|
44
tests/ui/transmute_undefined_repr.stderr
Normal file
44
tests/ui/transmute_undefined_repr.stderr
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
error: transmute from `Ty2<u32, i32>` which has an undefined layout
|
||||||
|
--> $DIR/transmute_undefined_repr.rs:23:33
|
||||||
|
|
|
||||||
|
LL | let _: Ty2C<u32, i32> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `-D clippy::transmute-undefined-repr` implied by `-D warnings`
|
||||||
|
|
||||||
|
error: transmute into `Ty2<u32, i32>` which has an undefined layout
|
||||||
|
--> $DIR/transmute_undefined_repr.rs:24:32
|
||||||
|
|
|
||||||
|
LL | let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: transmute from `Ty<Ty2<u32, i32>>` to `Ty2<u32, f32>`, both of which have an undefined layout
|
||||||
|
--> $DIR/transmute_undefined_repr.rs:29:32
|
||||||
|
|
|
||||||
|
LL | let _: Ty2<u32, f32> = core::mem::transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: two instances of the same generic type (`Ty2`) may have different layouts
|
||||||
|
|
||||||
|
error: transmute from `Ty2<u32, f32>` to `Ty<Ty2<u32, i32>>`, both of which have an undefined layout
|
||||||
|
--> $DIR/transmute_undefined_repr.rs:30:36
|
||||||
|
|
|
||||||
|
LL | let _: Ty<Ty2<u32, i32>> = core::mem::transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: two instances of the same generic type (`Ty2`) may have different layouts
|
||||||
|
|
||||||
|
error: transmute to `&Ty2<u32, f32>` which has an undefined layout
|
||||||
|
--> $DIR/transmute_undefined_repr.rs:35:33
|
||||||
|
|
|
||||||
|
LL | let _: &Ty2<u32, f32> = core::mem::transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: transmute from `&Ty2<u32, f32>` which has an undefined layout
|
||||||
|
--> $DIR/transmute_undefined_repr.rs:36:37
|
||||||
|
|
|
||||||
|
LL | let _: Ty<&Ty2<u32, i32>> = core::mem::transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 6 previous errors
|
||||||
|
|
Loading…
Reference in New Issue
Block a user