| 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 | |