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