| // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT |
| |
| // Copyright 2019 The Fuchsia Authors |
| // |
| // Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 |
| // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT |
| // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. |
| // This file may not be copied, modified, or distributed except according to |
| // those terms. |
| |
| //! Derive macros for [zerocopy]'s traits. |
| //! |
| //! [zerocopy]: https://docs.rs/zerocopy |
| |
| // Sometimes we want to use lints which were added after our MSRV. |
| // `unknown_lints` is `warn` by default and we deny warnings in CI, so without |
| // this attribute, any unknown lint would cause a CI failure when testing with |
| // our MSRV. |
| #![allow(unknown_lints)] |
| #![deny(renamed_and_removed_lints)] |
| #![deny( |
| clippy::all, |
| clippy::missing_safety_doc, |
| clippy::multiple_unsafe_ops_per_block, |
| clippy::undocumented_unsafe_blocks |
| )] |
| // We defer to own discretion on type complexity. |
| #![allow(clippy::type_complexity)] |
| // Inlining format args isn't supported on our MSRV. |
| #![allow(clippy::uninlined_format_args)] |
| #![deny( |
| rustdoc::bare_urls, |
| rustdoc::broken_intra_doc_links, |
| rustdoc::invalid_codeblock_attributes, |
| rustdoc::invalid_html_tags, |
| rustdoc::invalid_rust_codeblocks, |
| rustdoc::missing_crate_level_docs, |
| rustdoc::private_intra_doc_links |
| )] |
| #![recursion_limit = "128"] |
| |
| macro_rules! ident { |
| (($fmt:literal $(, $arg:expr)*), $span:expr) => { |
| syn::Ident::new(&format!($fmt $(, crate::util::to_ident_str($arg))*), $span) |
| }; |
| } |
| |
| mod derive; |
| #[cfg(test)] |
| mod output_tests; |
| mod repr; |
| mod util; |
| |
| use syn::{DeriveInput, Error}; |
| |
| use crate::util::*; |
| |
| // FIXME(https://github.com/rust-lang/rust/issues/54140): Some errors could be |
| // made better if we could add multiple lines of error output like this: |
| // |
| // error: unsupported representation |
| // --> enum.rs:28:8 |
| // | |
| // 28 | #[repr(transparent)] |
| // | |
| // help: required by the derive of FromBytes |
| // |
| // Instead, we have more verbose error messages like "unsupported representation |
| // for deriving FromZeros, FromBytes, IntoBytes, or Unaligned on an enum" |
| // |
| // This will probably require Span::error |
| // (https://doc.rust-lang.org/nightly/proc_macro/struct.Span.html#method.error), |
| // which is currently unstable. Revisit this once it's stable. |
| |
| /// Defines a derive function named `$outer` which parses its input |
| /// `TokenStream` as a `DeriveInput` and then invokes the `$inner` function. |
| /// |
| /// Note that the separate `$outer` parameter is required - proc macro functions |
| /// are currently required to live at the crate root, and so the caller must |
| /// specify the name in order to avoid name collisions. |
| macro_rules! derive { |
| ($trait:ident => $outer:ident => $inner:path) => { |
| #[proc_macro_derive($trait, attributes(zerocopy))] |
| pub fn $outer(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| let ast = syn::parse_macro_input!(ts as DeriveInput); |
| let ctx = match Ctx::try_from_derive_input(ast) { |
| Ok(ctx) => ctx, |
| Err(e) => return e.into_compile_error().into(), |
| }; |
| let ts = $inner(&ctx, Trait::$trait).into_ts(); |
| // We wrap in `const_block` as a backstop in case any derive fails |
| // to wrap its output in `const_block` (and thus fails to annotate) |
| // with the full set of `#[allow(...)]` attributes). |
| let ts = const_block([Some(ts)]); |
| #[cfg(test)] |
| crate::util::testutil::check_hygiene(ts.clone()); |
| ts.into() |
| } |
| }; |
| } |
| |
| trait IntoTokenStream { |
| fn into_ts(self) -> proc_macro2::TokenStream; |
| } |
| |
| impl IntoTokenStream for proc_macro2::TokenStream { |
| fn into_ts(self) -> proc_macro2::TokenStream { |
| self |
| } |
| } |
| |
| impl IntoTokenStream for Result<proc_macro2::TokenStream, Error> { |
| fn into_ts(self) -> proc_macro2::TokenStream { |
| match self { |
| Ok(ts) => ts, |
| Err(err) => err.to_compile_error(), |
| } |
| } |
| } |
| |
| derive!(KnownLayout => derive_known_layout => crate::derive::known_layout::derive); |
| derive!(Immutable => derive_immutable => crate::derive::derive_immutable); |
| derive!(TryFromBytes => derive_try_from_bytes => crate::derive::try_from_bytes::derive_try_from_bytes); |
| derive!(FromZeros => derive_from_zeros => crate::derive::from_bytes::derive_from_zeros); |
| derive!(FromBytes => derive_from_bytes => crate::derive::from_bytes::derive_from_bytes); |
| derive!(IntoBytes => derive_into_bytes => crate::derive::into_bytes::derive_into_bytes); |
| derive!(Unaligned => derive_unaligned => crate::derive::unaligned::derive_unaligned); |
| derive!(ByteHash => derive_hash => crate::derive::derive_hash); |
| derive!(ByteEq => derive_eq => crate::derive::derive_eq); |
| derive!(SplitAt => derive_split_at => crate::derive::derive_split_at); |
| |
| /// Deprecated: prefer [`FromZeros`] instead. |
| #[deprecated(since = "0.8.0", note = "`FromZeroes` was renamed to `FromZeros`")] |
| #[doc(hidden)] |
| #[proc_macro_derive(FromZeroes)] |
| pub fn derive_from_zeroes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| derive_from_zeros(ts) |
| } |
| |
| /// Deprecated: prefer [`IntoBytes`] instead. |
| #[deprecated(since = "0.8.0", note = "`AsBytes` was renamed to `IntoBytes`")] |
| #[doc(hidden)] |
| #[proc_macro_derive(AsBytes)] |
| pub fn derive_as_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { |
| derive_into_bytes(ts) |
| } |