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 semi-colon, 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 | //! - [`Debug`] |
174 | //! - `EqHashOrd`: Skips [`Eq`], [`Hash`], [`Ord`], [`PartialOrd`] and |
175 | //! [`PartialEq`]. |
176 | //! - [`Hash`] |
177 | //! - `Zeroize`: Skips [`Zeroize`] and [`ZeroizeOnDrop`]. |
178 | //! |
179 | //! ``` |
180 | //! # use std::marker::PhantomData; |
181 | //! # use derive_where::derive_where; |
182 | //! #[derive_where(Debug, PartialEq)] |
183 | //! #[derive_where(skip_inner(Debug))] |
184 | //! struct Example<T>(i32, PhantomData<T>); |
185 | //! |
186 | //! assert_eq!(format!("{:?}" , Example(42, PhantomData::<()>)), "Example" ); |
187 | //! assert_ne!( |
188 | //! Example(42, PhantomData::<()>), |
189 | //! Example(0, PhantomData::<()>) |
190 | //! ); |
191 | //! ``` |
192 | //! |
193 | //! ## Incomparable variants/items |
194 | //! |
195 | //! Similar to the `skip` attribute, `incomparable` can be used to skip variants |
196 | //! or items in [`PartialEq`] and [`PartialOrd`] trait implementations, meaning |
197 | //! they will always yield `false` for `eq` and `None` for `partial_cmp`. This |
198 | //! results in all comparisons but `!=`, i.e. `==`, `<`, `<=`, `>=` and `>`, |
199 | //! with the marked variant or struct evaluating to `false`. |
200 | //! |
201 | //! ``` |
202 | //! # use derive_where::derive_where; |
203 | //! #[derive(Debug)] |
204 | //! #[derive_where(PartialEq, PartialOrd)] |
205 | //! enum EnumExample { |
206 | //! #[derive_where(incomparable)] |
207 | //! Incomparable, |
208 | //! Comparable, |
209 | //! } |
210 | //! assert_eq!(EnumExample::Comparable, EnumExample::Comparable); |
211 | //! assert_ne!(EnumExample::Incomparable, EnumExample::Incomparable); |
212 | //! assert!(!(EnumExample::Comparable >= EnumExample::Incomparable)); |
213 | //! assert!(!(EnumExample::Comparable <= EnumExample::Incomparable)); |
214 | //! assert!(!(EnumExample::Incomparable >= EnumExample::Incomparable)); |
215 | //! assert!(!(EnumExample::Incomparable <= EnumExample::Incomparable)); |
216 | //! |
217 | //! #[derive(Debug)] |
218 | //! #[derive_where(PartialEq, PartialOrd)] |
219 | //! #[derive_where(incomparable)] |
220 | //! struct StructExample; |
221 | //! |
222 | //! assert_ne!(StructExample, StructExample); |
223 | //! assert!(!(StructExample >= StructExample)); |
224 | //! assert!(!(StructExample <= StructExample)); |
225 | //! ``` |
226 | //! |
227 | //! Note that it is not possible to use `incomparable` with [`Eq`] or [`Ord`] as |
228 | //! that would break their invariants. |
229 | //! |
230 | //! ## `Zeroize` options |
231 | //! |
232 | //! `Zeroize` has two options: |
233 | //! - `crate`: an item-level option which specifies a path to the [`zeroize`] |
234 | //! crate in case of a re-export or rename. |
235 | //! - `fqs`: a field-level option which will use fully-qualified-syntax instead |
236 | //! of calling the [`zeroize`][method@zeroize] method on `self` directly. This |
237 | //! is to avoid ambiguity between another method also called `zeroize`. |
238 | //! |
239 | //! ``` |
240 | //! # #[cfg (feature = "zeroize" )] |
241 | //! # { |
242 | //! # use std::marker::PhantomData; |
243 | //! # use derive_where::derive_where; |
244 | //! # use zeroize_::Zeroize; |
245 | //! #[derive_where(Zeroize(crate = zeroize_))] |
246 | //! struct Example(#[derive_where(Zeroize(fqs))] i32); |
247 | //! |
248 | //! impl Example { |
249 | //! // If we didn't specify the `fqs` option, this would lead to a compile |
250 | //! // error because of method ambiguity. |
251 | //! fn zeroize(&mut self) { |
252 | //! self.0 = 1; |
253 | //! } |
254 | //! } |
255 | //! |
256 | //! let mut test = Example(42); |
257 | //! |
258 | //! // Will call the struct method. |
259 | //! test.zeroize(); |
260 | //! assert_eq!(test.0, 1); |
261 | //! |
262 | //! // WIll call the `Zeroize::zeroize` method. |
263 | //! Zeroize::zeroize(&mut test); |
264 | //! assert_eq!(test.0, 0); |
265 | //! # } |
266 | //! ``` |
267 | //! |
268 | //! ## `ZeroizeOnDrop` options |
269 | //! |
270 | //! If the `zeroize-on-drop` feature is enabled, it implements [`ZeroizeOnDrop`] |
271 | //! and can be implemented without [`Zeroize`], otherwise it only implements |
272 | //! [`Drop`] and requires [`Zeroize`] to be implemented. |
273 | //! |
274 | //! [`ZeroizeOnDrop`] has one option: |
275 | //! - `crate`: an item-level option which specifies a path to the [`zeroize`] |
276 | //! crate in case of a re-export or rename. |
277 | //! |
278 | //! ``` |
279 | //! # #[cfg (feature = "zeroize-on-drop" )] |
280 | //! # { |
281 | //! # use std::marker::PhantomData; |
282 | //! # use derive_where::derive_where; |
283 | //! #[derive_where(ZeroizeOnDrop(crate = zeroize_))] |
284 | //! struct Example(i32); |
285 | //! |
286 | //! assert!(core::mem::needs_drop::<Example>()); |
287 | //! # } |
288 | //! ``` |
289 | //! |
290 | //! ## Supported traits |
291 | //! |
292 | //! The following traits can be derived with derive-where: |
293 | //! - [`Clone`] |
294 | //! - [`Copy`] |
295 | //! - [`Debug`] |
296 | //! - [`Default`] |
297 | //! - [`Eq`] |
298 | //! - [`Hash`] |
299 | //! - [`Ord`] |
300 | //! - [`PartialEq`] |
301 | //! - [`PartialOrd`] |
302 | //! - [`Zeroize`]: Only available with the `zeroize` crate feature. |
303 | //! - [`ZeroizeOnDrop`]: Only available with the `zeroize` crate feature. If the |
304 | //! `zeroize-on-drop` feature is enabled, it implements [`ZeroizeOnDrop`], |
305 | //! otherwise it only implements [`Drop`]. |
306 | //! |
307 | //! ## Supported items |
308 | //! |
309 | //! Structs, tuple structs, unions and enums are supported. Derive-where tries |
310 | //! it's best to discourage usage that could be covered by std's `derive`. For |
311 | //! example unit structs and enums only containing unit variants aren't |
312 | //! supported. |
313 | //! |
314 | //! Unions only support [`Clone`] and [`Copy`]. |
315 | //! |
316 | //! [`PartialOrd`] and [`Ord`] need to determine the discriminant type to |
317 | //! function correctly. To protect against a potential future change to the |
318 | //! default discriminant type, some compile-time validation is inserted to |
319 | //! ascertain that the type remains `isize`. |
320 | //! |
321 | //! ## `no_std` support |
322 | //! |
323 | //! `no_std` support is provided by default. |
324 | //! |
325 | //! # Crate features |
326 | //! |
327 | //! - `nightly`: Implements [`Ord`] and [`PartialOrd`] with the help of |
328 | //! [`core::intrinsics::discriminant_value`], which is what Rust does by |
329 | //! default too. This requires a nightly version of the Rust compiler. |
330 | //! - `safe`: `safe`: Uses only safe ways to access the discriminant of the enum |
331 | //! for [`Ord`] and [`PartialOrd`]. It also replaces all cases of |
332 | //! [`core::hint::unreachable_unchecked`] in [`Ord`], [`PartialEq`] and |
333 | //! [`PartialOrd`], which is what std uses, with [`unreachable`]. |
334 | //! - `zeroize`: Allows deriving [`Zeroize`] and [`zeroize`][method@zeroize] on |
335 | //! [`Drop`]. |
336 | //! - `zeroize-on-drop`: Allows deriving [`Zeroize`] and [`ZeroizeOnDrop`] and |
337 | //! requires [`zeroize`] v1.5. |
338 | //! |
339 | //! # MSRV |
340 | //! |
341 | //! The current MSRV is 1.57 and is being checked by the CI. A change will be |
342 | //! accompanied by a minor version bump. If MSRV is important to you, use |
343 | //! `derive-where = "~1.x"` to pin a specific minor version to your crate. |
344 | //! |
345 | //! # Alternatives |
346 | //! |
347 | //! - [derivative](https://crates.io/crates/derivative) [](https://crates.io/crates/derivative) |
348 | //! is a great alternative with many options. Notably it doesn't support |
349 | //! `no_std` and requires an extra `#[derive(Derivative)]` to use. |
350 | //! - [derive_bounded](https://crates.io/crates/derive_bounded) [](https://crates.io/crates/derive_bounded) |
351 | //! is a new alternative still in development. |
352 | //! |
353 | //! # Changelog |
354 | //! |
355 | //! See the [CHANGELOG] file for details. |
356 | //! |
357 | //! # License |
358 | //! |
359 | //! Licensed under either of |
360 | //! |
361 | //! - Apache License, Version 2.0 ([LICENSE-APACHE] or <http://www.apache.org/licenses/LICENSE-2.0>) |
362 | //! - MIT license ([LICENSE-MIT] or <http://opensource.org/licenses/MIT>) |
363 | //! |
364 | //! at your option. |
365 | //! |
366 | //! ## Contribution |
367 | //! |
368 | //! Unless you explicitly state otherwise, any contribution intentionally |
369 | //! submitted for inclusion in the work by you, as defined in the Apache-2.0 |
370 | //! license, shall be dual licensed as above, without any additional terms or |
371 | //! conditions. |
372 | //! |
373 | //! [CHANGELOG]: https://github.com/ModProg/derive-where/blob/main/CHANGELOG.md |
374 | //! [LICENSE-MIT]: https://github.com/ModProg/derive-where/blob/main/LICENSE-MIT |
375 | //! [LICENSE-APACHE]: https://github.com/ModProg/derive-where/blob/main/LICENSE-APACHE |
376 | //! [`Debug`]: core::fmt::Debug |
377 | //! [`Default`]: core::default::Default |
378 | //! [`Eq`]: core::cmp::Eq |
379 | //! [`Hash`]: core::hash::Hash |
380 | //! [`Ord`]: core::cmp::Ord |
381 | //! [`PartialEq`]: core::cmp::PartialEq |
382 | //! [`PartialOrd`]: core::cmp::PartialOrd |
383 | //! [`zeroize`]: https://docs.rs/zeroize |
384 | //! [`Zeroize`]: https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html |
385 | //! [`ZeroizeOnDrop`]: https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html |
386 | //! [method@zeroize]: https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html#tymethod.zeroize |
387 | |
388 | mod attr; |
389 | mod data; |
390 | mod error; |
391 | mod input; |
392 | mod item; |
393 | #[cfg (test)] |
394 | mod test; |
395 | mod trait_; |
396 | mod util; |
397 | |
398 | use std::{borrow::Cow, iter}; |
399 | |
400 | use input::SplitGenerics; |
401 | use proc_macro2::TokenStream; |
402 | use quote::{quote, quote_spanned, ToTokens}; |
403 | use syn::{ |
404 | spanned::Spanned, Attribute, DataEnum, DataStruct, DataUnion, DeriveInput, Expr, ExprLit, |
405 | ExprPath, Fields, FieldsNamed, FieldsUnnamed, Lit, Meta, Path, Result, Variant, |
406 | }; |
407 | use util::MetaListExt; |
408 | |
409 | #[cfg (feature = "zeroize" )] |
410 | use self::attr::ZeroizeFqs; |
411 | use self::{ |
412 | attr::{ |
413 | Default, DeriveTrait, DeriveWhere, FieldAttr, Incomparable, ItemAttr, Skip, SkipGroup, |
414 | VariantAttr, |
415 | }, |
416 | data::{Data, DataType, Field, SimpleType}, |
417 | error::Error, |
418 | input::Input, |
419 | item::{Discriminant, Item}, |
420 | trait_::{Trait, TraitImpl}, |
421 | util::Either, |
422 | }; |
423 | |
424 | /// Name of the `derive_where` attribute proc-macro. |
425 | const DERIVE_WHERE: &str = "derive_where" ; |
426 | /// Name of the `DeriveWhere` derive proc-macro. |
427 | const DERIVE_WHERE_FORWARD: &str = "DeriveWhere" ; |
428 | /// Name of the `derive_where_visited` proc-macro. |
429 | const DERIVE_WHERE_VISITED: &str = "derive_where_visited" ; |
430 | |
431 | /// Item-level options: |
432 | /// - `#[derive_where(crate = path)]`: Specify path to the `derive_where` crate. |
433 | /// - `#[derive_where(Clone, ..; T, ..)]`: Specify traits to implement and |
434 | /// optionally bounds. |
435 | /// - `#[derive_where(Zeroize(crate = path))]`: Specify path to [`Zeroize`] |
436 | /// trait. |
437 | /// - `#[derive_where(ZeroizeOnDrop(crate = path))]`: Specify path to |
438 | /// [`ZeroizeOnDrop`] trait. |
439 | /// - `#[derive_where(skip_inner(EqHashOrd, ..))]`: Skip all fields in the item. |
440 | /// Optionally specify trait groups to constrain skipping fields. Only works |
441 | /// for structs, for enums use this on the variant-level. |
442 | /// |
443 | /// Variant-level options: |
444 | /// - `#[derive_where(default)]`: Uses this variant as the default for the |
445 | /// [`Default`](trait@core::default::Default) implementation. |
446 | /// - `#[derive_where(skip_inner(EqHashOrd, ..))]`: Skip all fields in this |
447 | /// variant. Optionally specify trait groups to constrain skipping fields. |
448 | /// |
449 | /// Field-level options: |
450 | /// - `#[derive_where(skip(EqHashOrd, ...))]`: Skip field. Optionally specify |
451 | /// trait groups to constrain skipping field. |
452 | /// - `#[derive_where(Zeroize(fqs))]`: Use fully-qualified-syntax when |
453 | /// implementing [`Zeroize`]. |
454 | /// |
455 | /// See the [crate] level description for more details. |
456 | /// |
457 | /// [`Zeroize`]: https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html |
458 | /// [`ZeroizeOnDrop`]: https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html |
459 | #[proc_macro_attribute ] |
460 | pub fn derive_where ( |
461 | attr: proc_macro::TokenStream, |
462 | original_input: proc_macro::TokenStream, |
463 | ) -> proc_macro::TokenStream { |
464 | let attr: TokenStream = TokenStream::from(attr); |
465 | let mut original_input: TokenStream = TokenStream::from(original_input); |
466 | let mut input: TokenStream = quote_spanned! { attr.span()=> #[derive_where(#attr)] }; |
467 | input.extend(iter:original_input.clone()); |
468 | |
469 | match syn::parse2::<DeriveInput>(tokens:input) { |
470 | Ok(input: DeriveInput) => match derive_where_internal(item:input.clone()) { |
471 | Ok(item: TokenStream) => item.into(), |
472 | Err(error: Error) => { |
473 | let mut clean_input: TokenStream = |
474 | input_without_derive_where_attributes(input).into_token_stream(); |
475 | clean_input.extend(iter:error.into_compile_error()); |
476 | clean_input.into() |
477 | } |
478 | }, |
479 | Err(error: Error) => { |
480 | original_input.extend(iter:error.into_compile_error()); |
481 | original_input.into() |
482 | } |
483 | } |
484 | } |
485 | |
486 | /// Convenient way to deal with [`Result`] for [`derive_where()`]. |
487 | fn derive_where_internal(mut item: DeriveInput) -> Result<TokenStream> { |
488 | let mut crate_ = None; |
489 | |
490 | // Search for `crate` option. |
491 | for attr in &item.attrs { |
492 | if attr.path().is_ident(DERIVE_WHERE) { |
493 | if let Meta::List(list) = &attr.meta { |
494 | if let Ok(nested) = list.parse_non_empty_nested_metas() { |
495 | if nested.len() == 1 { |
496 | let meta = nested.into_iter().next().expect("unexpected empty list" ); |
497 | |
498 | if meta.path().is_ident("crate" ) { |
499 | if let Meta::NameValue(name_value) = meta { |
500 | let path = match &name_value.value { |
501 | Expr::Lit(ExprLit { |
502 | lit: Lit::Str(lit_str), |
503 | .. |
504 | }) => match lit_str.parse::<Path>() { |
505 | Ok(path) => path, |
506 | Err(error) => { |
507 | return Err(Error::path(lit_str.span(), error)) |
508 | } |
509 | }, |
510 | Expr::Path(ExprPath { path, .. }) => path.clone(), |
511 | _ => return Err(Error::option_syntax(name_value.value.span())), |
512 | }; |
513 | |
514 | if path == util::path_from_strs(&[DERIVE_WHERE]) { |
515 | return Err(Error::path_unnecessary( |
516 | path.span(), |
517 | &format!(":: {}" , DERIVE_WHERE), |
518 | )); |
519 | } |
520 | |
521 | match crate_ { |
522 | Some(_) => { |
523 | return Err(Error::option_duplicate( |
524 | name_value.span(), |
525 | "crate" , |
526 | )) |
527 | } |
528 | None => crate_ = Some(path), |
529 | } |
530 | } else { |
531 | return Err(Error::option_syntax(meta.span())); |
532 | } |
533 | } |
534 | } |
535 | } |
536 | } |
537 | } |
538 | } |
539 | |
540 | // Build [`Path`] to crate. |
541 | let crate_ = crate_.unwrap_or_else(|| util::path_from_strs(&[DERIVE_WHERE])); |
542 | |
543 | // Build `derive_where_visited` path. |
544 | let derive_where_visited = |
545 | util::path_from_root_and_strs(crate_.clone(), &[DERIVE_WHERE_VISITED]); |
546 | |
547 | // Check if we already parsed this item before. |
548 | for attr in &item.attrs { |
549 | if attr.path() == &derive_where_visited { |
550 | return Err(Error::visited(attr.span())); |
551 | } |
552 | } |
553 | |
554 | // Mark this as visited to prevent duplicate `derive_where` attributes. |
555 | item.attrs |
556 | .push(syn::parse_quote! { #[#derive_where_visited] }); |
557 | |
558 | // Build `DeriveWhere` path. |
559 | let derive_where = util::path_from_root_and_strs(crate_, &[DERIVE_WHERE_FORWARD]); |
560 | |
561 | // Let the `derive` proc-macro parse this. |
562 | let mut output = quote! { #[derive(#derive_where)] }; |
563 | output.extend(item.into_token_stream()); |
564 | Ok(output) |
565 | } |
566 | |
567 | #[doc (hidden)] |
568 | #[proc_macro_derive (DeriveWhere, attributes(derive_where))] |
569 | #[cfg_attr (feature = "nightly" , allow_internal_unstable(core_intrinsics))] |
570 | pub fn derive_where_actual(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
571 | let input = TokenStream::from(input); |
572 | let item = match syn::parse2::<DeriveInput>(input) { |
573 | Ok(item) => item, |
574 | Err(error) => { |
575 | return error.into_compile_error().into(); |
576 | } |
577 | }; |
578 | |
579 | let span = { |
580 | let clean_item = DeriveInput { |
581 | attrs: Vec::new(), |
582 | vis: item.vis.clone(), |
583 | ident: item.ident.clone(), |
584 | generics: item.generics.clone(), |
585 | data: item.data.clone(), |
586 | }; |
587 | |
588 | clean_item.span() |
589 | }; |
590 | |
591 | match { Input::from_input(span, &item) } { |
592 | Ok(Input { |
593 | derive_wheres, |
594 | generics, |
595 | item, |
596 | }) => derive_wheres |
597 | .iter() |
598 | .flat_map(|derive_where| iter::repeat(derive_where).zip(&derive_where.traits)) |
599 | .map(|(derive_where, trait_)| generate_impl(derive_where, trait_, &item, &generics)) |
600 | .collect::<TokenStream>() |
601 | .into(), |
602 | Err(error) => error.into_compile_error().into(), |
603 | } |
604 | } |
605 | |
606 | /// Marker attribute signifying that this item was already processed by a |
607 | /// `derive_where` attribute before. This should prevent users to wrongly use a |
608 | /// qualified path for a `derive_where` attribute except the first one. |
609 | /// |
610 | /// MSRV: This currently prevents an MSRV down to 1.34, as proc-macro derives |
611 | /// are not allowed to come before a proc-macro attribute. But the logic of this |
612 | /// proc-macro attribute is circumvented if it isn't inserted at the end, after |
613 | /// the proc-macro derive. |
614 | #[doc (hidden)] |
615 | #[proc_macro_attribute ] |
616 | pub fn derive_where_visited ( |
617 | _attr: proc_macro::TokenStream, |
618 | input: proc_macro::TokenStream, |
619 | ) -> proc_macro::TokenStream { |
620 | // No-op, just here to mark the item as visited. |
621 | input |
622 | } |
623 | |
624 | /// Generate implementation for a [`Trait`]. |
625 | fn generate_impl( |
626 | derive_where: &DeriveWhere, |
627 | trait_: &DeriveTrait, |
628 | item: &Item, |
629 | generics: &SplitGenerics, |
630 | ) -> TokenStream { |
631 | let SplitGenerics { |
632 | imp, |
633 | ty, |
634 | where_clause, |
635 | } = generics; |
636 | let mut where_clause = where_clause.map(Cow::Borrowed); |
637 | derive_where.where_clause(&mut where_clause, trait_, item); |
638 | |
639 | let body = generate_body(derive_where, &derive_where.traits, trait_, item, generics); |
640 | |
641 | let ident = item.ident(); |
642 | let path = trait_.impl_path(trait_); |
643 | let mut output = quote! { |
644 | #[automatically_derived] |
645 | impl #imp #path for #ident #ty |
646 | #where_clause |
647 | { |
648 | #body |
649 | } |
650 | }; |
651 | |
652 | if let Some((path, body)) = trait_.additional_impl(trait_) { |
653 | output.extend(quote! { |
654 | #[automatically_derived] |
655 | impl #imp #path for #ident #ty |
656 | #where_clause |
657 | { |
658 | #body |
659 | } |
660 | }) |
661 | } |
662 | |
663 | output |
664 | } |
665 | |
666 | /// Generate implementation method body for a [`Trait`]. |
667 | fn generate_body( |
668 | derive_where: &DeriveWhere, |
669 | traits: &[DeriveTrait], |
670 | trait_: &DeriveTrait, |
671 | item: &Item, |
672 | generics: &SplitGenerics<'_>, |
673 | ) -> TokenStream { |
674 | let any_bound: bool = !derive_where.generics.is_empty(); |
675 | |
676 | match &item { |
677 | Item::Item(data: &Data<'_>) => { |
678 | let body: TokenStream = trait_.build_body(any_bound, traits, trait_, data); |
679 | trait_.build_signature(any_bound, item, generics, traits, trait_, &body) |
680 | } |
681 | Item::Enum { variants: &Vec>, .. } => { |
682 | let body: TokenStream = variantsimpl Iterator |
683 | .iter() |
684 | .map(|data: &Data<'_>| trait_.build_body(any_bound, traits, trait_, data)) |
685 | .collect(); |
686 | |
687 | trait_.build_signature(any_bound, item, generics, traits, trait_, &body) |
688 | } |
689 | } |
690 | } |
691 | |
692 | /// Removes `derive_where` attributes from the item and all fields and variants. |
693 | /// |
694 | /// This is necessary because Rust currently does not support helper attributes |
695 | /// for attribute proc-macros and therefore doesn't automatically remove them. |
696 | fn input_without_derive_where_attributes(mut input: DeriveInput) -> DeriveInput { |
697 | use syn::Data; |
698 | |
699 | let DeriveInput { data, attrs, .. } = &mut input; |
700 | |
701 | /// Remove all `derive_where` attributes. |
702 | fn remove_derive_where(attrs: &mut Vec<Attribute>) { |
703 | attrs.retain(|attr| !attr.path().is_ident(DERIVE_WHERE)) |
704 | } |
705 | |
706 | /// Remove all `derive_where` attributes from [`FieldsNamed`]. |
707 | fn remove_derive_where_from_fields_named(fields: &mut FieldsNamed) { |
708 | let FieldsNamed { named, .. } = fields; |
709 | named |
710 | .iter_mut() |
711 | .for_each(|field| remove_derive_where(&mut field.attrs)) |
712 | } |
713 | |
714 | /// Remove all `derive_where` attributes from [`Fields`]. |
715 | fn remove_derive_where_from_fields(fields: &mut Fields) { |
716 | match fields { |
717 | Fields::Named(fields) => remove_derive_where_from_fields_named(fields), |
718 | Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => unnamed |
719 | .iter_mut() |
720 | .for_each(|field| remove_derive_where(&mut field.attrs)), |
721 | Fields::Unit => (), |
722 | } |
723 | } |
724 | |
725 | // Remove `derive_where` attributes from the item. |
726 | remove_derive_where(attrs); |
727 | |
728 | // Remove `derive_where` attributes from variants or fields. |
729 | match data { |
730 | Data::Struct(DataStruct { fields, .. }) => remove_derive_where_from_fields(fields), |
731 | Data::Enum(DataEnum { variants, .. }) => { |
732 | variants |
733 | .iter_mut() |
734 | .for_each(|Variant { attrs, fields, .. }| { |
735 | remove_derive_where(attrs); |
736 | remove_derive_where_from_fields(fields) |
737 | }) |
738 | } |
739 | Data::Union(DataUnion { fields, .. }) => remove_derive_where_from_fields_named(fields), |
740 | } |
741 | |
742 | input |
743 | } |
744 | |