Skip to main content

zerocopy_derive/
lib.rs

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
42macro_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
48mod derive;
49#[cfg(test)]
50mod output_tests;
51mod repr;
52mod util;
53
54use syn::{DeriveInput, Error};
55
56use 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.
81macro_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
102trait IntoTokenStream {
103    fn into_ts(self) -> proc_macro2::TokenStream;
104}
105
106impl IntoTokenStream for proc_macro2::TokenStream {
107    fn into_ts(self) -> proc_macro2::TokenStream {
108        self
109    }
110}
111
112impl 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
121derive!(KnownLayout => derive_known_layout => crate::derive::known_layout::derive);
122derive!(Immutable => derive_immutable => crate::derive::derive_immutable);
123derive!(TryFromBytes => derive_try_from_bytes => crate::derive::try_from_bytes::derive_try_from_bytes);
124derive!(FromZeros => derive_from_zeros => crate::derive::from_bytes::derive_from_zeros);
125derive!(FromBytes => derive_from_bytes => crate::derive::from_bytes::derive_from_bytes);
126derive!(IntoBytes => derive_into_bytes => crate::derive::into_bytes::derive_into_bytes);
127derive!(Unaligned => derive_unaligned => crate::derive::unaligned::derive_unaligned);
128derive!(ByteHash => derive_hash => crate::derive::derive_hash);
129derive!(ByteEq => derive_eq => crate::derive::derive_eq);
130derive!(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)]
136pub 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)]
144pub fn derive_as_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
145    derive_into_bytes(ts)
146}