xref: /linux/rust/zerocopy-derive/lib.rs (revision b437b3832874d4df88195d31b9052417674ffaed)
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