diff --git a/src/byteorder.rs b/src/byteorder.rs index a189b4a4fc..ca4faff105 100644 --- a/src/byteorder.rs +++ b/src/byteorder.rs @@ -511,8 +511,13 @@ mod tests { macro_rules! impl_traits { ($name:ident, $native:ident, $bytes:expr, $sign:ident) => { impl Native for $native { + // For some types, `0 as _` is required (for example, when + // `$native` is a floating-point type; `0` is an integer), but + // for other types, it's a trivial cast. In all cases, Clippy + // thinks it's dangerous. + #[allow(trivial_numeric_casts, clippy::as_conversions)] const ZERO: $native = 0 as _; - const MAX_VALUE: $native = ::core::$native::MAX; + const MAX_VALUE: $native = $native::MAX; fn rand() -> $native { rand::random() @@ -586,9 +591,9 @@ mod tests { } #[cfg(target_endian = "big")] - type NonNativeEndian = byteorder::LittleEndian; + type NonNativeEndian = LittleEndian; #[cfg(target_endian = "little")] - type NonNativeEndian = byteorder::BigEndian; + type NonNativeEndian = BigEndian; #[test] fn test_zero() { diff --git a/src/lib.rs b/src/lib.rs index 7fb32c409e..1c9f696c43 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,10 +46,65 @@ //! //! [simd-layout]: https://rust-lang.github.io/unsafe-code-guidelines/layout/packed-simd-vectors.html -#![deny(missing_docs, clippy::indexing_slicing)] +#![deny( + anonymous_parameters, + clippy::all, + clippy::alloc_instead_of_core, + clippy::arithmetic_side_effects, + clippy::as_underscore, + clippy::assertions_on_result_states, + clippy::as_conversions, + clippy::correctness, + clippy::dbg_macro, + clippy::decimal_literal_representation, + clippy::get_unwrap, + clippy::indexing_slicing, + clippy::obfuscated_if_else, + clippy::perf, + clippy::print_stdout, + clippy::std_instead_of_core, + clippy::style, + clippy::suspicious, + clippy::todo, + clippy::unimplemented, + clippy::unnested_or_patterns, + clippy::unwrap_used, + clippy::use_debug, + const_err, + deprecated_in_future, + illegal_floating_point_literal_pattern, + late_bound_lifetime_arguments, + missing_copy_implementations, + missing_debug_implementations, + missing_docs, + path_statements, + patterns_in_fns_without_body, + rust_2018_idioms, + rustdoc::broken_intra_doc_links, + rustdoc::private_intra_doc_links, + trivial_numeric_casts, + unreachable_pub, + unsafe_op_in_unsafe_fn, + unused_extern_crates, + unused_qualifications, + variant_size_differences +)] +// In test code, it makes sense to weight more heavily towards concise, readable +// code over correct or debuggable code. +#![cfg_attr(test, allow( + // In tests, you get line numbers and have access to source code, so panic + // messages are less important. You also often unwrap a lot, which would + // make expect'ing instead very verbose. + clippy::unwrap_used, + // In tests, there's no harm to "panic risks" - the worst that can happen is + // that your test will fail, and you'll fix it. By contrast, panic risks in + // production code introduce the possibly of code panicking unexpectedly "in + // the field". + clippy::arithmetic_side_effects, + clippy::indexing_slicing, +))] #![cfg_attr(not(test), no_std)] #![cfg_attr(feature = "simd-nightly", feature(stdsimd))] -#![recursion_limit = "2048"] pub mod byteorder; @@ -82,7 +137,7 @@ use { // work in this crate. They assume that zerocopy is linked as an extern crate, // so they access items from it as `zerocopy::Xxx`. This makes that still work. mod zerocopy { - pub use crate::*; + pub(crate) use crate::*; } // Implements an unsafe trait for a range of container types. @@ -451,7 +506,8 @@ pub unsafe trait AsBytes { // Note that this method does not have a `Self: Sized` bound; // `size_of_val` works for unsized values too. let len = mem::size_of_val(self); - slice::from_raw_parts(self as *const Self as *const u8, len) + let slf: *const Self = self; + slice::from_raw_parts(slf.cast::(), len) } } @@ -467,7 +523,8 @@ pub unsafe trait AsBytes { // Note that this method does not have a `Self: Sized` bound; // `size_of_val` works for unsized values too. let len = mem::size_of_val(self); - slice::from_raw_parts_mut(self as *mut Self as *mut u8, len) + let slf: *mut Self = self; + slice::from_raw_parts_mut(slf.cast::(), len) } } @@ -715,6 +772,7 @@ mod simd { /// /// [`get`]: Unalign::get /// [`into_inner`]: Unalign::into_inner +#[allow(missing_debug_implementations)] #[derive(FromBytes, Unaligned, Copy)] #[repr(C, packed)] pub struct Unalign(T); @@ -910,7 +968,7 @@ where /// either of these checks fail, it returns `None`. #[inline] pub fn new(bytes: B) -> Option> { - if bytes.len() != mem::size_of::() || !aligned_to(bytes.deref(), mem::align_of::()) { + if bytes.len() != mem::size_of::() || !aligned_to::(bytes.deref()) { return None; } Some(LayoutVerified(bytes, PhantomData)) @@ -925,7 +983,7 @@ where /// alignment checks fail, it returns `None`. #[inline] pub fn new_from_prefix(bytes: B) -> Option<(LayoutVerified, B)> { - if bytes.len() < mem::size_of::() || !aligned_to(bytes.deref(), mem::align_of::()) { + if bytes.len() < mem::size_of::() || !aligned_to::(bytes.deref()) { return None; } let (bytes, suffix) = bytes.split_at(mem::size_of::()); @@ -943,11 +1001,9 @@ where #[inline] pub fn new_from_suffix(bytes: B) -> Option<(B, LayoutVerified)> { let bytes_len = bytes.len(); - if bytes_len < mem::size_of::() { - return None; - } - let (prefix, bytes) = bytes.split_at(bytes_len - mem::size_of::()); - if !aligned_to(bytes.deref(), mem::align_of::()) { + let split_at = bytes_len.checked_sub(mem::size_of::())?; + let (prefix, bytes) = bytes.split_at(split_at); + if !aligned_to::(bytes.deref()) { return None; } Some((prefix, LayoutVerified(bytes, PhantomData))) @@ -970,10 +1026,11 @@ where /// `new_slice` panics if `T` is a zero-sized type. #[inline] pub fn new_slice(bytes: B) -> Option> { - assert_ne!(mem::size_of::(), 0); - if bytes.len() % mem::size_of::() != 0 - || !aligned_to(bytes.deref(), mem::align_of::()) - { + let remainder = bytes + .len() + .checked_rem(mem::size_of::()) + .expect("LayoutVerified::new_slice called on a zero-sized type"); + if remainder != 0 || !aligned_to::(bytes.deref()) { return None; } Some(LayoutVerified(bytes, PhantomData)) @@ -1236,10 +1293,8 @@ where #[inline] pub fn new_unaligned_from_suffix(bytes: B) -> Option<(B, LayoutVerified)> { let bytes_len = bytes.len(); - if bytes_len < mem::size_of::() { - return None; - } - let (prefix, bytes) = bytes.split_at(bytes_len - mem::size_of::()); + let split_at = bytes_len.checked_sub(mem::size_of::())?; + let (prefix, bytes) = bytes.split_at(split_at); Some((prefix, LayoutVerified(bytes, PhantomData))) } } @@ -1261,8 +1316,11 @@ where /// `new_slice` panics if `T` is a zero-sized type. #[inline] pub fn new_slice_unaligned(bytes: B) -> Option> { - assert_ne!(mem::size_of::(), 0); - if bytes.len() % mem::size_of::() != 0 { + let remainder = bytes + .len() + .checked_rem(mem::size_of::()) + .expect("LayoutVerified::new_slice_unaligned called on a zero-sized type"); + if remainder != 0 { return None; } Some(LayoutVerified(bytes, PhantomData)) @@ -1555,7 +1613,7 @@ where /// and no mutable references to the same memory may be constructed during /// `'a`. unsafe fn deref_helper<'a>(&self) -> &'a T { - &*(self.0.as_ptr() as *const T) + unsafe { &*self.0.as_ptr().cast::() } } } @@ -1576,7 +1634,7 @@ where /// and no other references - mutable or immutable - to the same memory may /// be constructed during `'a`. unsafe fn deref_mut_helper<'a>(&mut self) -> &'a mut T { - &mut *(self.0.as_mut_ptr() as *mut T) + unsafe { &mut *self.0.as_mut_ptr().cast::() } } } @@ -1594,9 +1652,14 @@ where let len = self.0.len(); let elem_size = mem::size_of::(); debug_assert_ne!(elem_size, 0); - debug_assert_eq!(len % elem_size, 0); - let elems = len / elem_size; - slice::from_raw_parts(self.0.as_ptr() as *const T, elems) + // `LayoutVerified<_, [T]>` maintains the invariant that `size_of::() + // > 0`. Thus, neither the mod nor division operations here can panic. + #[allow(clippy::arithmetic_side_effects)] + let elems = { + debug_assert_eq!(len % elem_size, 0); + len / elem_size + }; + unsafe { slice::from_raw_parts(self.0.as_ptr().cast::(), elems) } } } @@ -1615,15 +1678,30 @@ where let len = self.0.len(); let elem_size = mem::size_of::(); debug_assert_ne!(elem_size, 0); - debug_assert_eq!(len % elem_size, 0); - let elems = len / elem_size; - slice::from_raw_parts_mut(self.0.as_mut_ptr() as *mut T, elems) + // `LayoutVerified<_, [T]>` maintains the invariant that `size_of::() + // > 0`. Thus, neither the mod nor division operations here can panic. + #[allow(clippy::arithmetic_side_effects)] + let elems = { + debug_assert_eq!(len % elem_size, 0); + len / elem_size + }; + unsafe { slice::from_raw_parts_mut(self.0.as_mut_ptr().cast::(), elems) } } } -#[inline] -fn aligned_to(bytes: &[u8], align: usize) -> bool { - (bytes as *const _ as *const () as usize) % align == 0 +/// Are `bytes` aligned to `mem::align_of::()`? +#[inline(always)] +fn aligned_to(bytes: &[u8]) -> bool { + // TODO(https://github.com/rust-lang/rust/issues/91126): Use `.to_bits()` + // instead of `as usize` once it's stable, and get rid of this `allow`. + // Currently, `as usize` is the only way to accomplish this. + #[allow(clippy::as_conversions)] + let addr = bytes.as_ptr().cast::<()>() as usize; + // `mem::align_of::()` is guaranteed to return a non-zero value, which in + // turn guarantees that this mod operation will not panic. + #[allow(clippy::arithmetic_side_effects)] + let remainder = addr % mem::align_of::(); + remainder == 0 } impl LayoutVerified @@ -1662,7 +1740,7 @@ where // `self.0` is at least `size_of::()` bytes long, and that it is at // least as aligned as `align_of::()`. Because `T: FromBytes`, it is // sound to interpret these bytes as a `T`. - unsafe { ptr::read(self.0.as_ptr() as *const T) } + unsafe { ptr::read(self.0.as_ptr().cast::()) } } } @@ -1679,7 +1757,7 @@ where // least as aligned as `align_of::()`. Writing `t` to the buffer will // allow all of the bytes of `t` to be accessed as a `[u8]`, but because // `T: AsBytes`, we know this is sound. - unsafe { ptr::write(self.0.as_mut_ptr() as *mut T, t) } + unsafe { ptr::write(self.0.as_mut_ptr().cast::(), t) } } } @@ -2018,8 +2096,10 @@ mod alloc_support { unsafe { // This is a potentially overlapping copy. let ptr = v.as_mut_ptr(); + #[allow(clippy::arithmetic_side_effects)] ptr.add(position).copy_to(ptr.add(position + additional), v.len() - position); ptr.add(position).write_bytes(0, additional); + #[allow(clippy::arithmetic_side_effects)] v.set_len(v.len() + additional); } } @@ -2266,7 +2346,8 @@ mod tests { // Converts a `u64` to bytes using this platform's endianness. fn u64_to_bytes(u: u64) -> [u8; 8] { - unsafe { ptr::read(&u as *const u64 as *const [u8; 8]) } + let u: *const u64 = &u; + unsafe { ptr::read(u.cast::<[u8; 8]>()) } } #[test] @@ -2337,7 +2418,7 @@ mod tests { let buf = [0]; let lv = LayoutVerified::<_, u8>::new(&buf[..]).unwrap(); let buf_ptr = buf.as_ptr(); - let deref_ptr = lv.deref() as *const u8; + let deref_ptr: *const u8 = lv.deref(); assert_eq!(buf_ptr, deref_ptr); let buf = [0]; @@ -2351,7 +2432,7 @@ mod tests { // between the typed and untyped representations, that reads via `deref` and // `read` behave the same, and that writes via `deref_mut` and `write` // behave the same. - fn test_new_helper<'a>(mut lv: LayoutVerified<&'a mut [u8], u64>) { + fn test_new_helper(mut lv: LayoutVerified<&mut [u8], u64>) { // assert that the value starts at 0 assert_eq!(*lv, 0); assert_eq!(lv.read(), 0); @@ -2376,14 +2457,14 @@ mod tests { // Verify that values written to a `LayoutVerified` are properly shared // between the typed and untyped representations; pass a value with // `typed_len` `u64`s backed by an array of `typed_len * 8` bytes. - fn test_new_helper_slice<'a>(mut lv: LayoutVerified<&'a mut [u8], [u64]>, typed_len: usize) { + fn test_new_helper_slice(mut lv: LayoutVerified<&mut [u8], [u64]>, typed_len: usize) { // Assert that the value starts out zeroed. assert_eq!(&*lv, vec![0; typed_len].as_slice()); // Check the backing storage is the exact same slice. let untyped_len = typed_len * 8; assert_eq!(lv.bytes().len(), untyped_len); - assert_eq!(lv.bytes().as_ptr(), lv.as_ptr() as *const u8); + assert_eq!(lv.bytes().as_ptr(), lv.as_ptr().cast::()); // Assert that values written to the typed value are reflected in the // byte slice. @@ -2404,7 +2485,7 @@ mod tests { // between the typed and untyped representations, that reads via `deref` and // `read` behave the same, and that writes via `deref_mut` and `write` // behave the same. - fn test_new_helper_unaligned<'a>(mut lv: LayoutVerified<&'a mut [u8], [u8; 8]>) { + fn test_new_helper_unaligned(mut lv: LayoutVerified<&mut [u8], [u8; 8]>) { // assert that the value starts at 0 assert_eq!(*lv, [0; 8]); assert_eq!(lv.read(), [0; 8]); @@ -2429,7 +2510,7 @@ mod tests { // Verify that values written to a `LayoutVerified` are properly shared // between the typed and untyped representations; pass a value with `len` // `u8`s backed by an array of `len` bytes. - fn test_new_helper_slice_unaligned<'a>(mut lv: LayoutVerified<&'a mut [u8], [u8]>, len: usize) { + fn test_new_helper_slice_unaligned(mut lv: LayoutVerified<&mut [u8], [u8]>, len: usize) { // Assert that the value starts out zeroed. assert_eq!(&*lv, vec![0u8; len].as_slice()); @@ -2819,7 +2900,7 @@ mod tests { // Fail due to arithmetic overflow. let mut buf = AlignedBuffer::::default(); - let unreasonable_len = std::usize::MAX / mem::size_of::() + 1; + let unreasonable_len = usize::MAX / mem::size_of::() + 1; assert!(LayoutVerified::<_, [u64]>::new_slice_from_prefix(&buf.buf[..], unreasonable_len) .is_none()); assert!(LayoutVerified::<_, [u64]>::new_slice_from_prefix_zeroed( @@ -2863,9 +2944,9 @@ mod tests { // of the function under test for the name of the test itself. mod test_zst_panics { macro_rules! zst_test { - ($name:ident($($tt:tt)*)) => { + ($name:ident($($tt:tt)*), $constructor_in_panic_msg:tt) => { #[test] - #[should_panic = "assertion failed"] + #[should_panic = concat!("LayoutVerified::", $constructor_in_panic_msg, " called on a zero-sized type")] fn $name() { let mut buffer = [0u8]; let lv = $crate::LayoutVerified::<_, [()]>::$name(&mut buffer[..], $($tt)*); @@ -2873,18 +2954,18 @@ mod tests { } } } - zst_test!(new_slice()); - zst_test!(new_slice_zeroed()); - zst_test!(new_slice_from_prefix(1)); - zst_test!(new_slice_from_prefix_zeroed(1)); - zst_test!(new_slice_from_suffix(1)); - zst_test!(new_slice_from_suffix_zeroed(1)); - zst_test!(new_slice_unaligned()); - zst_test!(new_slice_unaligned_zeroed()); - zst_test!(new_slice_unaligned_from_prefix(1)); - zst_test!(new_slice_unaligned_from_prefix_zeroed(1)); - zst_test!(new_slice_unaligned_from_suffix(1)); - zst_test!(new_slice_unaligned_from_suffix_zeroed(1)); + zst_test!(new_slice(), "new_slice"); + zst_test!(new_slice_zeroed(), "new_slice"); + zst_test!(new_slice_from_prefix(1), "new_slice"); + zst_test!(new_slice_from_prefix_zeroed(1), "new_slice"); + zst_test!(new_slice_from_suffix(1), "new_slice"); + zst_test!(new_slice_from_suffix_zeroed(1), "new_slice"); + zst_test!(new_slice_unaligned(), "new_slice_unaligned"); + zst_test!(new_slice_unaligned_zeroed(), "new_slice_unaligned"); + zst_test!(new_slice_unaligned_from_prefix(1), "new_slice_unaligned"); + zst_test!(new_slice_unaligned_from_prefix_zeroed(1), "new_slice_unaligned"); + zst_test!(new_slice_unaligned_from_suffix(1), "new_slice_unaligned"); + zst_test!(new_slice_unaligned_from_suffix_zeroed(1), "new_slice_unaligned"); } #[test]