1 // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT 2 3 // Copyright 2019 The Fuchsia Authors 4 // 5 // Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 6 // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT 7 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. 8 // This file may not be copied, modified, or distributed except according to 9 // those terms. 10 11 //! Derive macros for [zerocopy]'s traits. 12 //! 13 //! [zerocopy]: https://docs.rs/zerocopy 14 15 // Sometimes we want to use lints which were added after our MSRV. 16 // `unknown_lints` is `warn` by default and we deny warnings in CI, so without 17 // this attribute, any unknown lint would cause a CI failure when testing with 18 // our MSRV. 19 #![allow(unknown_lints)] 20 #![deny(renamed_and_removed_lints)] 21 #![deny( 22 clippy::all, 23 clippy::missing_safety_doc, 24 clippy::multiple_unsafe_ops_per_block, 25 clippy::undocumented_unsafe_blocks 26 )] 27 // We defer to own discretion on type complexity. 28 #![allow(clippy::type_complexity)] 29 // Inlining format args isn't supported on our MSRV. 30 #![allow(clippy::uninlined_format_args)] 31 #![deny( 32 rustdoc::bare_urls, 33 rustdoc::broken_intra_doc_links, 34 rustdoc::invalid_codeblock_attributes, 35 rustdoc::invalid_html_tags, 36 rustdoc::invalid_rust_codeblocks, 37 rustdoc::missing_crate_level_docs, 38 rustdoc::private_intra_doc_links 39 )] 40 #![recursion_limit = "128"] 41 42 macro_rules! ident { 43 (($fmt:literal $(, $arg:expr)*), $span:expr) => { 44 syn::Ident::new(&format!($fmt $(, crate::util::to_ident_str($arg))*), $span) 45 }; 46 } 47 48 mod derive; 49 #[cfg(test)] 50 mod output_tests; 51 mod repr; 52 mod util; 53 54 use syn::{DeriveInput, Error}; 55 56 use crate::util::*; 57 58 // FIXME(https://github.com/rust-lang/rust/issues/54140): Some errors could be 59 // made better if we could add multiple lines of error output like this: 60 // 61 // error: unsupported representation 62 // --> enum.rs:28:8 63 // | 64 // 28 | #[repr(transparent)] 65 // | 66 // help: required by the derive of FromBytes 67 // 68 // Instead, we have more verbose error messages like "unsupported representation 69 // for deriving FromZeros, FromBytes, IntoBytes, or Unaligned on an enum" 70 // 71 // This will probably require Span::error 72 // (https://doc.rust-lang.org/nightly/proc_macro/struct.Span.html#method.error), 73 // which is currently unstable. Revisit this once it's stable. 74 75 /// Defines a derive function named `$outer` which parses its input 76 /// `TokenStream` as a `DeriveInput` and then invokes the `$inner` function. 77 /// 78 /// Note that the separate `$outer` parameter is required - proc macro functions 79 /// are currently required to live at the crate root, and so the caller must 80 /// specify the name in order to avoid name collisions. 81 macro_rules! derive { 82 ($trait:ident => $outer:ident => $inner:path) => { 83 #[proc_macro_derive($trait, attributes(zerocopy))] 84 pub fn $outer(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { 85 let ast = syn::parse_macro_input!(ts as DeriveInput); 86 let ctx = match Ctx::try_from_derive_input(ast) { 87 Ok(ctx) => ctx, 88 Err(e) => return e.into_compile_error().into(), 89 }; 90 let ts = $inner(&ctx, Trait::$trait).into_ts(); 91 // We wrap in `const_block` as a backstop in case any derive fails 92 // to wrap its output in `const_block` (and thus fails to annotate) 93 // with the full set of `#[allow(...)]` attributes). 94 let ts = const_block([Some(ts)]); 95 #[cfg(test)] 96 crate::util::testutil::check_hygiene(ts.clone()); 97 ts.into() 98 } 99 }; 100 } 101 102 trait IntoTokenStream { 103 fn into_ts(self) -> proc_macro2::TokenStream; 104 } 105 106 impl IntoTokenStream for proc_macro2::TokenStream { 107 fn into_ts(self) -> proc_macro2::TokenStream { 108 self 109 } 110 } 111 112 impl IntoTokenStream for Result<proc_macro2::TokenStream, Error> { 113 fn into_ts(self) -> proc_macro2::TokenStream { 114 match self { 115 Ok(ts) => ts, 116 Err(err) => err.to_compile_error(), 117 } 118 } 119 } 120 121 derive!(KnownLayout => derive_known_layout => crate::derive::known_layout::derive); 122 derive!(Immutable => derive_immutable => crate::derive::derive_immutable); 123 derive!(TryFromBytes => derive_try_from_bytes => crate::derive::try_from_bytes::derive_try_from_bytes); 124 derive!(FromZeros => derive_from_zeros => crate::derive::from_bytes::derive_from_zeros); 125 derive!(FromBytes => derive_from_bytes => crate::derive::from_bytes::derive_from_bytes); 126 derive!(IntoBytes => derive_into_bytes => crate::derive::into_bytes::derive_into_bytes); 127 derive!(Unaligned => derive_unaligned => crate::derive::unaligned::derive_unaligned); 128 derive!(ByteHash => derive_hash => crate::derive::derive_hash); 129 derive!(ByteEq => derive_eq => crate::derive::derive_eq); 130 derive!(SplitAt => derive_split_at => crate::derive::derive_split_at); 131 132 /// Deprecated: prefer [`FromZeros`] instead. 133 #[deprecated(since = "0.8.0", note = "`FromZeroes` was renamed to `FromZeros`")] 134 #[doc(hidden)] 135 #[proc_macro_derive(FromZeroes)] 136 pub fn derive_from_zeroes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { 137 derive_from_zeros(ts) 138 } 139 140 /// Deprecated: prefer [`IntoBytes`] instead. 141 #[deprecated(since = "0.8.0", note = "`AsBytes` was renamed to `IntoBytes`")] 142 #[doc(hidden)] 143 #[proc_macro_derive(AsBytes)] 144 pub fn derive_as_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { 145 derive_into_bytes(ts) 146 } 147