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