derive_where/lib.rs
1#![deny(unsafe_code)]
2#![cfg_attr(
3 feature = "nightly",
4 feature(allow_internal_unstable),
5 allow(internal_features)
6)]
7#![allow(clippy::tabs_in_doc_comments)]
8#![warn(clippy::cargo, clippy::missing_docs_in_private_items)]
9#![cfg_attr(feature = "nightly", allow(clippy::implied_bounds_in_impls))]
10#![cfg_attr(doc, allow(unknown_lints), warn(rustdoc::all))]
11
12//! # Description
13//!
14//! Attribute proc-macro to simplify deriving standard and other traits with
15//! custom generic type bounds.
16//!
17//! # Usage
18//!
19//! The [`derive_where`](macro@derive_where) attribute can be used just like
20//! std's `#[derive(...)]` statements:
21//!
22//! ```
23//! # use std::marker::PhantomData;
24//! # use derive_where::derive_where;
25//! #[derive_where(Clone, Debug)]
26//! struct Example<T>(PhantomData<T>);
27//! ```
28//!
29//! This will generate trait implementations for `Example` for any `T`,
30//! as opposed to std's derives, which would only implement these traits with
31//! `T: Trait` bound to the corresponding trait.
32//!
33//! Multiple [`derive_where`](macro@derive_where) attributes can be added to an
34//! item, but only the first one must use any path qualifications.
35//!
36//! ```
37//! # use std::marker::PhantomData;
38//! #[derive_where::derive_where(Clone, Debug)]
39//! #[derive_where(Eq, PartialEq)]
40//! struct Example1<T>(PhantomData<T>);
41//! ```
42//!
43//! If using a different package name, you must specify this:
44//!
45//! ```
46//! # extern crate derive_where as derive_where_;
47//! # use std::marker::PhantomData;
48//! # use derive_where::derive_where;
49//! #[derive_where(crate = derive_where_)]
50//! #[derive_where(Clone, Debug)]
51//! struct Example<T>(PhantomData<T>);
52//! ```
53//!
54//! In addition, the following convenience options are available:
55//!
56//! ## Generic type bounds
57//!
58//! Separated from the list of traits with a semicolon, types to bind to can be
59//! specified. This example will restrict the implementation for `Example` to
60//! `T: Clone`:
61//!
62//! ```
63//! # use std::marker::PhantomData;
64//! # use derive_where::derive_where;
65//! #[derive_where(Clone, Debug; T)]
66//! struct Example<T, U>(T, PhantomData<U>);
67//! ```
68//!
69//! It is also possible to specify the bounds to be applied. This will
70//! bind implementation for `Example` to `T: Super`:
71//!
72//! ```
73//! # use std::fmt::Debug;
74//! # use std::marker::PhantomData;
75//! # use derive_where::derive_where;
76//! trait Super: Clone + Debug {}
77//!
78//! #[derive_where(Clone, Debug; T: Super)]
79//! struct Example<T>(PhantomData<T>);
80//! ```
81//!
82//! But more complex trait bounds are possible as well.
83//! The example below will restrict the [`Clone`] implementation for `Example`
84//! to `T::Type: Clone`:
85//!
86//! ```
87//! # use std::marker::PhantomData;
88//! # use derive_where::derive_where;
89//! trait Trait {
90//! type Type;
91//! }
92//!
93//! struct Impl;
94//!
95//! impl Trait for Impl {
96//! type Type = i32;
97//! }
98//!
99//! #[derive_where(Clone, Debug; T::Type)]
100//! struct Example<T: Trait>(T::Type);
101//! ```
102//!
103//! Any combination of options listed here can be used to satisfy a
104//! specific constrain. It is also possible to use multiple separate
105//! constrain specifications when required:
106//!
107//! ```
108//! # use std::marker::PhantomData;
109//! # use derive_where::derive_where;
110//! #[derive_where(Clone, Debug; T)]
111//! #[derive_where(Eq, PartialEq; U)]
112//! struct Example<T, U>(PhantomData<T>, PhantomData<U>);
113//! ```
114//!
115//! ## Enum default
116//!
117//! Since Rust 1.62 deriving [`Default`] on an enum is possible with the
118//! `#[default]` attribute. Derive-where allows this with a
119//! `#[derive_where(default)]` attribute:
120//!
121//! ```
122//! # use std::marker::PhantomData;
123//! # use derive_where::derive_where;
124//! #[derive_where(Clone, Default)]
125//! enum Example<T> {
126//! #[derive_where(default)]
127//! A(PhantomData<T>),
128//! }
129//! ```
130//!
131//! ## Skipping fields
132//!
133//! With a `skip` or `skip_inner` attribute fields can be skipped for traits
134//! that allow it, which are: [`Debug`], [`Hash`], [`Ord`], [`PartialOrd`],
135//! [`PartialEq`], [`Zeroize`] and [`ZeroizeOnDrop`].
136//!
137//! ```
138//! # use std::marker::PhantomData;
139//! # use derive_where::derive_where;
140//! #[derive_where(Debug, PartialEq; T)]
141//! struct Example<T>(#[derive_where(skip)] T);
142//!
143//! assert_eq!(format!("{:?}", Example(42)), "Example");
144//! assert_eq!(Example(42), Example(0));
145//! ```
146//!
147//! It is also possible to skip all fields in an item or variant if desired:
148//!
149//! ```
150//! # use std::marker::PhantomData;
151//! # use derive_where::derive_where;
152//! #[derive_where(Debug, PartialEq)]
153//! #[derive_where(skip_inner)]
154//! struct StructExample<T>(T);
155//!
156//! assert_eq!(format!("{:?}", StructExample(42)), "StructExample");
157//! assert_eq!(StructExample(42), StructExample(0));
158//!
159//! #[derive_where(Debug, PartialEq)]
160//! enum EnumExample<T> {
161//! #[derive_where(skip_inner)]
162//! A(T),
163//! }
164//!
165//! assert_eq!(format!("{:?}", EnumExample::A(42)), "A");
166//! assert_eq!(EnumExample::A(42), EnumExample::A(0));
167//! ```
168//!
169//! Selective skipping of fields for certain traits is also an option, both in
170//! `skip` and `skip_inner`. To prevent breaking invariants defined for these
171//! traits, some of them can only be skipped in groups. The following groups are
172//! available:
173//! - [`Clone`]: Uses [`Default`] instead of [`Clone`].
174//! - [`Debug`]
175//! - `EqHashOrd`: Skips [`Eq`], [`Hash`], [`Ord`], [`PartialOrd`] and
176//! [`PartialEq`].
177//! - [`Hash`]
178//! - `Zeroize`: Skips [`Zeroize`] and [`ZeroizeOnDrop`].
179//!
180//! ```
181//! # use std::marker::PhantomData;
182//! # use derive_where::derive_where;
183//! #[derive_where(Debug, PartialEq)]
184//! #[derive_where(skip_inner(Debug))]
185//! struct Example<T>(i32, PhantomData<T>);
186//!
187//! assert_eq!(format!("{:?}", Example(42, PhantomData::<()>)), "Example");
188//! assert_ne!(
189//! Example(42, PhantomData::<()>),
190//! Example(0, PhantomData::<()>)
191//! );
192//! ```
193//!
194//! ## Incomparable variants/items
195//!
196//! Similar to the `skip` attribute, `incomparable` can be used to skip variants
197//! or items in [`PartialEq`] and [`PartialOrd`] trait implementations, meaning
198//! they will always yield `false` for `eq` and `None` for `partial_cmp`. This
199//! results in all comparisons but `!=`, i.e. `==`, `<`, `<=`, `>=` and `>`,
200//! with the marked variant or struct evaluating to `false`.
201//!
202//! ```
203//! # use derive_where::derive_where;
204//! #[derive(Debug)]
205//! #[derive_where(PartialEq, PartialOrd)]
206//! enum EnumExample {
207//! #[derive_where(incomparable)]
208//! Incomparable,
209//! Comparable,
210//! }
211//! assert_eq!(EnumExample::Comparable, EnumExample::Comparable);
212//! assert_ne!(EnumExample::Incomparable, EnumExample::Incomparable);
213//! assert!(!(EnumExample::Comparable >= EnumExample::Incomparable));
214//! assert!(!(EnumExample::Comparable <= EnumExample::Incomparable));
215//! assert!(!(EnumExample::Incomparable >= EnumExample::Incomparable));
216//! assert!(!(EnumExample::Incomparable <= EnumExample::Incomparable));
217//!
218//! #[derive(Debug)]
219//! #[derive_where(PartialEq, PartialOrd)]
220//! #[derive_where(incomparable)]
221//! struct StructExample;
222//!
223//! assert_ne!(StructExample, StructExample);
224//! assert!(!(StructExample >= StructExample));
225//! assert!(!(StructExample <= StructExample));
226//! ```
227//!
228//! Note that it is not possible to use `incomparable` with [`Eq`] or [`Ord`] as
229//! that would break their invariants.
230//!
231//! ## Serde `Deserialize` and `Serialize`
232//!
233//! Deriving [`Deserialize`] and [`Serialize`] works as expected. While
234//! derive-where does not offer any attribute options, regular `serde`
235//! attributes can be used. Derive-where will respect
236//! [`#[serde(crate = "...")]`](https://serde.rs/container-attrs.html#crate).
237//!
238//! ## `Zeroize` options
239//!
240//! `Zeroize` has two options:
241//! - `crate`: an item-level option which specifies a path to the [`zeroize`]
242//! crate in case of a re-export or rename.
243//! - `fqs`: a field-level option which will use fully-qualified-syntax instead
244//! of calling the [`zeroize`][method@zeroize] method on `self` directly. This
245//! is to avoid ambiguity between another method also called `zeroize`.
246//!
247//! ```
248//! # #[cfg(feature = "zeroize")]
249//! # {
250//! # use std::marker::PhantomData;
251//! # use derive_where::derive_where;
252//! # use zeroize_::Zeroize;
253//! #[derive_where(Zeroize(crate = zeroize_))]
254//! struct Example(#[derive_where(Zeroize(fqs))] i32);
255//!
256//! impl Example {
257//! // If we didn't specify the `fqs` option, this would lead to a compile
258//! // error because of method ambiguity.
259//! fn zeroize(&mut self) {
260//! self.0 = 1;
261//! }
262//! }
263//!
264//! let mut test = Example(42);
265//!
266//! // Will call the struct method.
267//! test.zeroize();
268//! assert_eq!(test.0, 1);
269//!
270//! // WIll call the `Zeroize::zeroize` method.
271//! Zeroize::zeroize(&mut test);
272//! assert_eq!(test.0, 0);
273//! # }
274//! ```
275//!
276//! ## `ZeroizeOnDrop` options
277//!
278//! If the `zeroize-on-drop` feature is enabled, it implements [`ZeroizeOnDrop`]
279//! and can be implemented without [`Zeroize`], otherwise it only implements
280//! [`Drop`] and requires [`Zeroize`] to be implemented.
281//!
282//! [`ZeroizeOnDrop`] has two options:
283//! - `crate`: an item-level option which specifies a path to the [`zeroize`]
284//! crate in case of a re-export or rename.
285//! - `no_drop`: an item-level option which will not implement [`Drop`] but
286//! instead only assert that every field implements [`ZeroizeOnDrop`].
287//! Requires the `zeroize-on-drop` feature.
288//!
289//! ```
290//! # #[cfg(feature = "zeroize-on-drop")]
291//! # {
292//! # use std::marker::PhantomData;
293//! # use derive_where::derive_where;
294//! #[derive_where(ZeroizeOnDrop(crate = zeroize_))]
295//! struct Example(i32);
296//!
297//! assert!(core::mem::needs_drop::<Example>());
298//! # }
299//! ```
300//!
301//! ## Supported traits
302//!
303//! The following traits can be derived with derive-where:
304//! - [`Clone`]
305//! - [`Copy`]
306//! - [`Debug`]
307//! - [`Default`]
308//! - [`Deserialize`]: Only available with the `serde` crate feature.
309//! - [`Eq`]
310//! - [`Hash`]
311//! - [`Ord`]
312//! - [`PartialEq`]
313//! - [`PartialOrd`]
314//! - [`Serialize`]: Only available with the `serde` crate feature.
315//! - [`Zeroize`]: Only available with the `zeroize` crate feature.
316//! - [`ZeroizeOnDrop`]: Only available with the `zeroize` crate feature. If the
317//! `zeroize-on-drop` feature is enabled, it implements [`ZeroizeOnDrop`],
318//! otherwise it only implements [`Drop`].
319//!
320//! ## Supported items
321//!
322//! Structs, tuple structs, unions and enums are supported. Derive-where tries
323//! its best to discourage usage that could be covered by std's `derive`. For
324//! example unit structs and enums only containing unit variants aren't
325//! supported.
326//!
327//! Unions only support [`Clone`] and [`Copy`].
328//!
329//! [`PartialOrd`] and [`Ord`] need to determine the discriminant type to
330//! function correctly. To protect against a potential future change to the
331//! default discriminant type, some compile-time validation is inserted to
332//! ascertain that the type remains `isize`.
333//!
334//! ## `no_std` support
335//!
336//! `no_std` support is provided by default.
337//!
338//! # Crate features
339//!
340//! - `nightly`: Implements [`Ord`] and [`PartialOrd`] with the help of
341//! [`core::intrinsics::discriminant_value`], which is what Rust does by
342//! default too. This requires a nightly version of the Rust compiler.
343//! - `safe`: `safe`: Uses only safe ways to access the discriminant of the enum
344//! for [`Ord`] and [`PartialOrd`]. It also replaces all cases of
345//! [`core::hint::unreachable_unchecked`] in [`Ord`], [`PartialEq`] and
346//! [`PartialOrd`], which is what std uses, with [`unreachable`].
347//! - `zeroize`: Allows deriving [`Zeroize`] and [`zeroize`][method@zeroize] on
348//! [`Drop`].
349//! - `zeroize-on-drop`: Allows deriving [`Zeroize`] and [`ZeroizeOnDrop`] and
350//! requires [`zeroize`] v1.5.
351//!
352//! # MSRV
353//!
354//! The current MSRV is 1.57 and is being checked by the CI. A change will be
355//! accompanied by a minor version bump. If MSRV is important to you, use
356//! `derive-where = "~1.x"` to pin a specific minor version to your crate.
357//!
358//! # Alternatives
359//!
360//! - [derivative](https://crates.io/crates/derivative) [](https://crates.io/crates/derivative)
361//! is a great alternative with many options. Notably it doesn't support
362//! `no_std` and requires an extra `#[derive(Derivative)]` to use.
363//! - [derive_bounded](https://crates.io/crates/derive_bounded) [](https://crates.io/crates/derive_bounded)
364//! is a new alternative still in development.
365//!
366//! # Changelog
367//!
368//! See the [CHANGELOG] file for details.
369//!
370//! # License
371//!
372//! Licensed under either of
373//!
374//! - Apache License, Version 2.0 ([LICENSE-APACHE] or <http://www.apache.org/licenses/LICENSE-2.0>)
375//! - MIT license ([LICENSE-MIT] or <http://opensource.org/licenses/MIT>)
376//!
377//! at your option.
378//!
379//! ## Contribution
380//!
381//! Unless you explicitly state otherwise, any contribution intentionally
382//! submitted for inclusion in the work by you, as defined in the Apache-2.0
383//! license, shall be dual licensed as above, without any additional terms or
384//! conditions.
385//!
386//! [CHANGELOG]: https://github.com/ModProg/derive-where/blob/main/CHANGELOG.md
387//! [LICENSE-MIT]: https://github.com/ModProg/derive-where/blob/main/LICENSE-MIT
388//! [LICENSE-APACHE]: https://github.com/ModProg/derive-where/blob/main/LICENSE-APACHE
389//! [`Debug`]: core::fmt::Debug
390//! [`Default`]: core::default::Default
391//! [`Deserialize`]: https://docs.rs/serde/latest/serde/derive.Deserialize.html
392//! [`Eq`]: core::cmp::Eq
393//! [`Hash`]: core::hash::Hash
394//! [`Ord`]: core::cmp::Ord
395//! [`PartialEq`]: core::cmp::PartialEq
396//! [`PartialOrd`]: core::cmp::PartialOrd
397//! [`Serialize`]: https://docs.rs/serde/latest/serde/derive.Serialize.html
398//! [`zeroize`]: https://docs.rs/zeroize
399//! [`Zeroize`]: https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html
400//! [`ZeroizeOnDrop`]: https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html
401//! [method@zeroize]: https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html#tymethod.zeroize
402
403mod attr;
404mod data;
405mod error;
406mod input;
407mod item;
408#[cfg(test)]
409mod test;
410mod trait_;
411mod util;
412
413use std::{borrow::Cow, iter};
414
415use input::SplitGenerics;
416use proc_macro2::TokenStream;
417use quote::{quote, quote_spanned, ToTokens};
418use syn::{
419 spanned::Spanned, Attribute, DataEnum, DataStruct, DataUnion, DeriveInput, Fields, FieldsNamed,
420 FieldsUnnamed, Meta, Path, Result, Variant,
421};
422use util::MetaListExt;
423
424#[cfg(feature = "zeroize")]
425use self::attr::ZeroizeFqs;
426#[cfg(not(feature = "nightly"))]
427use self::item::Discriminant;
428use self::{
429 attr::{Default, DeriveWhere, FieldAttr, Incomparable, ItemAttr, Skip, SkipGroup, VariantAttr},
430 data::{Data, DataType, Field, SimpleType},
431 error::Error,
432 input::Input,
433 item::Item,
434 trait_::{DeriveTrait, Trait, TraitImpl},
435 util::Either,
436};
437
438/// Name of the `derive_where` attribute proc-macro.
439const DERIVE_WHERE: &str = "derive_where";
440/// Name of the `DeriveWhere` derive proc-macro.
441const DERIVE_WHERE_FORWARD: &str = "DeriveWhere";
442/// Name of the `derive_where_visited` proc-macro.
443const DERIVE_WHERE_VISITED: &str = "derive_where_visited";
444
445/// Item-level options:
446/// - `#[derive_where(crate = path)]`: Specify path to the `derive_where` crate.
447/// - `#[derive_where(Clone, ..; T, ..)]`: Specify traits to implement and
448/// optionally bounds.
449/// - `#[derive_where(Zeroize(crate = path))]`: Specify path to [`Zeroize`]
450/// trait.
451/// - `#[derive_where(ZeroizeOnDrop(crate = path))]`: Specify path to
452/// [`ZeroizeOnDrop`] trait.
453/// - `#[derive_where(ZeroizeOnDrop(no_drop))]`: no [`Drop`] implementation.
454/// - `#[derive_where(skip_inner(EqHashOrd, ..))]`: Skip all fields in the item.
455/// Optionally specify trait groups to constrain skipping fields. Only works
456/// for structs, for enums use this on the variant-level.
457///
458/// Variant-level options:
459/// - `#[derive_where(default)]`: Uses this variant as the default for the
460/// [`Default`](trait@core::default::Default) implementation.
461/// - `#[derive_where(skip_inner(EqHashOrd, ..))]`: Skip all fields in this
462/// variant. Optionally specify trait groups to constrain skipping fields.
463///
464/// Field-level options:
465/// - `#[derive_where(skip(EqHashOrd, ...))]`: Skip field. Optionally specify
466/// trait groups to constrain skipping field.
467/// - `#[derive_where(Zeroize(fqs))]`: Use fully-qualified-syntax when
468/// implementing [`Zeroize`].
469///
470/// See the [crate] level description for more details.
471///
472/// [`Zeroize`]: https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html
473/// [`ZeroizeOnDrop`]: https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html
474#[proc_macro_attribute]
475pub fn derive_where(
476 attr: proc_macro::TokenStream,
477 original_input: proc_macro::TokenStream,
478) -> proc_macro::TokenStream {
479 let attr = TokenStream::from(attr);
480 let mut original_input = TokenStream::from(original_input);
481 let mut input = quote_spanned! { attr.span()=> #[derive_where(#attr)] };
482 input.extend(original_input.clone());
483
484 match syn::parse2::<DeriveInput>(input) {
485 Ok(input) => match derive_where_internal(input.clone()) {
486 Ok(item) => item.into(),
487 Err(error) => {
488 let mut clean_input =
489 input_without_derive_where_attributes(input).into_token_stream();
490 clean_input.extend(error.into_compile_error());
491 clean_input.into()
492 }
493 },
494 Err(error) => {
495 original_input.extend(error.into_compile_error());
496 original_input.into()
497 }
498 }
499}
500
501/// Convenient way to deal with [`Result`] for [`derive_where()`].
502fn derive_where_internal(mut item: DeriveInput) -> Result<TokenStream> {
503 let mut crate_ = None;
504
505 // Search for `crate` option.
506 for attr in &item.attrs {
507 if attr.path().is_ident(DERIVE_WHERE) {
508 if let Meta::List(list) = &attr.meta {
509 if let Ok(nested) = list.parse_non_empty_nested_metas() {
510 if nested.len() == 1 {
511 let meta = nested.into_iter().next().expect("unexpected empty list");
512
513 if meta.path().is_ident("crate") {
514 let (path, span) = attr::parse_crate(meta)?;
515
516 match crate_ {
517 Some(_) => return Err(Error::option_duplicate(span, "crate")),
518 None => crate_ = Some(path),
519 }
520 }
521 }
522 }
523 }
524 }
525 }
526
527 // Build [`Path`] to crate.
528 let crate_ = crate_.unwrap_or_else(|| util::path_from_strs(&[DERIVE_WHERE]));
529
530 // Build `derive_where_visited` path.
531 let derive_where_visited =
532 util::path_from_root_and_strs(crate_.clone(), &[DERIVE_WHERE_VISITED]);
533
534 // Check if we already parsed this item before.
535 for attr in &item.attrs {
536 if attr.path() == &derive_where_visited {
537 return Err(Error::visited(attr.span()));
538 }
539 }
540
541 // Mark this as visited to prevent duplicate `derive_where` attributes.
542 item.attrs
543 .push(syn::parse_quote! { #[#derive_where_visited] });
544
545 // Build `DeriveWhere` path.
546 let derive_where = util::path_from_root_and_strs(crate_, &[DERIVE_WHERE_FORWARD]);
547
548 // Let the `derive` proc-macro parse this.
549 let mut output = quote! { #[derive(#derive_where)] };
550 output.extend(item.into_token_stream());
551 Ok(output)
552}
553
554/// Internally we re-direct the attribute macro to the derive macro. Unlike the
555/// attribute macro, the derive macro supports helper attributes and evaluates
556/// `cfg`s.
557#[doc(hidden)]
558#[cfg_attr(
559 not(feature = "serde"),
560 proc_macro_derive(DeriveWhere, attributes(derive_where))
561)]
562#[cfg_attr(
563 feature = "serde",
564 proc_macro_derive(DeriveWhere, attributes(derive_where, serde))
565)]
566#[cfg_attr(feature = "nightly", allow_internal_unstable(core_intrinsics))]
567pub fn derive_where_actual(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
568 let input = TokenStream::from(input);
569 let full_item = match syn::parse2::<DeriveInput>(input) {
570 Ok(item) => item,
571 Err(error) => {
572 return error.into_compile_error().into();
573 }
574 };
575
576 let span = {
577 let clean_item = DeriveInput {
578 attrs: Vec::new(),
579 vis: full_item.vis.clone(),
580 ident: full_item.ident.clone(),
581 generics: full_item.generics.clone(),
582 data: full_item.data.clone(),
583 };
584
585 clean_item.span()
586 };
587
588 match Input::from_input(span, &full_item) {
589 Ok(Input {
590 crate_,
591 derive_wheres,
592 generics,
593 item,
594 }) => derive_wheres
595 .iter()
596 .flat_map(|derive_where| iter::repeat(derive_where).zip(&derive_where.traits))
597 .map(|(derive_where, trait_)| {
598 generate_impl(
599 crate_.as_ref(),
600 &full_item,
601 derive_where,
602 trait_,
603 &item,
604 &generics,
605 )
606 })
607 .collect::<TokenStream>()
608 .into(),
609 Err(error) => error.into_compile_error().into(),
610 }
611}
612
613/// Marker attribute signifying that this item was already processed by a
614/// `derive_where` attribute before. This should prevent users to wrongly use a
615/// qualified path for a `derive_where` attribute except the first one.
616///
617/// MSRV: This currently prevents an MSRV down to 1.34, as proc-macro derives
618/// are not allowed to come before a proc-macro attribute. But the logic of this
619/// proc-macro attribute is circumvented if it isn't inserted at the end, after
620/// the proc-macro derive.
621#[doc(hidden)]
622#[proc_macro_attribute]
623pub fn derive_where_visited(
624 _attr: proc_macro::TokenStream,
625 input: proc_macro::TokenStream,
626) -> proc_macro::TokenStream {
627 // No-op, just here to mark the item as visited.
628 input
629}
630
631/// Generate implementation for a [`Trait`].
632fn generate_impl(
633 crate_: Option<&Path>,
634 full_item: &DeriveInput,
635 derive_where: &DeriveWhere,
636 trait_: &DeriveTrait,
637 item: &Item,
638 generics: &SplitGenerics,
639) -> TokenStream {
640 let SplitGenerics {
641 imp,
642 ty,
643 where_clause,
644 } = generics;
645 let mut where_clause = where_clause.map(Cow::Borrowed);
646 derive_where.where_clause(&mut where_clause, trait_, item);
647
648 let body = generate_body(derive_where, trait_, item, generics);
649
650 let ident = item.ident();
651 let mut output = trait_.impl_item(crate_, full_item, imp, ident, ty, &where_clause, body);
652
653 if let Some((path, body)) = trait_.additional_impl() {
654 output.extend(quote! {
655 #[automatically_derived]
656 impl #imp #path for #ident #ty
657 #where_clause
658 {
659 #body
660 }
661 })
662 }
663
664 output
665}
666
667/// Generate implementation method body for a [`Trait`].
668fn generate_body(
669 derive_where: &DeriveWhere,
670 trait_: &DeriveTrait,
671 item: &Item,
672 generics: &SplitGenerics<'_>,
673) -> TokenStream {
674 match &item {
675 Item::Item(data) => {
676 let body = trait_.build_body(derive_where, data);
677 trait_.build_signature(derive_where, item, generics, &body)
678 }
679 Item::Enum { variants, .. } => {
680 let body: TokenStream = variants
681 .iter()
682 .map(|data| trait_.build_body(derive_where, data))
683 .collect();
684
685 trait_.build_signature(derive_where, item, generics, &body)
686 }
687 }
688}
689
690/// Removes `derive_where` attributes from the item and all fields and variants.
691///
692/// This is necessary because Rust currently does not support helper attributes
693/// for attribute proc-macros and therefore doesn't automatically remove them.
694fn input_without_derive_where_attributes(mut input: DeriveInput) -> DeriveInput {
695 use syn::Data;
696
697 let DeriveInput { data, attrs, .. } = &mut input;
698
699 /// Remove all `derive_where` attributes.
700 fn remove_derive_where(attrs: &mut Vec<Attribute>) {
701 attrs.retain(|attr| !attr.path().is_ident(DERIVE_WHERE))
702 }
703
704 /// Remove all `derive_where` attributes from [`FieldsNamed`].
705 fn remove_derive_where_from_fields_named(fields: &mut FieldsNamed) {
706 let FieldsNamed { named, .. } = fields;
707 named
708 .iter_mut()
709 .for_each(|field| remove_derive_where(&mut field.attrs))
710 }
711
712 /// Remove all `derive_where` attributes from [`Fields`].
713 fn remove_derive_where_from_fields(fields: &mut Fields) {
714 match fields {
715 Fields::Named(fields) => remove_derive_where_from_fields_named(fields),
716 Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => unnamed
717 .iter_mut()
718 .for_each(|field| remove_derive_where(&mut field.attrs)),
719 Fields::Unit => (),
720 }
721 }
722
723 // Remove `derive_where` attributes from the item.
724 remove_derive_where(attrs);
725
726 // Remove `derive_where` attributes from variants or fields.
727 match data {
728 Data::Struct(DataStruct { fields, .. }) => remove_derive_where_from_fields(fields),
729 Data::Enum(DataEnum { variants, .. }) => {
730 variants
731 .iter_mut()
732 .for_each(|Variant { attrs, fields, .. }| {
733 remove_derive_where(attrs);
734 remove_derive_where_from_fields(fields)
735 })
736 }
737 Data::Union(DataUnion { fields, .. }) => remove_derive_where_from_fields_named(fields),
738 }
739
740 input
741}
742
743/// This is used by the Serde implementation to remove the duplicate item.
744#[cfg(feature = "serde")]
745#[doc(hidden)]
746#[proc_macro_attribute]
747pub fn derive_where_serde(
748 _: proc_macro::TokenStream,
749 _: proc_macro::TokenStream,
750) -> proc_macro::TokenStream {
751 proc_macro::TokenStream::new()
752}