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