blob: 5594dc280b2bc9db9fdf7ccdae46c068ddf21cc0 [file] [log] [blame] [edit]
#![deny(unsafe_code)]
#![cfg_attr(
feature = "nightly",
feature(allow_internal_unstable),
allow(internal_features)
)]
#![allow(clippy::tabs_in_doc_comments)]
#![warn(clippy::cargo, clippy::missing_docs_in_private_items)]
#![cfg_attr(feature = "nightly", allow(clippy::implied_bounds_in_impls))]
#![cfg_attr(doc, allow(unknown_lints), warn(rustdoc::all))]
//! # Description
//!
//! Attribute proc-macro to simplify deriving standard and other traits with
//! custom generic type bounds.
//!
//! # Usage
//!
//! The [`derive_where`](macro@derive_where) attribute can be used just like
//! std's `#[derive(...)]` statements:
//!
//! ```
//! # use std::marker::PhantomData;
//! # use derive_where::derive_where;
//! #[derive_where(Clone, Debug)]
//! struct Example<T>(PhantomData<T>);
//! ```
//!
//! This will generate trait implementations for `Example` for any `T`,
//! as opposed to std's derives, which would only implement these traits with
//! `T: Trait` bound to the corresponding trait.
//!
//! Multiple [`derive_where`](macro@derive_where) attributes can be added to an
//! item, but only the first one must use any path qualifications.
//!
//! ```
//! # use std::marker::PhantomData;
//! #[derive_where::derive_where(Clone, Debug)]
//! #[derive_where(Eq, PartialEq)]
//! struct Example1<T>(PhantomData<T>);
//! ```
//!
//! If using a different package name, you must specify this:
//!
//! ```
//! # extern crate derive_where as derive_where_;
//! # use std::marker::PhantomData;
//! # use derive_where::derive_where;
//! #[derive_where(crate = derive_where_)]
//! #[derive_where(Clone, Debug)]
//! struct Example<T>(PhantomData<T>);
//! ```
//!
//! In addition, the following convenience options are available:
//!
//! ## Generic type bounds
//!
//! Separated from the list of traits with a semi-colon, types to bind to can be
//! specified. This example will restrict the implementation for `Example` to
//! `T: Clone`:
//!
//! ```
//! # use std::marker::PhantomData;
//! # use derive_where::derive_where;
//! #[derive_where(Clone, Debug; T)]
//! struct Example<T, U>(T, PhantomData<U>);
//! ```
//!
//! It is also possible to specify the bounds to be applied. This will
//! bind implementation for `Example` to `T: Super`:
//!
//! ```
//! # use std::fmt::Debug;
//! # use std::marker::PhantomData;
//! # use derive_where::derive_where;
//! trait Super: Clone + Debug {}
//!
//! #[derive_where(Clone, Debug; T: Super)]
//! struct Example<T>(PhantomData<T>);
//! ```
//!
//! But more complex trait bounds are possible as well.
//! The example below will restrict the [`Clone`] implementation for `Example`
//! to `T::Type: Clone`:
//!
//! ```
//! # use std::marker::PhantomData;
//! # use derive_where::derive_where;
//! trait Trait {
//! type Type;
//! }
//!
//! struct Impl;
//!
//! impl Trait for Impl {
//! type Type = i32;
//! }
//!
//! #[derive_where(Clone, Debug; T::Type)]
//! struct Example<T: Trait>(T::Type);
//! ```
//!
//! Any combination of options listed here can be used to satisfy a
//! specific constrain. It is also possible to use multiple separate
//! constrain specifications when required:
//!
//! ```
//! # use std::marker::PhantomData;
//! # use derive_where::derive_where;
//! #[derive_where(Clone, Debug; T)]
//! #[derive_where(Eq, PartialEq; U)]
//! struct Example<T, U>(PhantomData<T>, PhantomData<U>);
//! ```
//!
//! ## Enum default
//!
//! Since Rust 1.62 deriving [`Default`] on an enum is possible with the
//! `#[default]` attribute. Derive-where allows this with a
//! `#[derive_where(default)]` attribute:
//!
//! ```
//! # use std::marker::PhantomData;
//! # use derive_where::derive_where;
//! #[derive_where(Clone, Default)]
//! enum Example<T> {
//! #[derive_where(default)]
//! A(PhantomData<T>),
//! }
//! ```
//!
//! ## Skipping fields
//!
//! With a `skip` or `skip_inner` attribute fields can be skipped for traits
//! that allow it, which are: [`Debug`], [`Hash`], [`Ord`], [`PartialOrd`],
//! [`PartialEq`], [`Zeroize`] and [`ZeroizeOnDrop`].
//!
//! ```
//! # use std::marker::PhantomData;
//! # use derive_where::derive_where;
//! #[derive_where(Debug, PartialEq; T)]
//! struct Example<T>(#[derive_where(skip)] T);
//!
//! assert_eq!(format!("{:?}", Example(42)), "Example");
//! assert_eq!(Example(42), Example(0));
//! ```
//!
//! It is also possible to skip all fields in an item or variant if desired:
//!
//! ```
//! # use std::marker::PhantomData;
//! # use derive_where::derive_where;
//! #[derive_where(Debug, PartialEq)]
//! #[derive_where(skip_inner)]
//! struct StructExample<T>(T);
//!
//! assert_eq!(format!("{:?}", StructExample(42)), "StructExample");
//! assert_eq!(StructExample(42), StructExample(0));
//!
//! #[derive_where(Debug, PartialEq)]
//! enum EnumExample<T> {
//! #[derive_where(skip_inner)]
//! A(T),
//! }
//!
//! assert_eq!(format!("{:?}", EnumExample::A(42)), "A");
//! assert_eq!(EnumExample::A(42), EnumExample::A(0));
//! ```
//!
//! Selective skipping of fields for certain traits is also an option, both in
//! `skip` and `skip_inner`. To prevent breaking invariants defined for these
//! traits, some of them can only be skipped in groups. The following groups are
//! available:
//! - [`Debug`]
//! - `EqHashOrd`: Skips [`Eq`], [`Hash`], [`Ord`], [`PartialOrd`] and
//! [`PartialEq`].
//! - [`Hash`]
//! - `Zeroize`: Skips [`Zeroize`] and [`ZeroizeOnDrop`].
//!
//! ```
//! # use std::marker::PhantomData;
//! # use derive_where::derive_where;
//! #[derive_where(Debug, PartialEq)]
//! #[derive_where(skip_inner(Debug))]
//! struct Example<T>(i32, PhantomData<T>);
//!
//! assert_eq!(format!("{:?}", Example(42, PhantomData::<()>)), "Example");
//! assert_ne!(
//! Example(42, PhantomData::<()>),
//! Example(0, PhantomData::<()>)
//! );
//! ```
//!
//! ## Incomparable variants/items
//!
//! Similar to the `skip` attribute, `incomparable` can be used to skip variants
//! or items in [`PartialEq`] and [`PartialOrd`] trait implementations, meaning
//! they will always yield `false` for `eq` and `None` for `partial_cmp`. This
//! results in all comparisons but `!=`, i.e. `==`, `<`, `<=`, `>=` and `>`,
//! with the marked variant or struct evaluating to `false`.
//!
//! ```
//! # use derive_where::derive_where;
//! #[derive(Debug)]
//! #[derive_where(PartialEq, PartialOrd)]
//! enum EnumExample {
//! #[derive_where(incomparable)]
//! Incomparable,
//! Comparable,
//! }
//! assert_eq!(EnumExample::Comparable, EnumExample::Comparable);
//! assert_ne!(EnumExample::Incomparable, EnumExample::Incomparable);
//! assert!(!(EnumExample::Comparable >= EnumExample::Incomparable));
//! assert!(!(EnumExample::Comparable <= EnumExample::Incomparable));
//! assert!(!(EnumExample::Incomparable >= EnumExample::Incomparable));
//! assert!(!(EnumExample::Incomparable <= EnumExample::Incomparable));
//!
//! #[derive(Debug)]
//! #[derive_where(PartialEq, PartialOrd)]
//! #[derive_where(incomparable)]
//! struct StructExample;
//!
//! assert_ne!(StructExample, StructExample);
//! assert!(!(StructExample >= StructExample));
//! assert!(!(StructExample <= StructExample));
//! ```
//!
//! Note that it is not possible to use `incomparable` with [`Eq`] or [`Ord`] as
//! that would break their invariants.
//!
//! ## `Zeroize` options
//!
//! `Zeroize` has two options:
//! - `crate`: an item-level option which specifies a path to the [`zeroize`]
//! crate in case of a re-export or rename.
//! - `fqs`: a field-level option which will use fully-qualified-syntax instead
//! of calling the [`zeroize`][method@zeroize] method on `self` directly. This
//! is to avoid ambiguity between another method also called `zeroize`.
//!
//! ```
//! # #[cfg(feature = "zeroize")]
//! # {
//! # use std::marker::PhantomData;
//! # use derive_where::derive_where;
//! # use zeroize_::Zeroize;
//! #[derive_where(Zeroize(crate = zeroize_))]
//! struct Example(#[derive_where(Zeroize(fqs))] i32);
//!
//! impl Example {
//! // If we didn't specify the `fqs` option, this would lead to a compile
//! // error because of method ambiguity.
//! fn zeroize(&mut self) {
//! self.0 = 1;
//! }
//! }
//!
//! let mut test = Example(42);
//!
//! // Will call the struct method.
//! test.zeroize();
//! assert_eq!(test.0, 1);
//!
//! // WIll call the `Zeroize::zeroize` method.
//! Zeroize::zeroize(&mut test);
//! assert_eq!(test.0, 0);
//! # }
//! ```
//!
//! ## `ZeroizeOnDrop` options
//!
//! If the `zeroize-on-drop` feature is enabled, it implements [`ZeroizeOnDrop`]
//! and can be implemented without [`Zeroize`], otherwise it only implements
//! [`Drop`] and requires [`Zeroize`] to be implemented.
//!
//! [`ZeroizeOnDrop`] has one option:
//! - `crate`: an item-level option which specifies a path to the [`zeroize`]
//! crate in case of a re-export or rename.
//!
//! ```
//! # #[cfg(feature = "zeroize-on-drop")]
//! # {
//! # use std::marker::PhantomData;
//! # use derive_where::derive_where;
//! #[derive_where(ZeroizeOnDrop(crate = zeroize_))]
//! struct Example(i32);
//!
//! assert!(core::mem::needs_drop::<Example>());
//! # }
//! ```
//!
//! ## Supported traits
//!
//! The following traits can be derived with derive-where:
//! - [`Clone`]
//! - [`Copy`]
//! - [`Debug`]
//! - [`Default`]
//! - [`Eq`]
//! - [`Hash`]
//! - [`Ord`]
//! - [`PartialEq`]
//! - [`PartialOrd`]
//! - [`Zeroize`]: Only available with the `zeroize` crate feature.
//! - [`ZeroizeOnDrop`]: Only available with the `zeroize` crate feature. If the
//! `zeroize-on-drop` feature is enabled, it implements [`ZeroizeOnDrop`],
//! otherwise it only implements [`Drop`].
//!
//! ## Supported items
//!
//! Structs, tuple structs, unions and enums are supported. Derive-where tries
//! it's best to discourage usage that could be covered by std's `derive`. For
//! example unit structs and enums only containing unit variants aren't
//! supported.
//!
//! Unions only support [`Clone`] and [`Copy`].
//!
//! [`PartialOrd`] and [`Ord`] need to determine the discriminant type to
//! function correctly. To protect against a potential future change to the
//! default discriminant type, some compile-time validation is inserted to
//! ascertain that the type remains `isize`.
//!
//! ## `no_std` support
//!
//! `no_std` support is provided by default.
//!
//! # Crate features
//!
//! - `nightly`: Implements [`Ord`] and [`PartialOrd`] with the help of
//! [`core::intrinsics::discriminant_value`], which is what Rust does by
//! default too. This requires a nightly version of the Rust compiler.
//! - `safe`: `safe`: Uses only safe ways to access the discriminant of the enum
//! for [`Ord`] and [`PartialOrd`]. It also replaces all cases of
//! [`core::hint::unreachable_unchecked`] in [`Ord`], [`PartialEq`] and
//! [`PartialOrd`], which is what std uses, with [`unreachable`].
//! - `zeroize`: Allows deriving [`Zeroize`] and [`zeroize`][method@zeroize] on
//! [`Drop`].
//! - `zeroize-on-drop`: Allows deriving [`Zeroize`] and [`ZeroizeOnDrop`] and
//! requires [`zeroize`] v1.5.
//!
//! # MSRV
//!
//! The current MSRV is 1.57 and is being checked by the CI. A change will be
//! accompanied by a minor version bump. If MSRV is important to you, use
//! `derive-where = "~1.x"` to pin a specific minor version to your crate.
//!
//! # Alternatives
//!
//! - [derivative](https://crates.io/crates/derivative) [![Crates.io](https://img.shields.io/crates/v/derivative.svg)](https://crates.io/crates/derivative)
//! is a great alternative with many options. Notably it doesn't support
//! `no_std` and requires an extra `#[derive(Derivative)]` to use.
//! - [derive_bounded](https://crates.io/crates/derive_bounded) [![Crates.io](https://img.shields.io/crates/v/derive_bounded.svg)](https://crates.io/crates/derive_bounded)
//! is a new alternative still in development.
//!
//! # Changelog
//!
//! See the [CHANGELOG] file for details.
//!
//! # License
//!
//! Licensed under either of
//!
//! - Apache License, Version 2.0 ([LICENSE-APACHE] or <http://www.apache.org/licenses/LICENSE-2.0>)
//! - MIT license ([LICENSE-MIT] or <http://opensource.org/licenses/MIT>)
//!
//! at your option.
//!
//! ## Contribution
//!
//! Unless you explicitly state otherwise, any contribution intentionally
//! submitted for inclusion in the work by you, as defined in the Apache-2.0
//! license, shall be dual licensed as above, without any additional terms or
//! conditions.
//!
//! [CHANGELOG]: https://github.com/ModProg/derive-where/blob/main/CHANGELOG.md
//! [LICENSE-MIT]: https://github.com/ModProg/derive-where/blob/main/LICENSE-MIT
//! [LICENSE-APACHE]: https://github.com/ModProg/derive-where/blob/main/LICENSE-APACHE
//! [`Debug`]: core::fmt::Debug
//! [`Default`]: core::default::Default
//! [`Eq`]: core::cmp::Eq
//! [`Hash`]: core::hash::Hash
//! [`Ord`]: core::cmp::Ord
//! [`PartialEq`]: core::cmp::PartialEq
//! [`PartialOrd`]: core::cmp::PartialOrd
//! [`zeroize`]: https://docs.rs/zeroize
//! [`Zeroize`]: https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html
//! [`ZeroizeOnDrop`]: https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html
//! [method@zeroize]: https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html#tymethod.zeroize
mod attr;
mod data;
mod error;
mod input;
mod item;
#[cfg(test)]
mod test;
mod trait_;
mod util;
use std::{borrow::Cow, iter};
use input::SplitGenerics;
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned, ToTokens};
use syn::{
spanned::Spanned, Attribute, DataEnum, DataStruct, DataUnion, DeriveInput, Expr, ExprLit,
ExprPath, Fields, FieldsNamed, FieldsUnnamed, Lit, Meta, Path, Result, Variant,
};
use util::MetaListExt;
#[cfg(feature = "zeroize")]
use self::attr::ZeroizeFqs;
use self::{
attr::{
Default, DeriveTrait, DeriveWhere, FieldAttr, Incomparable, ItemAttr, Skip, SkipGroup,
VariantAttr,
},
data::{Data, DataType, Field, SimpleType},
error::Error,
input::Input,
item::{Discriminant, Item},
trait_::{Trait, TraitImpl},
util::Either,
};
/// Name of the `derive_where` attribute proc-macro.
const DERIVE_WHERE: &str = "derive_where";
/// Name of the `DeriveWhere` derive proc-macro.
const DERIVE_WHERE_FORWARD: &str = "DeriveWhere";
/// Name of the `derive_where_visited` proc-macro.
const DERIVE_WHERE_VISITED: &str = "derive_where_visited";
/// Item-level options:
/// - `#[derive_where(crate = path)]`: Specify path to the `derive_where` crate.
/// - `#[derive_where(Clone, ..; T, ..)]`: Specify traits to implement and
/// optionally bounds.
/// - `#[derive_where(Zeroize(crate = path))]`: Specify path to [`Zeroize`]
/// trait.
/// - `#[derive_where(ZeroizeOnDrop(crate = path))]`: Specify path to
/// [`ZeroizeOnDrop`] trait.
/// - `#[derive_where(skip_inner(EqHashOrd, ..))]`: Skip all fields in the item.
/// Optionally specify trait groups to constrain skipping fields. Only works
/// for structs, for enums use this on the variant-level.
///
/// Variant-level options:
/// - `#[derive_where(default)]`: Uses this variant as the default for the
/// [`Default`](trait@core::default::Default) implementation.
/// - `#[derive_where(skip_inner(EqHashOrd, ..))]`: Skip all fields in this
/// variant. Optionally specify trait groups to constrain skipping fields.
///
/// Field-level options:
/// - `#[derive_where(skip(EqHashOrd, ...))]`: Skip field. Optionally specify
/// trait groups to constrain skipping field.
/// - `#[derive_where(Zeroize(fqs))]`: Use fully-qualified-syntax when
/// implementing [`Zeroize`].
///
/// See the [crate] level description for more details.
///
/// [`Zeroize`]: https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html
/// [`ZeroizeOnDrop`]: https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html
#[proc_macro_attribute]
pub fn derive_where(
attr: proc_macro::TokenStream,
original_input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let attr = TokenStream::from(attr);
let mut original_input = TokenStream::from(original_input);
let mut input = quote_spanned! { attr.span()=> #[derive_where(#attr)] };
input.extend(original_input.clone());
match syn::parse2::<DeriveInput>(input) {
Ok(input) => match derive_where_internal(input.clone()) {
Ok(item) => item.into(),
Err(error) => {
let mut clean_input =
input_without_derive_where_attributes(input).into_token_stream();
clean_input.extend(error.into_compile_error());
clean_input.into()
}
},
Err(error) => {
original_input.extend(error.into_compile_error());
original_input.into()
}
}
}
/// Convenient way to deal with [`Result`] for [`derive_where()`].
fn derive_where_internal(mut item: DeriveInput) -> Result<TokenStream> {
let mut crate_ = None;
// Search for `crate` option.
for attr in &item.attrs {
if attr.path().is_ident(DERIVE_WHERE) {
if let Meta::List(list) = &attr.meta {
if let Ok(nested) = list.parse_non_empty_nested_metas() {
if nested.len() == 1 {
let meta = nested.into_iter().next().expect("unexpected empty list");
if meta.path().is_ident("crate") {
if let Meta::NameValue(name_value) = meta {
let path = match &name_value.value {
Expr::Lit(ExprLit {
lit: Lit::Str(lit_str),
..
}) => match lit_str.parse::<Path>() {
Ok(path) => path,
Err(error) => {
return Err(Error::path(lit_str.span(), error))
}
},
Expr::Path(ExprPath { path, .. }) => path.clone(),
_ => return Err(Error::option_syntax(name_value.value.span())),
};
if path == util::path_from_strs(&[DERIVE_WHERE]) {
return Err(Error::path_unnecessary(
path.span(),
&format!("::{}", DERIVE_WHERE),
));
}
match crate_ {
Some(_) => {
return Err(Error::option_duplicate(
name_value.span(),
"crate",
))
}
None => crate_ = Some(path),
}
} else {
return Err(Error::option_syntax(meta.span()));
}
}
}
}
}
}
}
// Build [`Path`] to crate.
let crate_ = crate_.unwrap_or_else(|| util::path_from_strs(&[DERIVE_WHERE]));
// Build `derive_where_visited` path.
let derive_where_visited =
util::path_from_root_and_strs(crate_.clone(), &[DERIVE_WHERE_VISITED]);
// Check if we already parsed this item before.
for attr in &item.attrs {
if attr.path() == &derive_where_visited {
return Err(Error::visited(attr.span()));
}
}
// Mark this as visited to prevent duplicate `derive_where` attributes.
item.attrs
.push(syn::parse_quote! { #[#derive_where_visited] });
// Build `DeriveWhere` path.
let derive_where = util::path_from_root_and_strs(crate_, &[DERIVE_WHERE_FORWARD]);
// Let the `derive` proc-macro parse this.
let mut output = quote! { #[derive(#derive_where)] };
output.extend(item.into_token_stream());
Ok(output)
}
#[doc(hidden)]
#[proc_macro_derive(DeriveWhere, attributes(derive_where))]
#[cfg_attr(feature = "nightly", allow_internal_unstable(core_intrinsics))]
pub fn derive_where_actual(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = TokenStream::from(input);
let item = match syn::parse2::<DeriveInput>(input) {
Ok(item) => item,
Err(error) => {
return error.into_compile_error().into();
}
};
let span = {
let clean_item = DeriveInput {
attrs: Vec::new(),
vis: item.vis.clone(),
ident: item.ident.clone(),
generics: item.generics.clone(),
data: item.data.clone(),
};
clean_item.span()
};
match { Input::from_input(span, &item) } {
Ok(Input {
derive_wheres,
generics,
item,
}) => derive_wheres
.iter()
.flat_map(|derive_where| iter::repeat(derive_where).zip(&derive_where.traits))
.map(|(derive_where, trait_)| generate_impl(derive_where, trait_, &item, &generics))
.collect::<TokenStream>()
.into(),
Err(error) => error.into_compile_error().into(),
}
}
/// Marker attribute signifying that this item was already processed by a
/// `derive_where` attribute before. This should prevent users to wrongly use a
/// qualified path for a `derive_where` attribute except the first one.
///
/// MSRV: This currently prevents an MSRV down to 1.34, as proc-macro derives
/// are not allowed to come before a proc-macro attribute. But the logic of this
/// proc-macro attribute is circumvented if it isn't inserted at the end, after
/// the proc-macro derive.
#[doc(hidden)]
#[proc_macro_attribute]
pub fn derive_where_visited(
_attr: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
// No-op, just here to mark the item as visited.
input
}
/// Generate implementation for a [`Trait`].
fn generate_impl(
derive_where: &DeriveWhere,
trait_: &DeriveTrait,
item: &Item,
generics: &SplitGenerics,
) -> TokenStream {
let SplitGenerics {
imp,
ty,
where_clause,
} = generics;
let mut where_clause = where_clause.map(Cow::Borrowed);
derive_where.where_clause(&mut where_clause, trait_, item);
let body = generate_body(derive_where, &derive_where.traits, trait_, item, generics);
let ident = item.ident();
let path = trait_.impl_path(trait_);
let mut output = quote! {
#[automatically_derived]
impl #imp #path for #ident #ty
#where_clause
{
#body
}
};
if let Some((path, body)) = trait_.additional_impl(trait_) {
output.extend(quote! {
#[automatically_derived]
impl #imp #path for #ident #ty
#where_clause
{
#body
}
})
}
output
}
/// Generate implementation method body for a [`Trait`].
fn generate_body(
derive_where: &DeriveWhere,
traits: &[DeriveTrait],
trait_: &DeriveTrait,
item: &Item,
generics: &SplitGenerics<'_>,
) -> TokenStream {
let any_bound = !derive_where.generics.is_empty();
match &item {
Item::Item(data) => {
let body = trait_.build_body(any_bound, traits, trait_, data);
trait_.build_signature(any_bound, item, generics, traits, trait_, &body)
}
Item::Enum { variants, .. } => {
let body: TokenStream = variants
.iter()
.map(|data| trait_.build_body(any_bound, traits, trait_, data))
.collect();
trait_.build_signature(any_bound, item, generics, traits, trait_, &body)
}
}
}
/// Removes `derive_where` attributes from the item and all fields and variants.
///
/// This is necessary because Rust currently does not support helper attributes
/// for attribute proc-macros and therefore doesn't automatically remove them.
fn input_without_derive_where_attributes(mut input: DeriveInput) -> DeriveInput {
use syn::Data;
let DeriveInput { data, attrs, .. } = &mut input;
/// Remove all `derive_where` attributes.
fn remove_derive_where(attrs: &mut Vec<Attribute>) {
attrs.retain(|attr| !attr.path().is_ident(DERIVE_WHERE))
}
/// Remove all `derive_where` attributes from [`FieldsNamed`].
fn remove_derive_where_from_fields_named(fields: &mut FieldsNamed) {
let FieldsNamed { named, .. } = fields;
named
.iter_mut()
.for_each(|field| remove_derive_where(&mut field.attrs))
}
/// Remove all `derive_where` attributes from [`Fields`].
fn remove_derive_where_from_fields(fields: &mut Fields) {
match fields {
Fields::Named(fields) => remove_derive_where_from_fields_named(fields),
Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => unnamed
.iter_mut()
.for_each(|field| remove_derive_where(&mut field.attrs)),
Fields::Unit => (),
}
}
// Remove `derive_where` attributes from the item.
remove_derive_where(attrs);
// Remove `derive_where` attributes from variants or fields.
match data {
Data::Struct(DataStruct { fields, .. }) => remove_derive_where_from_fields(fields),
Data::Enum(DataEnum { variants, .. }) => {
variants
.iter_mut()
.for_each(|Variant { attrs, fields, .. }| {
remove_derive_where(attrs);
remove_derive_where_from_fields(fields)
})
}
Data::Union(DataUnion { fields, .. }) => remove_derive_where_from_fields_named(fields),
}
input
}