diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 7d14ba7fcf13..76f4ae00e93f 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -1,9 +1,10 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::ty::is_c_void; use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, sym}; +use rustc_abi::Align; use rustc_hir::{Expr, ExprKind, GenericArg}; use rustc_lint::LateContext; -use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::layout::{LayoutError, LayoutOf}; use rustc_middle::ty::{self, Ty}; use super::CAST_PTR_ALIGNMENT; @@ -29,24 +30,50 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f if let ty::RawPtr(from_ptr_ty, _) = *cast_from.kind() && let ty::RawPtr(to_ptr_ty, _) = *cast_to.kind() && let Ok(from_layout) = cx.layout_of(from_ptr_ty) - && let Ok(to_layout) = cx.layout_of(to_ptr_ty) - && from_layout.align.abi < to_layout.align.abi // with c_void, we inherently need to trust the user && !is_c_void(cx, from_ptr_ty) // when casting from a ZST, we don't know enough to properly lint && !from_layout.is_zst() && !is_used_as_unaligned(cx, expr) { - span_lint( - cx, - CAST_PTR_ALIGNMENT, - expr.span, - format!( - "casting from `{cast_from}` to a more-strictly-aligned pointer (`{cast_to}`) ({} < {} bytes)", - from_layout.align.bytes(), - to_layout.align.bytes(), - ), - ); + // When the to-type has a lower alignment, it's always properly aligned when cast. Only when the + // to-type has a higher alignment can it not be properly aligned (16 -> 32, 48 is not a multiple of + // 32!) + match cx.layout_of(to_ptr_ty) { + Ok(to_layout) => { + if from_layout.align.abi < to_layout.align.abi { + span_lint( + cx, + CAST_PTR_ALIGNMENT, + expr.span, + format!( + "casting from `{cast_from}` to a more-strictly-aligned pointer (`{cast_to}`) ({} < {} bytes)", + from_layout.align.bytes(), + to_layout.align.bytes(), + ), + ); + } + }, + Err(LayoutError::TooGeneric(too_generic_ty)) => { + // With the maximum possible alignment, there is no "higher alignment" case. + if from_layout.align.abi != Align::MAX { + span_lint_and_then( + cx, + CAST_PTR_ALIGNMENT, + expr.span, + format!("casting from `{cast_from}` to a possibly more-strictly-aligned pointer (`{cast_to}`)"), + |diag| { + if too_generic_ty == to_ptr_ty { + diag.note(format!("the alignment of `{too_generic_ty}` can vary")); + } else { + diag.note(format!("the alignment of the target pointer isn't known because the alignment of `{too_generic_ty}` can vary")); + } + }, + ); + } + }, + _ => {}, + } } } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 47cc1da0a6e9..830eca372811 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -222,16 +222,18 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for casts, using `as` or `pointer::cast`, from a - /// less strictly aligned pointer to a more strictly aligned pointer. + /// less strictly aligned pointer to a (possibly) more strictly aligned pointer. /// /// ### Why is this bad? /// Dereferencing the resulting pointer may be undefined behavior. /// /// ### Known problems - /// Using [`std::ptr::read_unaligned`](https://doc.rust-lang.org/std/ptr/fn.read_unaligned.html) and [`std::ptr::write_unaligned`](https://doc.rust-lang.org/std/ptr/fn.write_unaligned.html) or - /// similar on the resulting pointer is fine. Is over-zealous: casts with - /// manual alignment checks or casts like `u64` -> `u8` -> `u16` can be - /// fine. Miri is able to do a more in-depth analysis. + /// Using [`std::ptr::read_unaligned`](https://doc.rust-lang.org/std/ptr/fn.read_unaligned.html) + /// and [`std::ptr::write_unaligned`](https://doc.rust-lang.org/std/ptr/fn.write_unaligned.html) + /// or similar on the resulting pointer is fine. + /// + /// Is over-zealous: casts with manual alignment checks or casts like `u64` -> `u8` -> `u16` can be fine. + /// Miri is able to do a more in-depth analysis. /// /// ### Example /// ```no_run diff --git a/tests/ui/cast_alignment.rs b/tests/ui/cast_ptr_alignment.rs similarity index 59% rename from tests/ui/cast_alignment.rs rename to tests/ui/cast_ptr_alignment.rs index ef667f5598a6..1f0362f81717 100644 --- a/tests/ui/cast_alignment.rs +++ b/tests/ui/cast_ptr_alignment.rs @@ -1,13 +1,7 @@ -//! Test casts for alignment issues - +//@require-annotations-for-level: ERROR #![feature(core_intrinsics)] #![warn(clippy::cast_ptr_alignment)] -#![allow( - clippy::no_effect, - clippy::unnecessary_operation, - clippy::cast_lossless, - clippy::borrow_as_ptr -)] +#![expect(clippy::no_effect, clippy::cast_lossless, clippy::borrow_as_ptr)] fn main() { /* These should be warned against */ @@ -33,13 +27,20 @@ fn main() { // cast to less-strictly-aligned type (&1u16 as *const u16) as *const u8; (&mut 1u16 as *mut u16) as *mut u8; - // For c_void, we should trust the user. See #2677 +} + +// For c_void, we should trust the user +fn issue_2677() { (&1u32 as *const u32 as *const std::os::raw::c_void) as *const u32; (&1u32 as *const u32 as *const libc::c_void) as *const u32; - // For ZST, we should trust the user. See #4256 +} + +// For ZST, we should trust the user +fn issue_4256() { (&1u32 as *const u32 as *const ()) as *const u32; +} - // Issue #2881 +fn issue_2881() { let mut data = [0u8, 0u8]; unsafe { let ptr = &data as *const [u8; 2] as *const u8; @@ -52,3 +53,21 @@ fn main() { core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0); } } + +fn issue_3440() { + #[rustfmt::skip] // the error message comment gets split in 2 lines... + trait Trait { + unsafe fn frob(bytes: *const u8) -> *const Self + where + Self: Sized, + { + let _ = bytes as *const [Self; 2]; + //~^ ERROR: casting from `*const u8` to a possibly more-strictly-aligned pointer (`*const [Self; 2]`) + //~| NOTE: the alignment of the target pointer isn't known because the alignment of `Self` can vary + + bytes as *const Self + //~^ ERROR: casting from `*const u8` to a possibly more-strictly-aligned pointer (`*const Self`) + //~| NOTE: the alignment of `Self` can vary + } + } +} diff --git a/tests/ui/cast_alignment.stderr b/tests/ui/cast_ptr_alignment.stderr similarity index 52% rename from tests/ui/cast_alignment.stderr rename to tests/ui/cast_ptr_alignment.stderr index ee4c3e9a77ef..80af38721f08 100644 --- a/tests/ui/cast_alignment.stderr +++ b/tests/ui/cast_ptr_alignment.stderr @@ -1,5 +1,5 @@ error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) - --> tests/ui/cast_alignment.rs:16:5 + --> tests/ui/cast_ptr_alignment.rs:10:5 | LL | (&1u8 as *const u8) as *const u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,22 +8,38 @@ LL | (&1u8 as *const u8) as *const u16; = help: to override `-D warnings` add `#[allow(clippy::cast_ptr_alignment)]` error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) - --> tests/ui/cast_alignment.rs:19:5 + --> tests/ui/cast_ptr_alignment.rs:13:5 | LL | (&mut 1u8 as *mut u8) as *mut u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) - --> tests/ui/cast_alignment.rs:23:5 + --> tests/ui/cast_ptr_alignment.rs:17:5 | LL | (&1u8 as *const u8).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) - --> tests/ui/cast_alignment.rs:26:5 + --> tests/ui/cast_ptr_alignment.rs:20:5 | LL | (&mut 1u8 as *mut u8).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: casting from `*const u8` to a possibly more-strictly-aligned pointer (`*const [Self; 2]`) + --> tests/ui/cast_ptr_alignment.rs:64:21 + | +LL | let _ = bytes as *const [Self; 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the alignment of the target pointer isn't known because the alignment of `Self` can vary + +error: casting from `*const u8` to a possibly more-strictly-aligned pointer (`*const Self`) + --> tests/ui/cast_ptr_alignment.rs:68:13 + | +LL | bytes as *const Self + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: the alignment of `Self` can vary + +error: aborting due to 6 previous errors