diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 079b7ff0675b..a4ef1344ab95 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::is_hir_ty_cfg_dependant; +use clippy_utils::ty::is_c_void; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, GenericArg}; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::sym; use super::CAST_PTR_ALIGNMENT; @@ -62,19 +62,3 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f } } } - -/// Check if the given type is either `core::ffi::c_void` or -/// one of the platform specific `libc::::c_void` of libc. -fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - if let ty::Adt(adt, _) = ty.kind() { - let names = cx.get_def_path(adt.did); - - if names.is_empty() { - return false; - } - if names[0] == sym::libc || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) { - return true; - } - } - false -} diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 89439862f691..b6cc1676b008 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -1,5 +1,6 @@ use super::TRANSMUTE_UNDEFINED_REPR; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::is_c_void; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::subst::{GenericArg, Subst}; @@ -18,33 +19,55 @@ pub(super) fn check<'tcx>( while 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 from_ty_orig.peel_refs() != unsized_ty { - diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty)); - } - }, - ); - return true; + ReducedTys::FromFatPtr { + unsized_ty, + to_ty: to_sub_ty, + } => match reduce_ty(cx, to_sub_ty) { + ReducedTy::IntArray | ReducedTy::TypeErasure => break, + ReducedTy::Ref(to_sub_ty) => { + from_ty = unsized_ty; + to_ty = to_sub_ty; + continue; + }, + _ => { + span_lint_and_then( + cx, + TRANSMUTE_UNDEFINED_REPR, + e.span, + &format!("transmute from `{}` which has an undefined layout", from_ty_orig), + |diag| { + if 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 to_ty_orig.peel_refs() != unsized_ty { - diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty)); - } - }, - ); - return true; + ReducedTys::ToFatPtr { + unsized_ty, + from_ty: from_sub_ty, + } => match reduce_ty(cx, from_sub_ty) { + ReducedTy::IntArray | ReducedTy::TypeErasure => break, + ReducedTy::Ref(from_sub_ty) => { + from_ty = from_sub_ty; + to_ty = unsized_ty; + continue; + }, + _ => { + span_lint_and_then( + cx, + TRANSMUTE_UNDEFINED_REPR, + e.span, + &format!("transmute to `{}` which has an undefined layout", to_ty_orig), + |diag| { + if 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, @@ -100,7 +123,8 @@ pub(super) fn check<'tcx>( 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::IntArray | ReducedTy::TypeErasure, _) + | (_, ReducedTy::IntArray | ReducedTy::TypeErasure) => return false, (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => { span_lint_and_then( cx, @@ -182,13 +206,14 @@ pub(super) fn check<'tcx>( } enum ReducedTys<'tcx> { - FromFatPtr { unsized_ty: Ty<'tcx> }, - ToFatPtr { unsized_ty: Ty<'tcx> }, + FromFatPtr { unsized_ty: Ty<'tcx>, to_ty: Ty<'tcx> }, + ToFatPtr { unsized_ty: Ty<'tcx>, from_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> }, } +/// Remove references so long as both types are references. fn reduce_refs<'tcx>( cx: &LateContext<'tcx>, span: Span, @@ -208,12 +233,12 @@ fn reduce_refs<'tcx>( (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 } + ReducedTys::FromFatPtr { unsized_ty, to_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 } + ReducedTys::ToFatPtr { unsized_ty, from_ty } }, (ty::Ref(_, from_ty, _) | ty::RawPtr(TypeAndMut { ty: from_ty, .. }), _) => { ReducedTys::FromPtr { from_ty, to_ty } @@ -227,13 +252,23 @@ fn reduce_refs<'tcx>( } enum ReducedTy<'tcx> { + /// The type can be used for type erasure. + TypeErasure, + /// The type is a struct containing either zero non-zero sized fields, or multiple non-zero + /// sized fields with a defined order. OrderedFields(Ty<'tcx>), + /// The type is a struct containing multiple non-zero sized fields with no defined order. UnorderedFields(Ty<'tcx>), + /// The type is a reference to the contained type. Ref(Ty<'tcx>), - Other(Ty<'tcx>), + /// The type is an array of a primitive integer type. These can be used as storage for a value + /// of another type. IntArray, + /// Any other type. + Other(Ty<'tcx>), } +/// Reduce structs containing a single non-zero sized field to it's contained type. 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); @@ -243,6 +278,7 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> ty = sub_ty; continue; }, + ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure, 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 { @@ -261,7 +297,7 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> .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); + return ReducedTy::TypeErasure; }; if iter.all(|ty| is_zero_sized_ty(cx, ty)) { ty = sized_ty; @@ -273,7 +309,12 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> ReducedTy::UnorderedFields(ty) } }, - ty::Ref(..) | ty::RawPtr(_) => ReducedTy::Ref(ty), + ty::Adt(def, _) if def.is_enum() && (def.variants.is_empty() || is_c_void(cx, ty)) => { + ReducedTy::TypeErasure + }, + ty::Foreign(_) => ReducedTy::TypeErasure, + ty::Ref(_, ty, _) => ReducedTy::Ref(ty), + ty::RawPtr(ty) => ReducedTy::Ref(ty.ty), _ => ReducedTy::Other(ty), }; } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 73d91550693d..5b37a1de8ed8 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1,6 +1,7 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(let_else)] +#![feature(let_chains)] #![feature(once_cell)] #![feature(rustc_private)] #![recursion_limit = "512"] diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 7d74b69906d9..6c9ba64525a7 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -572,3 +572,17 @@ pub fn get_discriminant_value(tcx: TyCtxt<'_>, adt: &'_ AdtDef, i: VariantIdx) - }, } } + +/// Check if the given type is either `core::ffi::c_void`, `std::os::raw::c_void`, or one of the +/// platform specific `libc::::c_void` types in libc. +pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + if let ty::Adt(adt, _) = ty.kind() + && let &[krate, .., name] = &*cx.get_def_path(adt.did) + && let sym::libc | sym::core | sym::std = krate + && name.as_str() == "c_void" + { + true + } else { + false + } +} diff --git a/tests/ui/transmute_undefined_repr.rs b/tests/ui/transmute_undefined_repr.rs index f0383a03fbde..b163d6056343 100644 --- a/tests/ui/transmute_undefined_repr.rs +++ b/tests/ui/transmute_undefined_repr.rs @@ -1,5 +1,8 @@ #![warn(clippy::transmute_undefined_repr)] -#![allow(clippy::unit_arg)] +#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref)] + +use core::ffi::c_void; +use core::mem::{size_of, transmute}; fn value() -> T { unimplemented!() @@ -14,35 +17,75 @@ struct Ty2C(T, U); fn main() { unsafe { - let _: () = core::mem::transmute(value::()); - let _: Empty = core::mem::transmute(value::<()>()); + let _: () = transmute(value::()); + let _: Empty = transmute(value::<()>()); - let _: Ty = core::mem::transmute(value::()); - let _: Ty = core::mem::transmute(value::()); + let _: Ty = transmute(value::()); + let _: Ty = transmute(value::()); - let _: Ty2C = core::mem::transmute(value::>()); // Lint, Ty2 is unordered - let _: Ty2 = core::mem::transmute(value::>()); // Lint, Ty2 is unordered + let _: Ty2C = transmute(value::>()); // Lint, Ty2 is unordered + let _: Ty2 = transmute(value::>()); // Lint, Ty2 is unordered - let _: Ty2 = core::mem::transmute(value::>>()); // Ok, Ty2 types are the same - let _: Ty> = core::mem::transmute(value::>()); // Ok, Ty2 types are the same + let _: Ty2 = transmute(value::>>()); // Ok, Ty2 types are the same + let _: Ty> = transmute(value::>()); // Ok, Ty2 types are the same - let _: Ty2 = core::mem::transmute(value::>>()); // Lint, different Ty2 instances - let _: Ty> = core::mem::transmute(value::>()); // Lint, different Ty2 instances + let _: Ty2 = transmute(value::>>()); // Lint, different Ty2 instances + let _: Ty> = transmute(value::>()); // Lint, different Ty2 instances - let _: Ty<&()> = core::mem::transmute(value::<&()>()); - let _: &() = core::mem::transmute(value::>()); + let _: Ty<&()> = transmute(value::<&()>()); + let _: &() = transmute(value::>()); - let _: &Ty2 = core::mem::transmute(value::>>()); // Lint, different Ty2 instances - let _: Ty<&Ty2> = core::mem::transmute(value::<&Ty2>()); // Lint, different Ty2 instances + let _: &Ty2 = transmute(value::>>()); // Lint, different Ty2 instances + let _: Ty<&Ty2> = transmute(value::<&Ty2>()); // Lint, different Ty2 instances - let _: Ty = core::mem::transmute(value::<&Ty2>()); // Ok, pointer to usize conversion - let _: &Ty2 = core::mem::transmute(value::>()); // Ok, pointer to usize conversion + let _: Ty = transmute(value::<&Ty2>()); // Ok, pointer to usize conversion + let _: &Ty2 = transmute(value::>()); // Ok, pointer to usize conversion - let _: Ty<[u8; 8]> = core::mem::transmute(value::>()); // Ok, transmute to byte array - let _: Ty2 = core::mem::transmute(value::>()); // Ok, transmute from byte array + let _: Ty<[u8; 8]> = transmute(value::>()); // Ok, transmute to byte array + let _: Ty2 = transmute(value::>()); // Ok, transmute from byte array // issue #8417 - let _: Ty2C, ()> = core::mem::transmute(value::>()); // Ok, Ty2 types are the same - let _: Ty2 = core::mem::transmute(value::, ()>>()); // Ok, Ty2 types are the same + let _: Ty2C, ()> = transmute(value::>()); // Ok, Ty2 types are the same + let _: Ty2 = transmute(value::, ()>>()); // Ok, Ty2 types are the same + + let _: &'static mut Ty2 = transmute(value::>>()); // Ok, Ty2 types are the same + let _: Box> = transmute(value::<&'static mut Ty2>()); // Ok, Ty2 types are the same + let _: *mut Ty2 = transmute(value::>>()); // Ok, Ty2 types are the same + let _: Box> = transmute(value::<*mut Ty2>()); // Ok, Ty2 types are the same + + let _: &'static mut Ty2 = transmute(value::>>()); // Lint, different Ty2 instances + let _: Box> = transmute(value::<&'static mut Ty2>()); // Lint, different Ty2 instances + + let _: *const () = transmute(value::>>()); // Ok, type erasure + let _: Ty<&Ty2> = transmute(value::<*const ()>()); // Ok, reverse type erasure + + let _: *const c_void = transmute(value::>>()); // Ok, type erasure + let _: Ty<&Ty2> = transmute(value::<*const c_void>()); // Ok, reverse type erasure + + enum Erase {} + let _: *const Erase = transmute(value::>>()); // Ok, type erasure + let _: Ty<&Ty2> = transmute(value::<*const Erase>()); // Ok, reverse type erasure + + struct Erase2( + [u8; 0], + core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, + ); + let _: *const Erase2 = transmute(value::>>()); // Ok, type erasure + let _: Ty<&Ty2> = transmute(value::<*const Erase2>()); // Ok, reverse type erasure + + let _: *const () = transmute(value::<&&[u8]>()); // Ok, type erasure + let _: &&[u8] = transmute(value::<*const ()>()); // Ok, reverse type erasure + + let _: *mut c_void = transmute(value::<&mut &[u8]>()); // Ok, type erasure + let _: &mut &[u8] = transmute(value::<*mut c_void>()); // Ok, reverse type erasure + + let _: [u8; size_of::<&[u8]>()] = transmute(value::<&[u8]>()); // Ok, transmute to byte array + let _: &[u8] = transmute(value::<[u8; size_of::<&[u8]>()]>()); // Ok, transmute from byte array + + let _: [usize; 2] = transmute(value::<&[u8]>()); // Ok, transmute to int array + let _: &[u8] = transmute(value::<[usize; 2]>()); // Ok, transmute from int array + + let _: *const [u8] = transmute(value::>()); // Ok + let _: Box<[u8]> = transmute(value::<*mut [u8]>()); // Ok } } diff --git a/tests/ui/transmute_undefined_repr.stderr b/tests/ui/transmute_undefined_repr.stderr index 040c63c7afa6..42d544fc954c 100644 --- a/tests/ui/transmute_undefined_repr.stderr +++ b/tests/ui/transmute_undefined_repr.stderr @@ -1,44 +1,64 @@ error: transmute from `Ty2` which has an undefined layout - --> $DIR/transmute_undefined_repr.rs:23:33 + --> $DIR/transmute_undefined_repr.rs:26:33 | -LL | let _: Ty2C = core::mem::transmute(value::>()); // Lint, Ty2 is unordered - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: Ty2C = transmute(value::>()); // Lint, Ty2 is unordered + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::transmute-undefined-repr` implied by `-D warnings` error: transmute into `Ty2` which has an undefined layout - --> $DIR/transmute_undefined_repr.rs:24:32 + --> $DIR/transmute_undefined_repr.rs:27:32 | -LL | let _: Ty2 = core::mem::transmute(value::>()); // Lint, Ty2 is unordered - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: Ty2 = transmute(value::>()); // Lint, Ty2 is unordered + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `Ty>` to `Ty2`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:29:32 + --> $DIR/transmute_undefined_repr.rs:32:32 | -LL | let _: Ty2 = core::mem::transmute(value::>>()); // Lint, different Ty2 instances - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: Ty2 = transmute(value::>>()); // Lint, different Ty2 instances + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `Ty2` to `Ty>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:30:36 + --> $DIR/transmute_undefined_repr.rs:33:36 | -LL | let _: Ty> = core::mem::transmute(value::>()); // Lint, different Ty2 instances - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: Ty> = transmute(value::>()); // Lint, different Ty2 instances + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: two instances of the same generic type (`Ty2`) may have different layouts -error: transmute to `&Ty2` which has an undefined layout - --> $DIR/transmute_undefined_repr.rs:35:33 +error: transmute from `Ty<&Ty2>` to `&Ty2`, both of which have an undefined layout + --> $DIR/transmute_undefined_repr.rs:38:33 | -LL | let _: &Ty2 = core::mem::transmute(value::>>()); // Lint, different Ty2 instances - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: &Ty2 = transmute(value::>>()); // Lint, different Ty2 instances + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: two instances of the same generic type (`Ty2`) may have different layouts + +error: transmute from `&Ty2` to `Ty<&Ty2>`, both of which have an undefined layout + --> $DIR/transmute_undefined_repr.rs:39:37 + | +LL | let _: Ty<&Ty2> = transmute(value::<&Ty2>()); // Lint, different Ty2 instances + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: two instances of the same generic type (`Ty2`) may have different layouts -error: transmute from `&Ty2` which has an undefined layout - --> $DIR/transmute_undefined_repr.rs:36:37 +error: transmute from `std::boxed::Box>` to `&mut Ty2`, both of which have an undefined layout + --> $DIR/transmute_undefined_repr.rs:56:45 | -LL | let _: Ty<&Ty2> = core::mem::transmute(value::<&Ty2>()); // Lint, different Ty2 instances - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _: &'static mut Ty2 = transmute(value::>>()); // Lint, different Ty2 instances + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: two instances of the same generic type (`Ty2`) may have different layouts + +error: transmute from `&mut Ty2` to `std::boxed::Box>`, both of which have an undefined layout + --> $DIR/transmute_undefined_repr.rs:57:37 + | +LL | let _: Box> = transmute(value::<&'static mut Ty2>()); // Lint, different Ty2 instances + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: two instances of the same generic type (`Ty2`) may have different layouts -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors