180 lines · 6.7 KB · Rust
RawDownload
// 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); #[cfg_attr(not(zerocopy_unstable_linux), doc(hidden))]#[proc_macro_derive(most_traits, attributes(zerocopy))]pub fn most_traits(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(),    }    .skip_on_error();     // top-level traits for which to attempt a derive    let derives: [(fn(&Ctx, Trait) -> _, _); 6] = [        (crate::derive::known_layout::derive, Trait::KnownLayout),        (crate::derive::derive_immutable, Trait::Immutable),        (crate::derive::from_bytes::derive_from_bytes, Trait::FromBytes),        (crate::derive::into_bytes::derive_into_bytes, Trait::IntoBytes),        (crate::derive::derive_split_at, Trait::SplitAt),        (crate::derive::unaligned::derive_unaligned, Trait::Unaligned),    ];     let mut tokens = proc_macro2::TokenStream::new();    for (derive, t) in derives {        tokens.extend(derive(&ctx, t))    }     // 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(tokens)]);    #[cfg(test)]    crate::util::testutil::check_hygiene(ts.clone());    ts.into()} /// 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)}