derive-where-1.2.7/.cargo_vcs_info.json0000644000000001360000000000100134310ustar { "git": { "sha1": "a47e8eddbc27be383999bd3e1f6aabfb0d2d62b5" }, "path_in_vcs": "" }derive-where-1.2.7/Cargo.toml0000644000000032240000000000100114300ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.57" name = "derive-where" version = "1.2.7" include = [ "src/**/*", "Cargo.toml", "LICENSE-*", "README.md", ] description = "Deriving with custom trait bounds" documentation = "https://docs.rs/derive-where" readme = "README.md" keywords = [ "derive", "macro", ] categories = [ "rust-patterns", "no-std", ] license = "MIT OR Apache-2.0" repository = "https://github.com/ModProg/derive-where" [package.metadata.docs.rs] all-features = true targets = [] [lib] proc-macro = true [dependencies.proc-macro2] version = "1" features = ["proc-macro"] default-features = false [dependencies.quote] version = "1" default-features = false [dependencies.syn] version = "2" features = [ "clone-impls", "derive", "extra-traits", "full", "parsing", "printing", ] default-features = false [dev-dependencies.pretty_assertions] version = "1" [dev-dependencies.rustversion] version = "1" [dev-dependencies.trybuild] version = "1.0.18" default-features = false [dev-dependencies.zeroize_] version = "1.5" default-features = false package = "zeroize" [features] nightly = [] safe = [] zeroize = [] zeroize-on-drop = ["zeroize"] derive-where-1.2.7/Cargo.toml.orig000064400000000000000000000022651046102023000151150ustar 00000000000000[workspace] default-members = [""] members = ["", "test-crates/crate_", "test-crates/ensure-no-std"] resolver = "2" [package] categories = ["rust-patterns", "no-std"] description = "Deriving with custom trait bounds" documentation = "https://docs.rs/derive-where" edition = "2021" include = ["src/**/*", "Cargo.toml", "LICENSE-*", "README.md"] keywords = ["derive", "macro"] license = "MIT OR Apache-2.0" name = "derive-where" readme = "README.md" repository = "https://github.com/ModProg/derive-where" rust-version = "1.57" version = "1.2.7" [lib] proc-macro = true [features] nightly = [] safe = [] zeroize = [] zeroize-on-drop = ["zeroize"] [dependencies] proc-macro2 = { version = "1", default-features = false, features = ["proc-macro"] } quote = { version = "1", default-features = false } syn = { version = "2", default-features = false, features = [ "clone-impls", "derive", "extra-traits", "full", "parsing", "printing", ] } [dev-dependencies] pretty_assertions = "1" rustversion = "1" trybuild = { version = "1.0.18", default-features = false } zeroize_ = { version = "1.5", package = "zeroize", default-features = false } [package.metadata.docs.rs] all-features = true targets = [] derive-where-1.2.7/LICENSE-APACHE000064400000000000000000000261351046102023000141540ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a peretual, worldwide, non-exclusive, no-cpharge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides it s Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. derive-where-1.2.7/LICENSE-MIT000064400000000000000000000020631046102023000136560ustar 00000000000000MIT License Copyright (c) 2021 Roland Fredenhagen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. derive-where-1.2.7/README.md000064400000000000000000000272271046102023000135120ustar 00000000000000# derive-where [![Crates.io Version](https://img.shields.io/crates/v/derive-where.svg)](https://crates.io/crates/derive-where) [![Live Build Status](https://img.shields.io/github/actions/workflow/status/ModProg/derive-where/test.yml?branch=main)](https://github.com/ModProg/derive-where/actions/workflows/test.yml) [![Docs.rs Documentation](https://img.shields.io/docsrs/derive-where)](https://docs.rs/crate/derive-where) ## Description Attribute proc-macro to simplify deriving standard and other traits with custom generic type bounds. ## Usage The [`derive_where`] attribute can be used just like std's `#[derive(...)]` statements: ```rust #[derive_where(Clone, Debug)] struct Example(PhantomData); ``` 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`] attributes can be added to an item, but only the first one must use any path qualifications. ```rust #[derive_where::derive_where(Clone, Debug)] #[derive_where(Eq, PartialEq)] struct Example1(PhantomData); ``` If using a different package name, you must specify this: ```rust #[derive_where(crate = derive_where_)] #[derive_where(Clone, Debug)] struct Example(PhantomData); ``` 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`: ```rust #[derive_where(Clone, Debug; T)] struct Example(T, PhantomData); ``` It is also possible to specify the bounds to be applied. This will bind implementation for `Example` to `T: Super`: ```rust trait Super: Clone + Debug {} #[derive_where(Clone, Debug; T: Super)] struct Example(PhantomData); ``` But more complex trait bounds are possible as well. The example below will restrict the [`Clone`] implementation for `Example` to `T::Type: Clone`: ```rust trait Trait { type Type; } struct Impl; impl Trait for Impl { type Type = i32; } #[derive_where(Clone, Debug; T::Type)] struct Example(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: ```rust #[derive_where(Clone, Debug; T)] #[derive_where(Eq, PartialEq; U)] struct Example(PhantomData, PhantomData); ``` ### 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: ```rust #[derive_where(Clone, Default)] enum Example { #[derive_where(default)] A(PhantomData), } ``` ### 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`]. ```rust #[derive_where(Debug, PartialEq; T)] struct Example(#[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: ```rust #[derive_where(Debug, PartialEq)] #[derive_where(skip_inner)] struct StructExample(T); assert_eq!(format!("{:?}", StructExample(42)), "StructExample"); assert_eq!(StructExample(42), StructExample(0)); #[derive_where(Debug, PartialEq)] enum EnumExample { #[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`]. ```rust #[derive_where(Debug, PartialEq)] #[derive_where(skip_inner(Debug))] struct Example(i32, PhantomData); 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`. ```rust # 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`. ```rust #[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. ```rust #[derive_where(ZeroizeOnDrop(crate = zeroize_))] struct Example(i32); assert!(core::mem::needs_drop::()); ``` ### 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 ) - MIT license ([LICENSE-MIT] or ) 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`]: https://doc.rust-lang.org/core/fmt/trait.Debug.html [`Default`]: https://doc.rust-lang.org/core/default/trait.Default.html [`Hash`]: https://doc.rust-lang.org/core/hash/trait.Hash.html [`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 [`Clone`]: https://doc.rust-lang.org/core/clone/trait.Clone.html [`Copy`]: https://doc.rust-lang.org/core/marker/trait.Copy.html [`core::hint::unreachable_unchecked`]: https://doc.rust-lang.org/core/hint/fn.unreachable_unchecked.html [`core::intrinsics::discriminant_value`]: https://doc.rust-lang.org/core/intrinsics/fn.discriminant_value.html [`derive_where`]: https://docs.rs/derive-where/latest/derive_where/attr.derive_where.html [`Discriminant`]: https://doc.rust-lang.org/core/mem/struct.Discriminant.html [`Drop`]: https://doc.rust-lang.org/core/ops/trait.Drop.html [`Eq`]: https://doc.rust-lang.org/core/cmp/trait.Eq.html [`i32`]: https://doc.rust-lang.org/core/primitive.i32.html [`isize`]: https://doc.rust-lang.org/core/primitive.isize.html [`Ord`]: https://doc.rust-lang.org/core/cmp/trait.Ord.html [`PartialEq`]: https://doc.rust-lang.org/core/cmp/trait.PartialEq.html [`PartialOrd`]: https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html [`transmute`]: https://doc.rust-lang.org/core/mem/fn.transmute.html [`unreachable`]: https://doc.rust-lang.org/core/macro.unreachable.html derive-where-1.2.7/src/attr/default.rs000064400000000000000000000022271046102023000157570ustar 00000000000000//! Attribute parsing for the `default` option. use proc_macro2::Span; use syn::{spanned::Spanned, Meta, Result}; use crate::{DeriveWhere, Error, Trait}; /// Stores if this variant should be the default when implementing /// [`Default`](trait@std::default::Default). #[derive(Clone, Copy, Default)] #[cfg_attr(test, derive(Debug))] pub struct Default(pub Option); impl Default { /// Token used for the `default` option. pub const DEFAULT: &'static str = "default"; /// Adds a [`Meta`] to this [`Default`](Self). pub fn add_attribute(&mut self, meta: &Meta, derive_wheres: &[DeriveWhere]) -> Result<()> { debug_assert!(meta.path().is_ident(Self::DEFAULT)); if let Meta::Path(path) = meta { if self.0.is_some() { Err(Error::option_duplicate(path.span(), Self::DEFAULT)) } else { let mut impl_default = false; for derive_where in derive_wheres { if derive_where.contains(Trait::Default) { impl_default = true; break; } } if impl_default { self.0 = Some(path.span()); Ok(()) } else { Err(Error::default(path.span())) } } } else { Err(Error::option_syntax(meta.span())) } } } derive-where-1.2.7/src/attr/field.rs000064400000000000000000000034471046102023000154230ustar 00000000000000//! Attribute parsing for fields. use syn::{spanned::Spanned, Attribute, Meta, Result}; use crate::{util::MetaListExt, DeriveWhere, Error, Skip, DERIVE_WHERE}; #[cfg(feature = "zeroize")] use crate::{Trait, TraitImpl, ZeroizeFqs}; /// Attributes on field. #[derive(Default)] #[cfg_attr(test, derive(Debug))] pub struct FieldAttr { /// [`Trait`](crate::Trait)s to skip this field for. #[cfg_attr(feature = "zeroize", allow(rustdoc::redundant_explicit_links))] pub skip: Skip, /// Use fully-qualified-syntax for the [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html) implementation on this field. #[cfg(feature = "zeroize")] pub zeroize_fqs: ZeroizeFqs, } impl FieldAttr { /// Create [`FieldAttr`] from [`Attribute`]s. pub fn from_attrs( derive_wheres: &[DeriveWhere], skip_inner: &Skip, attrs: &[Attribute], ) -> Result { let mut self_ = FieldAttr::default(); for attr in attrs { if attr.path().is_ident(DERIVE_WHERE) { self_.add_meta(derive_wheres, skip_inner, &attr.meta)? } } Ok(self_) } /// Add [`Meta`] to [`FieldAttr`]. fn add_meta( &mut self, derive_wheres: &[DeriveWhere], skip_inner: &Skip, meta: &Meta, ) -> Result<()> { debug_assert!(meta.path().is_ident(DERIVE_WHERE)); if let Meta::List(list) = meta { let nested = list.parse_non_empty_nested_metas()?; for meta in &nested { if meta.path().is_ident(Skip::SKIP) { self.skip .add_attribute(derive_wheres, Some(skip_inner), meta)?; continue; } #[cfg(feature = "zeroize")] { if meta.path().is_ident(Trait::Zeroize.as_str()) { self.zeroize_fqs.add_attribute(meta, derive_wheres)?; continue; } } return Err(Error::option(meta.path().span())); } Ok(()) } else { Err(Error::option_syntax(meta.span())) } } } derive-where-1.2.7/src/attr/incomparable.rs000064400000000000000000000026201046102023000167640ustar 00000000000000//! Attribute parsing for the `incomparable` option. use proc_macro2::Span; use syn::{spanned::Spanned, Meta, Result}; use crate::{attr::DeriveTrait, DeriveWhere, Error}; /// Stores if this variant should be incomparable when implementing /// [`PartialEq`] or [`PartialOrd`]. #[derive(Clone, Copy, Default)] #[cfg_attr(test, derive(Debug))] pub struct Incomparable(pub Option); impl Incomparable { /// Token used for the `incomparable` option. pub const INCOMPARABLE: &'static str = "incomparable"; /// Adds a [`Meta`] to this [`Incomparable`]. pub fn add_attribute(&mut self, meta: &Meta, derive_wheres: &[DeriveWhere]) -> Result<()> { debug_assert!(meta.path().is_ident(Self::INCOMPARABLE)); if let Meta::Path(path) = meta { if self.0.is_some() { Err(Error::option_duplicate(path.span(), Self::INCOMPARABLE)) } else { let mut impl_cmp = false; for trait_ in derive_wheres .iter() .flat_map(|derive_where| derive_where.traits.iter()) { match trait_ { DeriveTrait::Eq | DeriveTrait::Ord => { return Err(Error::non_partial_incomparable(path.span())); } DeriveTrait::PartialEq | DeriveTrait::PartialOrd => impl_cmp = true, _ => {} } } if impl_cmp { self.0 = Some(path.span()); Ok(()) } else { Err(Error::incomparable(path.span())) } } } else { Err(Error::option_syntax(meta.span())) } } } derive-where-1.2.7/src/attr/item.rs000064400000000000000000000343631046102023000152770ustar 00000000000000//! [`Attribute`] parsing for items. use std::{borrow::Cow, ops::Deref}; use proc_macro2::Span; use syn::{ parse::{discouraged::Speculative, Parse, ParseStream}, punctuated::Punctuated, spanned::Spanned, Attribute, Data, Ident, Meta, Path, PredicateType, Result, Token, TraitBound, TraitBoundModifier, Type, TypeParamBound, TypePath, WhereClause, WherePredicate, }; use crate::{ util::{self, MetaListExt}, Error, Incomparable, Item, Skip, SkipGroup, Trait, TraitImpl, DERIVE_WHERE, }; /// Attributes on item. #[derive(Default)] pub struct ItemAttr { /// [`Trait`]s to skip all fields for. pub skip_inner: Skip, /// Comparing item will yield `false` for [`PartialEq`] and [`None`] for /// [`PartialOrd`]. pub incomparable: Incomparable, /// [`DeriveWhere`]s on this item. pub derive_wheres: Vec, } impl ItemAttr { /// Create [`ItemAttr`] from [`Attribute`]s. pub fn from_attrs(span: Span, data: &Data, attrs: &[Attribute]) -> Result { let mut self_ = ItemAttr::default(); let mut skip_inners = Vec::new(); let mut incomparables = Vec::new(); for attr in attrs { if attr.path().is_ident(DERIVE_WHERE) { if let Meta::List(list) = &attr.meta { if let Ok(nested) = list.parse_args_with(Punctuated::::parse_terminated) { match nested.len() { // Don't allow an empty list. 0 => return Err(Error::empty(list.span())), // Check for `skip_inner` if list only has one item. 1 => { let meta = nested.into_iter().next().expect("unexpected empty list"); if meta.path().is_ident(Skip::SKIP_INNER) { // Don't allow `skip_inner` on the item level for enums. if let Data::Enum(_) = data { return Err(Error::option_enum_skip_inner(meta.span())); } // Don't parse `Skip` yet, because it needs access to all // `DeriveWhere`s. skip_inners.push(meta); } else if meta.path().is_ident(Incomparable::INCOMPARABLE) { // Needs to be parsed after all traits are known. incomparables.push(meta) } else if meta.path().is_ident("crate") { // Do nothing, we checked this before // already. } // The list can have one item but still not be the `skip_inner` // attribute, continue with parsing `DeriveWhere`. else { self_ .derive_wheres .push(DeriveWhere::from_attr(span, data, attr)?); } } _ => self_ .derive_wheres .push(DeriveWhere::from_attr(span, data, attr)?), } } // Anything list that isn't using `,` as separator, is because we expect // `A, B; C`. else { self_ .derive_wheres .push(DeriveWhere::from_attr(span, data, attr)?) } } else { return Err(Error::option_syntax(attr.meta.span())); } } } // Check that we specified at least one `#[derive_where(..)]` with traits. if self_.derive_wheres.is_empty() { return Err(Error::none(span)); } // Merge `DeriveWhere`s with the same bounds. self_ .derive_wheres .dedup_by(|derive_where_1, derive_where_2| { if derive_where_1.generics == derive_where_2.generics { derive_where_2.spans.append(&mut derive_where_1.spans); derive_where_2.traits.append(&mut derive_where_1.traits); true } else { false } }); // Check for duplicate traits in the same `derive_where` after merging with the // same bounds. for derive_where in &self_.derive_wheres { for (skip, trait_) in (1..).zip(&derive_where.traits) { if let Some((span, _)) = derive_where .spans .iter() .zip(&derive_where.traits) .skip(skip) .find(|(_, other_trait)| *other_trait == trait_) { return Err(Error::trait_duplicate(*span)); } } } // Delayed parsing of `skip_inner` and `incomparable` to get access to all // traits to be implemented. for meta in skip_inners { self_ .skip_inner .add_attribute(&self_.derive_wheres, None, &meta)?; } for meta in incomparables { self_ .incomparable .add_attribute(&meta, &self_.derive_wheres)?; } Ok(self_) } } /// Holds parsed [generics](Generic) and [traits](crate::Trait). pub struct DeriveWhere { /// [`Span`]s for each [trait](DeriveTrait). pub spans: Vec, /// [Traits](DeriveTrait) to implement. pub traits: Vec, /// [Generics](Generic) for where clause. pub generics: Vec, } impl DeriveWhere { /// Create [`DeriveWhere`] from [`Attribute`]. fn from_attr(span: Span, data: &Data, attr: &Attribute) -> Result { attr.parse_args_with(|input: ParseStream| { // Parse the attribute input, this should either be: // - Comma separated traits. // - Comma separated traits `;` Comma separated generics. let mut spans = Vec::new(); let mut traits = Vec::new(); let mut generics = Vec::new(); // Check for an empty list is already done in `ItemAttr::from_attrs`. assert!(!input.is_empty()); while !input.is_empty() { // Start with parsing a trait. // Not checking for duplicates here, we do that after merging `derive_where`s // with the same bounds. let (span, trait_) = DeriveTrait::from_stream(span, data, input)?; spans.push(span); traits.push(trait_); if !input.is_empty() { let mut fork = input.fork(); // Track `Span` of whatever was found instead of a delimiter. We parse the `,` // first because it's allowed to be followed by a `;`. let no_delimiter_found = match ::parse(&fork) { Ok(_) => { input.advance_to(&fork); None } Err(error) => { // Reset the fork if we didn't find a `,`. fork = input.fork(); Some(error.span()) } }; if ::parse(&fork).is_ok() { input.advance_to(&fork); // If we found a semi-colon, start parsing generics. if !input.is_empty() { // `parse_terminated` parses everything left, which should end the // while-loop. // Not checking for duplicates here, as even Rust doesn't give a warning // for those: `where T: Clone, T: Clone` produces no error or warning. generics = Punctuated::::parse_terminated(input)? .into_iter() .collect(); } } // We are here because the input isn't empty, but we also found no delimiter, // something unexpected is here instead. else if let Some(span) = no_delimiter_found { return Err(Error::derive_where_delimiter(span)); } } } Ok(Self { generics, spans, traits, }) }) } /// Returns `true` if [`Trait`] is present. pub fn contains(&self, trait_: Trait) -> bool { self.traits .iter() .any(|derive_trait| derive_trait == trait_) } /// Returns `true` if any [`CustomBound`](Generic::CustomBound) is present. pub fn any_custom_bound(&self) -> bool { self.generics.iter().any(|generic| match generic { Generic::CustomBound(_) => true, Generic::NoBound(_) => false, }) } /// Returns `true` if the given generic type parameter if present. pub fn has_type_param(&self, type_param: &Ident) -> bool { self.generics.iter().any(|generic| match generic { Generic::NoBound(Type::Path(TypePath { qself: None, path })) => { if let Some(ident) = path.get_ident() { ident == type_param } else { false } } _ => false, }) } /// Returns `true` if any [`Trait`] supports skipping. pub fn any_skip(&self) -> bool { self.traits .iter() .any(|trait_| SkipGroup::trait_supported(**trait_)) } /// Create [`WhereClause`] for the given parameters. pub fn where_clause( &self, where_clause: &mut Option>, trait_: &DeriveTrait, item: &Item, ) { // Only create a where clause if required if !self.generics.is_empty() { // We use the existing where clause or create a new one if required. let where_clause = where_clause.get_or_insert(Cow::Owned(WhereClause { where_token: ::default(), predicates: Punctuated::default(), })); // Insert bounds into the `where` clause. for generic in &self.generics { where_clause .to_mut() .predicates .push(WherePredicate::Type(match generic { Generic::CustomBound(type_bound) => type_bound.clone(), Generic::NoBound(path) => PredicateType { lifetimes: None, bounded_ty: path.clone(), colon_token: ::default(), bounds: trait_.where_bounds(item), }, })); } } } } /// Holds a single generic [type](Type) or [type with bound](PredicateType). #[derive(Eq, PartialEq)] pub enum Generic { /// Generic type with custom [specified bounds](PredicateType). CustomBound(PredicateType), /// Generic [type](Type) which will be bound to the [`DeriveTrait`]. NoBound(Type), } impl Parse for Generic { fn parse(input: ParseStream) -> Result { let fork = input.fork(); // Try to parse input as a `WherePredicate`. The problem is, both expressions // start with a Type, so starting with the `WherePredicate` is the easiest way // of differentiating them. if let Ok(where_predicate) = WherePredicate::parse(&fork) { input.advance_to(&fork); // Don't allow lifetimes, as it doesn't make sense in the context. if let WherePredicate::Type(path) = where_predicate { Ok(Generic::CustomBound(path)) } else { Err(Error::generic(where_predicate.span())) } } else { match Type::parse(input) { Ok(type_) => Ok(Generic::NoBound(type_)), Err(error) => Err(Error::generic_syntax(error.span(), error)), } } } } /// Trait to implement. #[derive(Eq, PartialEq)] pub enum DeriveTrait { /// [`Clone`]. Clone, /// [`Copy`]. Copy, /// [`Debug`](std::fmt::Debug). Debug, /// [`Default`]. Default, /// [`Eq`]. Eq, /// [`Hash`](std::hash::Hash). Hash, /// [`Ord`]. Ord, /// [`PartialEq`]. PartialEq, /// [`PartialOrd`]. PartialOrd, /// [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html). #[cfg(feature = "zeroize")] Zeroize { /// [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html) path. crate_: Option, }, /// [`ZeroizeOnDrop`](https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html). #[cfg(feature = "zeroize")] ZeroizeOnDrop { /// [`ZeroizeOnDrop`](https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html) path. crate_: Option, }, } impl Deref for DeriveTrait { type Target = Trait; fn deref(&self) -> &Self::Target { use DeriveTrait::*; match self { Clone => &Trait::Clone, Copy => &Trait::Copy, Debug => &Trait::Debug, Default => &Trait::Default, Eq => &Trait::Eq, Hash => &Trait::Hash, Ord => &Trait::Ord, PartialEq => &Trait::PartialEq, PartialOrd => &Trait::PartialOrd, #[cfg(feature = "zeroize")] Zeroize { .. } => &Trait::Zeroize, #[cfg(feature = "zeroize")] ZeroizeOnDrop { .. } => &Trait::ZeroizeOnDrop, } } } impl PartialEq for &DeriveTrait { fn eq(&self, other: &Trait) -> bool { let trait_: &Trait = self; trait_ == other } } impl DeriveTrait { /// Returns fully qualified [`Path`] for this trait. pub fn path(&self) -> Path { use DeriveTrait::*; match self { Clone => util::path_from_root_and_strs(self.crate_(), &["clone", "Clone"]), Copy => util::path_from_root_and_strs(self.crate_(), &["marker", "Copy"]), Debug => util::path_from_root_and_strs(self.crate_(), &["fmt", "Debug"]), Default => util::path_from_root_and_strs(self.crate_(), &["default", "Default"]), Eq => util::path_from_root_and_strs(self.crate_(), &["cmp", "Eq"]), Hash => util::path_from_root_and_strs(self.crate_(), &["hash", "Hash"]), Ord => util::path_from_root_and_strs(self.crate_(), &["cmp", "Ord"]), PartialEq => util::path_from_root_and_strs(self.crate_(), &["cmp", "PartialEq"]), PartialOrd => util::path_from_root_and_strs(self.crate_(), &["cmp", "PartialOrd"]), #[cfg(feature = "zeroize")] Zeroize { .. } => util::path_from_root_and_strs(self.crate_(), &["Zeroize"]), #[cfg(feature = "zeroize")] ZeroizeOnDrop { .. } => util::path_from_root_and_strs(self.crate_(), &["ZeroizeOnDrop"]), } } /// Returns the path to the root crate for this trait. pub fn crate_(&self) -> Path { use DeriveTrait::*; match self { Clone => util::path_from_strs(&["core"]), Copy => util::path_from_strs(&["core"]), Debug => util::path_from_strs(&["core"]), Default => util::path_from_strs(&["core"]), Eq => util::path_from_strs(&["core"]), Hash => util::path_from_strs(&["core"]), Ord => util::path_from_strs(&["core"]), PartialEq => util::path_from_strs(&["core"]), PartialOrd => util::path_from_strs(&["core"]), #[cfg(feature = "zeroize")] Zeroize { crate_, .. } => { if let Some(crate_) = crate_ { crate_.clone() } else { util::path_from_strs(&["zeroize"]) } } #[cfg(feature = "zeroize")] ZeroizeOnDrop { crate_, .. } => { if let Some(crate_) = crate_ { crate_.clone() } else { util::path_from_strs(&["zeroize"]) } } } } /// Returns where-clause bounds for the trait in respect of the item type. fn where_bounds(&self, data: &Item) -> Punctuated { let mut list = Punctuated::new(); list.push(TypeParamBound::Trait(TraitBound { paren_token: None, modifier: TraitBoundModifier::None, lifetimes: None, path: self.path(), })); // Add bounds specific to the trait. if let Some(bound) = self.additional_where_bounds(data) { list.push(bound) } list } /// Create [`DeriveTrait`] from [`ParseStream`]. fn from_stream(span: Span, data: &Data, input: ParseStream) -> Result<(Span, Self)> { match Meta::parse(input) { Ok(meta) => { let trait_ = Trait::from_path(meta.path())?; if let Data::Union(_) = data { // Make sure this `Trait` supports unions. if !trait_.supports_union() { return Err(Error::union(span)); } } match &meta { Meta::Path(path) => Ok((path.span(), trait_.default_derive_trait())), Meta::List(list) => { let nested = list.parse_non_empty_nested_metas()?; // This will return an error if no options are supported. Ok((list.span(), trait_.parse_derive_trait(meta.span(), nested)?)) } Meta::NameValue(name_value) => Err(Error::option_syntax(name_value.span())), } } Err(error) => Err(Error::trait_syntax(error.span())), } } } derive-where-1.2.7/src/attr/skip.rs000064400000000000000000000153671046102023000153120ustar 00000000000000//! Attribute parsing for the `skip` and `skip_inner` options. use std::default::Default; use syn::{spanned::Spanned, Meta, Path, Result}; use crate::{util::MetaListExt, DeriveWhere, Error, Trait}; /// Stores what [`Trait`]s to skip this field or variant for. #[cfg_attr(test, derive(Debug))] pub enum Skip { /// Field skipped for no [`Trait`]. None, /// Field skipped for all [`Trait`]s that support it. All, /// Field skipped for the [`Trait`]s listed. Traits(Vec), } impl Default for Skip { fn default() -> Self { Skip::None } } impl Skip { /// Token used for the `skip` option. pub const SKIP: &'static str = "skip"; /// Token used for the `skip_inner` option. pub const SKIP_INNER: &'static str = "skip_inner"; /// Returns `true` if variant is [`Skip::None`]. pub fn is_none(&self) -> bool { matches!(self, Skip::None) } /// Adds a [`Meta`] to this [`Skip`]. pub fn add_attribute( &mut self, derive_wheres: &[DeriveWhere], skip_inner: Option<&Skip>, meta: &Meta, ) -> Result<()> { debug_assert!(meta.path().is_ident(Self::SKIP) || meta.path().is_ident(Self::SKIP_INNER)); match meta { Meta::Path(path) => { // Check for duplicates. if self.is_none() { // Check against parent `skip_inner`. match skip_inner { // Allow `Skip::All` on field if parent has a tighter constraint. Some(Skip::None) | Some(Skip::Traits(..)) | None => { // Don't allow to skip all traits if no trait to be implemented supports // skipping. if derive_wheres .iter() .any(|derive_where| derive_where.any_skip()) { *self = Skip::All; Ok(()) } else { Err(Error::option_skip_no_trait(path.span())) } } // Don't allow `Skip::All` on field if parent already covers it. Some(Skip::All) => Err(Error::option_skip_inner(path.span())), } } else { Err(Error::option_duplicate( path.span(), &meta .path() .get_ident() .expect("unexpected skip syntax") .to_string(), )) } } Meta::List(list) => { let nested = list.parse_non_empty_nested_metas()?; // Get traits already set to be skipped. let traits = match self { // If no traits are set, change to empty `Skip::Traits` and return that. Skip::None => { *self = Skip::Traits(Vec::new()); if let Skip::Traits(traits) = self { traits } else { unreachable!("unexpected variant") } } // If we are already skipping all traits, we can't skip again with constraints. Skip::All => return Err(Error::option_skip_all(list.span())), Skip::Traits(traits) => traits, }; for nested_meta in &nested { if let Meta::Path(path) = nested_meta { let skip_group = SkipGroup::from_path(path)?; // Don't allow to skip the same trait twice. if traits.contains(&skip_group) { return Err(Error::option_skip_duplicate( path.span(), skip_group.as_str(), )); } else { // Don't allow to skip a trait already set to be skipped in the // parent. match skip_inner { Some(skip_inner) if skip_inner.group_skipped(skip_group) => { return Err(Error::option_skip_inner(path.span())) } _ => { // Don't allow to skip trait that isn't being implemented. if derive_wheres.iter().any(|derive_where| { skip_group .traits() .any(|trait_| derive_where.contains(trait_)) }) { traits.push(skip_group) } else { return Err(Error::option_skip_trait(path.span())); } } } } } else { return Err(Error::option_syntax(nested_meta.span())); } } Ok(()) } _ => Err(Error::option_syntax(meta.span())), } } /// Returns `true` if this item, variant or field is skipped with the given /// [`Trait`]. pub fn trait_skipped(&self, trait_: Trait) -> bool { match self { Skip::None => false, Skip::All => SkipGroup::trait_supported(trait_), Skip::Traits(skip_groups) => skip_groups .iter() .any(|skip_group| skip_group.traits().any(|this_trait| this_trait == trait_)), } } /// Returns `true` if this item, variant or field is skipped with the given /// [`SkipGroup`]. pub fn group_skipped(&self, group: SkipGroup) -> bool { match self { Skip::None => false, Skip::All => true, Skip::Traits(groups) => groups.iter().any(|this_group| *this_group == group), } } } /// Available groups of [`Trait`]s to skip. #[derive(Clone, Copy, Eq, PartialEq)] #[cfg_attr(test, derive(Debug))] pub enum SkipGroup { /// [`Debug`]. Debug, /// [`Eq`], [`Hash`], [`Ord`], [`PartialEq`] and [`PartialOrd`]. EqHashOrd, /// [`Hash`]. Hash, /// [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html) and /// [`ZeroizeOnDrop`](https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html). #[cfg(feature = "zeroize")] Zeroize, } impl SkipGroup { /// Create [`SkipGroup`] from [`Path`]. fn from_path(path: &Path) -> Result { if let Some(ident) = path.get_ident() { use SkipGroup::*; match ident.to_string().as_str() { "Debug" => Ok(Debug), "EqHashOrd" => Ok(EqHashOrd), "Hash" => Ok(Hash), #[cfg(feature = "zeroize")] "Zeroize" => Ok(Zeroize), _ => Err(Error::skip_group(path.span())), } } else { Err(Error::skip_group(path.span())) } } /// [`str`] representation of this [`Trait`]. /// Used to compare against [`Ident`](struct@syn::Ident)s and create error /// messages. const fn as_str(self) -> &'static str { match self { Self::Debug => "Debug", Self::EqHashOrd => "EqHashOrd", Self::Hash => "Hash", #[cfg(feature = "zeroize")] Self::Zeroize => "Zeroize", } } /// [`Trait`]s supported by this group. fn traits(self) -> impl Iterator { match self { Self::Debug => [Some(Trait::Debug), None, None, None, None] .into_iter() .flatten(), Self::EqHashOrd => [ Some(Trait::Eq), Some(Trait::Hash), Some(Trait::Ord), Some(Trait::PartialEq), Some(Trait::PartialOrd), ] .into_iter() .flatten(), Self::Hash => [Some(Trait::Hash), None, None, None, None] .into_iter() .flatten(), #[cfg(feature = "zeroize")] Self::Zeroize => [ Some(Trait::Zeroize), Some(Trait::ZeroizeOnDrop), None, None, None, ] .into_iter() .flatten(), } } /// Returns `true` if [`Trait`] is supported by any group. pub fn trait_supported(trait_: Trait) -> bool { match trait_ { Trait::Clone | Trait::Copy | Trait::Default => false, Trait::Debug | Trait::Eq | Trait::Hash | Trait::Ord | Trait::PartialEq | Trait::PartialOrd => true, #[cfg(feature = "zeroize")] Trait::Zeroize | Trait::ZeroizeOnDrop => true, } } } derive-where-1.2.7/src/attr/variant.rs000064400000000000000000000042671046102023000160050ustar 00000000000000//! Attribute parsing for variants. use syn::{spanned::Spanned, Attribute, Fields, Meta, Result, Variant}; use crate::{util::MetaListExt, Default, DeriveWhere, Error, Incomparable, Skip, DERIVE_WHERE}; /// Attributes on variant. #[derive(Default)] pub struct VariantAttr { /// Default variant. pub default: Default, /// [`Trait`](crate::Trait)s to skip all fields for. pub skip_inner: Skip, /// Comparing variant will yield `false` for [`PartialEq`] and [`None`] for /// [`PartialOrd`]. pub incomparable: Incomparable, } impl VariantAttr { /// Create [`VariantAttr`] from [`Attribute`]s. pub fn from_attrs( attrs: &[Attribute], derive_wheres: &[DeriveWhere], variant: &Variant, ) -> Result { let mut self_ = VariantAttr::default(); for attr in attrs { if attr.path().is_ident(DERIVE_WHERE) { self_.add_meta(&attr.meta, derive_wheres, variant)? } } Ok(self_) } /// Add [`Meta`] to [`VariantAttr`]. fn add_meta( &mut self, meta: &Meta, derive_wheres: &[DeriveWhere], variant: &Variant, ) -> Result<()> { debug_assert!(meta.path().is_ident(DERIVE_WHERE)); if let Meta::List(list) = meta { let nested = list.parse_non_empty_nested_metas()?; if nested.is_empty() { return Err(Error::empty(list.span())); } for meta in &nested { if meta.path().is_ident(Skip::SKIP_INNER) { // Don't allow `skip_inner` on empty variants. match &variant.fields { Fields::Named(fields) if fields.named.is_empty() => { return Err(Error::option_skip_empty(variant.span())) } Fields::Unnamed(fields) if fields.unnamed.is_empty() => { return Err(Error::option_skip_empty(variant.span())) } Fields::Unit => return Err(Error::option_skip_empty(variant.span())), _ => self.skip_inner.add_attribute(derive_wheres, None, meta)?, } } else if meta.path().is_ident(Default::DEFAULT) { self.default.add_attribute(meta, derive_wheres)?; } else if meta.path().is_ident(Incomparable::INCOMPARABLE) { self.incomparable.add_attribute(meta, derive_wheres)?; } else { return Err(Error::option(meta.path().span())); } } Ok(()) } else { Err(Error::option_syntax(meta.span())) } } } derive-where-1.2.7/src/attr/zeroize_fqs.rs000064400000000000000000000027271046102023000167000ustar 00000000000000//! Attribute parsing for the `Zeroize(fqs)` option. use syn::{spanned::Spanned, Meta, Result}; use crate::{util::MetaListExt, DeriveWhere, Error, Trait, TraitImpl}; /// Stores if this field should use FQS to call [`Zeroize::zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html#tymethod.zeroize). #[derive(Default)] #[cfg_attr(test, derive(Debug))] pub struct ZeroizeFqs(pub bool); impl ZeroizeFqs { /// Token used for the `Zeroize(fqs)` option. const FQS: &'static str = "fqs"; /// Adds a [`Meta`] to this [`ZeroizeFqs`]. pub fn add_attribute(&mut self, meta: &Meta, derive_wheres: &[DeriveWhere]) -> Result<()> { debug_assert!(meta.path().is_ident(Trait::Zeroize.as_str())); if !derive_wheres .iter() .any(|derive_where| derive_where.contains(Trait::Zeroize)) { return Err(Error::zeroize(meta.span())); } match meta { Meta::List(list) => { let nested = list.parse_non_empty_nested_metas()?; for meta in &nested { if let Meta::Path(path) = meta { if path.is_ident(Self::FQS) { if self.0 { return Err(Error::option_duplicate(path.span(), Self::FQS)); } else { self.0 = true } } else { return Err(Error::option(path.span())); } } else { return Err(Error::option_syntax(meta.span())); } } Ok(()) } Meta::Path(path) => Err(Error::option_required(path.span(), Trait::Zeroize.as_str())), _ => Err(Error::option_syntax(meta.span())), } } } derive-where-1.2.7/src/attr.rs000064400000000000000000000006731046102023000143360ustar 00000000000000//! [`Attribute`](syn::Attribute) parsing for items, variants and fields. mod default; mod field; mod incomparable; mod item; mod skip; mod variant; #[cfg(feature = "zeroize")] mod zeroize_fqs; #[cfg(feature = "zeroize")] pub use self::zeroize_fqs::ZeroizeFqs; pub use self::{ default::Default, field::FieldAttr, incomparable::Incomparable, item::{DeriveTrait, DeriveWhere, ItemAttr}, skip::{Skip, SkipGroup}, variant::VariantAttr, }; derive-where-1.2.7/src/data/field.rs000064400000000000000000000064301046102023000153550ustar 00000000000000//! Field parsing. use std::fmt::{self, Display, Formatter}; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, IdentFragment, ToTokens}; use syn::{ext::IdentExt, Attribute, FieldsNamed, FieldsUnnamed, Ident, Index, Result, Type}; use crate::{DeriveWhere, FieldAttr, Skip, Trait}; /// Struct, union, struct variant or tuple variant field. #[cfg_attr(test, derive(Debug))] pub struct Field<'a> { /// Attributes. pub attr: FieldAttr, /// [`struct@Ident`] or [`Index`] for this field. pub member: Member<'a>, /// [`struct@Ident`] used as a Temporary variable for destructuring `self`. pub self_ident: Ident, /// [`struct@Ident`] used as a Temporary variable for destructuring `other`. pub other_ident: Ident, /// [`Type`] used for asserting traits on fields for [`Eq`]. pub type_: &'a Type, } /// Borrowed version of [`syn::Member`], to avoid unnecessary allocations. #[cfg_attr(test, derive(Debug))] pub enum Member<'a> { /// Named field. Named(&'a Ident), /// Unnamed field. Unnamed(Index), } impl IdentFragment for Member<'_> { fn fmt(&self, f: &mut Formatter) -> fmt::Result { Display::fmt(&self, f) } } impl ToTokens for Member<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Member::Named(ident) => ident.to_tokens(tokens), Member::Unnamed(index) => index.to_tokens(tokens), } } } impl Display for Member<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { Member::Named(ident) => write!(f, "{}", ident.unraw()), Member::Unnamed(index) => write!(f, "{}", index.index), } } } impl<'a> Field<'a> { /// Create [`Field`]s from [`syn::FieldsNamed`]. pub fn from_named( derive_wheres: &[DeriveWhere], skip_inner: &Skip, fields: &'a FieldsNamed, ) -> Result> { fields .named .iter() .map(|field| { Field::from_field( derive_wheres, skip_inner, &field.attrs, Member::Named(field.ident.as_ref().expect("unexpected unnamed field")), &field.ty, ) }) .collect() } /// Create [`Field`]s from [`syn::FieldsUnnamed`]. pub fn from_unnamed( derive_wheres: &[DeriveWhere], skip_inner: &Skip, fields: &'a FieldsUnnamed, ) -> Result> { (0_u32..) .zip(&fields.unnamed) .map(|(index, field)| { Field::from_field( derive_wheres, skip_inner, &field.attrs, Member::Unnamed(Index { index, span: Span::call_site(), }), &field.ty, ) }) .collect() } /// Create [`Field`] from [`syn::Field`]. fn from_field( derive_wheres: &[DeriveWhere], skip_inner: &Skip, attrs: &[Attribute], member: Member<'a>, type_: &'a Type, ) -> Result { let attr = FieldAttr::from_attrs(derive_wheres, skip_inner, attrs)?; let self_ident = format_ident!("__field_{}", member); let other_ident = format_ident!("__other_field_{}", member); Ok(Self { attr, member, self_ident, other_ident, type_, }) } /// Convert to [`syn::Member`]. pub fn to_member(&self) -> syn::Member { match self.member { Member::Named(ident) => syn::Member::Named(ident.clone()), Member::Unnamed(ref index) => syn::Member::Unnamed(index.clone()), } } /// Returns `true` if this field is skipped with the given [`Trait`]. pub fn skip(&self, trait_: Trait) -> bool { self.attr.skip.trait_skipped(trait_) } } derive-where-1.2.7/src/data/fields.rs000064400000000000000000000077661046102023000155550ustar 00000000000000//! Storage for items or variants with data. use syn::{ token::{Brace, Paren}, FieldPat, FieldsNamed, FieldsUnnamed, Ident, Pat, PatIdent, PatStruct, PatTupleStruct, Path, Result, Token, }; use crate::{DeriveWhere, Field, Skip, Trait}; /// Struct, union, struct variant or tuple variant fields. #[cfg_attr(test, derive(Debug))] pub struct Fields<'a> { /// [Pattern](Pat) to use in a match arm to destructure `self`. pub self_pattern: Pat, /// [Pattern](Pat) to use in a match arm to destructure `other`. pub other_pattern: Pat, /// [`Field`]s of this struct, union or variant. pub fields: Vec>, } impl<'a> Fields<'a> { /// Create [`Fields`]s from [`FieldsNamed`]. pub fn from_named( derive_wheres: &[DeriveWhere], skip_inner: &Skip, path: Path, fields: &'a FieldsNamed, ) -> Result { let fields = Field::from_named(derive_wheres, skip_inner, fields)?; let self_pattern = Self::struct_pattern(path.clone(), &fields, |field| &field.self_ident); let other_pattern = Self::struct_pattern(path, &fields, |field| &field.other_ident); Ok(Self { self_pattern, other_pattern, fields, }) } /// Create [`Fields`]s from [`FieldsUnnamed`]. pub fn from_unnamed( derive_wheres: &[DeriveWhere], skip_inner: &Skip, path: Path, fields: &'a FieldsUnnamed, ) -> Result { let fields = Field::from_unnamed(derive_wheres, skip_inner, fields)?; let self_pattern = Self::tuple_pattern(path.clone(), &fields, |field| &field.self_ident); let other_pattern = Self::tuple_pattern(path, &fields, |field| &field.other_ident); Ok(Self { self_pattern, other_pattern, fields, }) } /// Destructuring pattern in a match arm for this item or variant. fn struct_pattern( path: Path, fields: &[Field], field_ident: impl for<'b> Fn(&'b Field) -> &'b Ident, ) -> Pat { Pat::Struct(PatStruct { attrs: Vec::new(), qself: None, path, brace_token: Brace::default(), fields: fields .iter() .map(|field| FieldPat { attrs: Vec::new(), member: field.to_member(), colon_token: Some(::default()), pat: Box::new(Pat::Ident(PatIdent { attrs: Vec::new(), by_ref: Some(::default()), mutability: None, ident: field_ident(field).clone(), subpat: None, })), }) .collect(), rest: None, }) } /// Destructuring pattern in a match arm for this item or variant. fn tuple_pattern( path: Path, fields: &[Field], field_ident: impl for<'b> Fn(&'b Field) -> &'b Ident, ) -> Pat { Pat::TupleStruct(PatTupleStruct { attrs: Vec::new(), qself: None, path, paren_token: Paren::default(), elems: fields .iter() .map(|field| { Pat::Ident(PatIdent { attrs: Vec::new(), by_ref: Some(::default()), mutability: None, ident: field_ident(field).clone(), subpat: None, }) }) .collect(), }) } /// Returns a [Pattern](Pat) to use in a match arm to destructure `self` as /// mutable. #[cfg(feature = "zeroize")] pub fn self_pattern_mut(&self) -> Pat { let mut pattern = self.self_pattern.clone(); match &mut pattern { Pat::Struct(pattern) => { for field in &mut pattern.fields { if let Pat::Ident(pattern) = &mut *field.pat { pattern.mutability = Some(::default()); } else { unreachable!("unexpected pattern") } } } Pat::TupleStruct(pattern) => { for field in &mut pattern.elems { if let Pat::Ident(pattern) = &mut *field { pattern.mutability = Some(::default()); } else { unreachable!("unexpected pattern") } } } _ => unreachable!("unexpected pattern"), } pattern } /// Returns `true` if any field is skipped with that [`Trait`]. pub fn any_skip_trait(&self, trait_: Trait) -> bool { self.fields.iter().any(|field| field.skip(trait_)) } /// Returns `true` if all fields are skipped with that [`Trait`]. pub fn skip(&self, trait_: Trait) -> bool { self.fields.iter().all(|field| field.skip(trait_)) } } derive-where-1.2.7/src/data.rs000064400000000000000000000224461046102023000142770ustar 00000000000000//! Types holding data of items. mod field; mod fields; use proc_macro2::Span; use syn::{Expr, FieldsNamed, Ident, Pat, PatPath, Path, Result, Variant}; pub use self::{ field::{Field, Member}, fields::Fields, }; use crate::{util, Default, DeriveWhere, Either, Error, Incomparable, Skip, Trait, VariantAttr}; /// Holds all relevant data of a struct, union or variant. #[cfg_attr(test, derive(Debug))] pub struct Data<'a> { /// [`Skip`] attribute of this struct, union or variant. skip_inner: Skip, /// [`Incomparable`] attribute of this struct, union or variant. pub incomparable: Incomparable, /// [`struct@Ident`] of this struct, union or variant, used for implementing /// [`Debug`](std::fmt::Debug). pub ident: &'a Ident, /// [`Path`] of this struct, union or variant, used to construct new /// instances of that item, for example when implementing [`Clone`]. pub path: Path, /// [Type](DataType) of this struct, union or variant. pub type_: DataType<'a>, /// Discriminant of this variant. pub discriminant: Option<&'a Expr>, } /// Type of this data. #[cfg_attr(test, derive(Debug))] pub enum DataType<'a> { /// Struct. Struct(Fields<'a>), /// Tuple. Tuple(Fields<'a>), /// Union. Union(Fields<'a>), /// Variant. Variant { /// [`struct@Default`] attribute of this variant. default: Default, /// [Type](VariantType) of this variant. type_: VariantType<'a>, }, /// Unit. Unit(Pat), } /// Type of [`Data`]. #[cfg_attr(test, derive(Debug))] pub enum VariantType<'a> { /// Struct variant. Struct(Fields<'a>), /// Tuple variant. Tuple(Fields<'a>), /// Unit variant. Unit(Pat), } /// Type to enable simplified matching. pub enum SimpleType<'a> { /// Struct, struct variant. Struct(&'a Fields<'a>), /// Tuple struct or tuple variant. Tuple(&'a Fields<'a>), /// Union. Union(&'a Fields<'a>), /// Unit variant. Unit(&'a Pat), } impl<'a> Data<'a> { /// Create [`Data`]s from [`syn::Fields`] of a struct. pub fn from_struct( span: Span, derive_wheres: &[DeriveWhere], skip_inner: Skip, incomparable: Incomparable, ident: &'a Ident, fields: &'a syn::Fields, ) -> Result { let path = util::path_from_idents(&[ident]); match fields { syn::Fields::Named(fields) => { if fields.named.is_empty() && incomparable.0.is_none() { Err(Error::item_empty(span)) } else { let fields = Fields::from_named(derive_wheres, &skip_inner, path.clone(), fields)?; Ok(Self { skip_inner, incomparable, ident, path, type_: DataType::Struct(fields), discriminant: None, }) } } syn::Fields::Unnamed(fields) => { if fields.unnamed.is_empty() && incomparable.0.is_none() { Err(Error::item_empty(span)) } else { let fields = Fields::from_unnamed(derive_wheres, &skip_inner, path.clone(), fields)?; Ok(Self { skip_inner, incomparable, ident, path, type_: DataType::Tuple(fields), discriminant: None, }) } } syn::Fields::Unit if incomparable.0.is_some() => Ok(Self { skip_inner, incomparable, ident, path: path.clone(), type_: DataType::Unit(Pat::Path(PatPath { attrs: Vec::new(), qself: None, path, })), discriminant: None, }), syn::Fields::Unit => Err(Error::item_empty(span)), } } /// Create [`Data`]s from [`FieldsNamed`] of an union. pub fn from_union( span: Span, derive_wheres: &[DeriveWhere], skip_inner: Skip, incomparable: Incomparable, ident: &'a Ident, fields: &'a FieldsNamed, ) -> Result { if fields.named.is_empty() && incomparable.0.is_none() { Err(Error::item_empty(span)) } else { let path = util::path_from_idents(&[ident]); let fields = Fields::from_named(derive_wheres, &skip_inner, path.clone(), fields)?; Ok(Self { skip_inner, incomparable, ident, path, type_: DataType::Union(fields), discriminant: None, }) } } /// Create [`Data`]s from [`syn::Fields`] of a variant. pub fn from_variant( item_ident: &'a Ident, derive_wheres: &[DeriveWhere], variant: &'a Variant, ) -> Result { // Parse `Attribute`s on variant. let VariantAttr { default, skip_inner, incomparable, } = VariantAttr::from_attrs(&variant.attrs, derive_wheres, variant)?; let path = util::path_from_idents(&[item_ident, &variant.ident]); match &variant.fields { syn::Fields::Named(fields) => { let fields = Fields::from_named(derive_wheres, &skip_inner, path.clone(), fields)?; Ok(Self { skip_inner, incomparable, ident: &variant.ident, path, type_: DataType::Variant { default, type_: VariantType::Struct(fields), }, discriminant: variant.discriminant.as_ref().map(|(_, expr)| expr), }) } syn::Fields::Unnamed(fields) => { let fields = Fields::from_unnamed(derive_wheres, &skip_inner, path.clone(), fields)?; Ok(Self { skip_inner, incomparable, ident: &variant.ident, path, type_: DataType::Variant { default, type_: VariantType::Tuple(fields), }, discriminant: variant.discriminant.as_ref().map(|(_, expr)| expr), }) } syn::Fields::Unit => { let pattern = Pat::Path(PatPath { attrs: Vec::new(), qself: None, path: path.clone(), }); Ok(Self { skip_inner, incomparable, ident: &variant.ident, path, type_: DataType::Variant { default, type_: VariantType::Unit(pattern), }, discriminant: variant.discriminant.as_ref().map(|(_, expr)| expr), }) } } } /// Returns the [`Fields`] of this [`Data`]. If [`Data`] is a unit variant /// or struct returns [`Pat`] instead. pub fn fields(&self) -> Either<&Fields, &Pat> { match &self.type_ { DataType::Struct(fields) | DataType::Tuple(fields) | DataType::Union(fields) | DataType::Variant { type_: VariantType::Struct(fields), .. } | DataType::Variant { type_: VariantType::Tuple(fields), .. } => Either::Left(fields), DataType::Unit(pattern) | DataType::Variant { type_: VariantType::Unit(pattern), .. } => Either::Right(pattern), } } /// Returns the destructuring `self` pattern of this [`Data`]. pub fn self_pattern(&self) -> &Pat { match self.fields() { Either::Left(fields) => &fields.self_pattern, Either::Right(pattern) => pattern, } } /// Returns `true` if this variant is marked as the [`struct@Default`]. If /// not a variant, always returns `true`. pub fn is_default(&self) -> bool { match self.type_ { DataType::Variant { default, .. } => default.0.is_some(), _ => true, } } /// Returns `true` if this item or variant is marked as [`Incomparable`]. pub fn is_incomparable(&self) -> bool { self.incomparable.0.is_some() } /// Returns [`Some`] if this variant has a [`struct@Default`]. If /// not a variant, always returns [`None`]. pub fn default_span(&self) -> Option { match &self.type_ { DataType::Variant { default, .. } => default.0, _ => None, } } /// Returns `true` if this [`Data`] has no [`Fields`]. pub fn is_empty(&self, trait_: Trait) -> bool { self.iter_fields(trait_).count() == 0 } /// Returns `true` if a field is skipped with that [`Trait`]. pub fn any_skip_trait(&self, trait_: Trait) -> bool { self.skip_inner.trait_skipped(trait_) || match self.fields() { Either::Left(fields) => fields.any_skip_trait(trait_), Either::Right(_) => false, } } /// Returns `true` if all fields are skipped with that [`Trait`]. fn skip(&self, trait_: Trait) -> bool { self.skip_inner.trait_skipped(trait_) || match self.fields() { Either::Left(fields) => fields.skip(trait_), Either::Right(_) => false, } } /// Return a [`SimpleType`]. pub fn simple_type(&self) -> SimpleType { match &self.type_ { DataType::Struct(fields) | DataType::Variant { type_: VariantType::Struct(fields), .. } => SimpleType::Struct(fields), DataType::Tuple(fields) | DataType::Variant { type_: VariantType::Tuple(fields), .. } => SimpleType::Tuple(fields), DataType::Unit(pattern) | DataType::Variant { type_: VariantType::Unit(pattern), .. } => SimpleType::Unit(pattern), DataType::Union(fields) => SimpleType::Union(fields), } } /// Returns an [`Iterator`] over [`Field`]s. pub fn iter_fields( &self, trait_: Trait, ) -> impl '_ + Iterator + DoubleEndedIterator { if self.skip(trait_) { [].iter() } else { match self.fields() { Either::Left(fields) => fields.fields.iter(), Either::Right(_) => [].iter(), } } .filter(move |field| !field.skip(trait_)) } /// Returns an [`Iterator`] over [`Member`]s. pub fn iter_field_ident(&self, trait_: Trait) -> impl '_ + Iterator { self.iter_fields(trait_).map(|field| &field.member) } /// Returns an [`Iterator`] over [`struct@Ident`]s used as temporary /// variables for destructuring `self`. pub fn iter_self_ident( &self, trait_: Trait, ) -> impl Iterator + DoubleEndedIterator { self.iter_fields(trait_).map(|field| &field.self_ident) } /// Returns an [`Iterator`] over [`struct@Ident`]s used as temporary /// variables for destructuring `other`. pub fn iter_other_ident( &self, trait_: Trait, ) -> impl Iterator + DoubleEndedIterator { self.iter_fields(trait_).map(|field| &field.other_ident) } } derive-where-1.2.7/src/error.rs000064400000000000000000000221661046102023000145160ustar 00000000000000//! Error type. use proc_macro2::Span; /// Easy API to create all [`syn::Error`] messages in this crate. pub struct Error; impl Error { /// `derive_where` was already applied on this item before. pub fn visited(span: Span) -> syn::Error { syn::Error::new( span, "`#[derive_where(..)` was already applied to this item before, this occurs when using \ a qualified path for any `#[derive_where(..)`s except the first", ) } /// Unnecessary `crate` option because it is equal to the default. pub fn path_unnecessary(span: Span, default: &str) -> syn::Error { syn::Error::new( span, format!( "unnecessary path qualification, `{}` is used by default", default ), ) } /// The `crate` option was defined together with traits. pub fn crate_(span: Span) -> syn::Error { syn::Error::new( span, "the `crate` option has to be defined in it's own `#[derive_where(..)` attribute", ) } /// No `derive_where` with [`Trait`](crate::Trait) found. pub fn none(span: Span) -> syn::Error { syn::Error::new( span, "no traits found to implement, use `#[derive_where(..)` to specify some", ) } /// Unsupported empty `derive_where` on item. pub fn empty(span: Span) -> syn::Error { syn::Error::new(span, "empty `derive_where` found") } /// Item has no use-case because it's covered by standard `#[derive(..)]`. pub fn use_case(span: Span) -> syn::Error { syn::Error::new( span, "this can be handled by standard `#[derive(..)]`, use a `skip` or `incomparable` \ attribute, implement `Default` on an enum, or different generic type parameters", ) } /// Unsupported empty item. pub fn item_empty(span: Span) -> syn::Error { syn::Error::new( span, "derive-where doesn't support empty items, as this can already be handled by standard \ `#[derive(..)]`", ) } /// Unsupported trait for union. pub fn union(span: Span) -> syn::Error { syn::Error::new( span, "traits other then `Clone` and `Copy` aren't supported by unions", ) } /// Unsupported option in attribute. #[cfg(feature = "zeroize")] pub fn option_trait(span: Span, attribute: &str) -> syn::Error { syn::Error::new(span, format!("`{}` doesn't support this option", attribute)) } /// Unsupported option in attribute. pub fn option(span: Span) -> syn::Error { syn::Error::new(span, "unknown option") } /// Unsupported options in attribute. pub fn options(span: Span, trait_: &str) -> syn::Error { syn::Error::new(span, format!("`{}` doesn't support any options", trait_)) } /// Invalid syntax for an option in attribute. pub fn option_syntax(span: Span) -> syn::Error { syn::Error::new(span, "unexpected option syntax") } /// Unsupported empty attribute option. pub fn option_empty(span: Span) -> syn::Error { syn::Error::new(span, "empty attribute option found") } /// Missing sub-option for an option. #[cfg(feature = "zeroize")] pub fn option_required(span: Span, option: &str) -> syn::Error { syn::Error::new(span, format!("`{}` requires an option", option)) } /// Duplicate option in attribute. pub fn option_duplicate(span: Span, option: &str) -> syn::Error { syn::Error::new(span, format!("duplicate `{}` option", option)) } /// Unsupported `skip_inner` on an enum. pub fn option_enum_skip_inner(span: Span) -> syn::Error { syn::Error::new( span, "enums don't support `skip_inner`, use it on a variant instead", ) } /// Unexpected `skip` on a field when `skip_inner` is already used on the /// item or variant with this [`Trait`](crate::Trait). pub fn option_skip_inner(span: Span) -> syn::Error { syn::Error::new( span, "unexpected `skip` on a field when parent already uses `skip_inner` with this trait", ) } /// Unsupported `skip_inner` on empty variant. pub fn option_skip_empty(span: Span) -> syn::Error { syn::Error::new(span, "no fields to skip") } /// Unexpected constrained field skipping when configured to skip all traits /// anyway. pub fn option_skip_all(span: Span) -> syn::Error { syn::Error::new( span, "unexpected constraint on `skip` when unconstrained `skip` already used", ) } /// Duplicate trait constraint on `skip`. pub fn option_skip_duplicate(span: Span, trait_: &str) -> syn::Error { syn::Error::new(span, format!("duplicate `{}` constraint on `skip`", trait_)) } /// No trait that can be skipped is being implemented. pub fn option_skip_no_trait(span: Span) -> syn::Error { syn::Error::new(span, "no trait that can be skipped is being implemented") } /// Trait to be skipped isn't being implemented pub fn option_skip_trait(span: Span) -> syn::Error { syn::Error::new(span, "trait to be skipped isn't being implemented") } /// Unsupported [`SkipGroup`](crate::SkipGroup). pub fn skip_group(span: Span) -> syn::Error { syn::Error::new( span, format!( "unsupported skip group, expected one of {}", Self::skip_group_list() ), ) } /// Invalid value for the `derive_where` or `Zeroize` `crate` option. pub fn path(span: Span, parse_error: syn::Error) -> syn::Error { syn::Error::new(span, format!("expected path, {}", parse_error)) } /// Unsupported [`Trait`](crate::Trait). pub fn trait_(span: Span) -> syn::Error { syn::Error::new( span, format!("unsupported trait, expected one of {}", Self::trait_list()), ) } /// Invalid syntax for a [`Trait`](crate::Trait). pub fn trait_syntax(span: Span) -> syn::Error { syn::Error::new( span, format!( "unsupported trait syntax, expected one of {}", Self::trait_list() ), ) } /// Invalid delimiter in `derive_where` attribute for /// [`Trait`](crate::Trait)s. pub fn derive_where_delimiter(span: Span) -> syn::Error { syn::Error::new(span, "expected `;` or `,") } /// Unsupported predicate type in `derive_where` attribute for where clause. pub fn generic(span: Span) -> syn::Error { syn::Error::new(span, "only type predicates are supported") } /// Invalid syntax in `derive_where` attribute for generics. pub fn generic_syntax(span: Span, parse_error: syn::Error) -> syn::Error { syn::Error::new(span, format!("expected type to bind to, {}", parse_error)) } /// Duplicate trait with the same bound. pub fn trait_duplicate(span: Span) -> syn::Error { syn::Error::new(span, "duplicate trait with the same bound") } /// Unknown `repr`. pub fn repr_unknown(span: Span) -> syn::Error { syn::Error::new(span, "found unknown representation") } /// Invalid enum with non-empty variants and custom discriminants without an /// integer representation. pub fn repr_discriminant_invalid(span: Span) -> syn::Error { syn::Error::new( span, "enums with non-empty variants and custom discriminants require a integer \ representation", ) } /// Unsupported default option if [`Default`] isn't implemented. pub fn default(span: Span) -> syn::Error { syn::Error::new( span, "`default` is only supported if `Default` is being implemented", ) } /// Missing `default` option on a variant when [`Default`] is implemented /// for an enum. pub fn default_missing(span: Span) -> syn::Error { syn::Error::new( span, "required `default` option on a variant if `Default` is being implemented", ) } /// Duplicate `default` option on a variant. pub fn default_duplicate(span: Span) -> syn::Error { syn::Error::new(span, "multiple `default` options in enum") } /// Unsupported `incomparable` option if [`PartialEq`] or [`PartialOrd`] /// isn't implemented. pub fn incomparable(span: Span) -> syn::Error { syn::Error::new( span, "`incomparable` is only supported if `PartialEq` or `PartialOrd` is being implemented", ) } /// Unsupported `incomparable` option if [`Eq`] or [`Ord`] is implemented. pub fn non_partial_incomparable(span: Span) -> syn::Error { syn::Error::new( span, "`incomparable` is not supported if `Eq` or `Ord` is being implemented", ) } /// Unsupported `incomparable` option on both enum and variant. pub fn incomparable_on_item_and_variant(item: Span, variant: Span) -> syn::Error { syn::Error::new( // This will only produce joint spans on nightly. item.join(variant).unwrap_or(item), "`incomparable` cannot be specified on both item and variant", ) } /// List of available [`Trait`](crate::Trait)s. fn trait_list() -> String { [ "Clone", "Copy", "Debug", "Default", "Eq", "Hash", "Ord", "PartialEq", "PartialOrd", #[cfg(feature = "zeroize")] "Zeroize", #[cfg(feature = "zeroize")] "ZeroizeOnDrop", ] .join(", ") } /// List of available [`SkipGroup`](crate::SkipGroup)s. fn skip_group_list() -> String { [ "Debug", "EqHashOrd", "Hash", #[cfg(feature = "zeroize")] "Zeroize", ] .join(", ") } /// Unsupported `Zeroize` option if [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html) isn't implemented. #[cfg(feature = "zeroize")] pub fn zeroize(span: Span) -> syn::Error { syn::Error::new( span, "`Zeroize` option is only supported if `Zeroize` is being implemented", ) } /// Deprecated use of `Zeroize(drop)`. #[cfg(feature = "zeroize")] pub fn deprecated_zeroize_drop(span: Span) -> syn::Error { syn::Error::new( span, "`Zeroize(drop)` is deprecated, use `ZeroizeOnDrop` instead", ) } } derive-where-1.2.7/src/input.rs000064400000000000000000000136631046102023000145260ustar 00000000000000//! Parses [`DeriveInput`] into something more useful. use proc_macro2::Span; use syn::{DeriveInput, GenericParam, Generics, ImplGenerics, Result, TypeGenerics, WhereClause}; #[cfg(feature = "zeroize")] use crate::DeriveTrait; use crate::{Data, DeriveWhere, Discriminant, Either, Error, Item, ItemAttr, Trait}; /// Parsed input. pub struct Input<'a> { /// `derive_where` attributes on the item. pub derive_wheres: Vec, /// Generics necessary to define for an `impl`. pub generics: SplitGenerics<'a>, /// Fields or variants of this item. pub item: Item<'a>, } impl<'a> Input<'a> { /// Create [`Input`] from `proc_macro_derive` parameter. pub fn from_input( span: Span, DeriveInput { attrs, ident, generics, data, .. }: &'a DeriveInput, ) -> Result { // Parse `Attribute`s on item. let ItemAttr { skip_inner, derive_wheres, incomparable, } = ItemAttr::from_attrs(span, data, attrs)?; // Find if `incomparable` is specified on any item/variant. let mut found_incomparable = incomparable.0.is_some(); // Extract fields and variants of this item. let item = match &data { syn::Data::Struct(data) => Data::from_struct( span, &derive_wheres, skip_inner, incomparable, ident, &data.fields, ) .map(Item::Item)?, syn::Data::Enum(data) => { let discriminant = Discriminant::parse(attrs, &data.variants)?; let variants = data .variants .iter() .map(|variant| Data::from_variant(ident, &derive_wheres, variant)) .collect::>>()?; // Find if a default option is specified on a variant. let mut found_default = false; // While searching for a default option, check for duplicates. for variant in &variants { if let Some(span) = variant.default_span() { if found_default { return Err(Error::default_duplicate(span)); } else { found_default = true; } } if let (Some(item), Some(variant)) = (incomparable.0, variant.incomparable.0) { return Err(Error::incomparable_on_item_and_variant(item, variant)); } found_incomparable |= variant.is_incomparable(); } // Make sure a variant has the `option` attribute if `Default` is being // implemented. if !found_default && derive_wheres .iter() .any(|derive_where| derive_where.contains(Trait::Default)) { return Err(Error::default_missing(span)); } // Empty enums aren't allowed unless they implement `Default` or are // incomparable. if !found_default && !found_incomparable && variants.iter().all(|variant| match variant.fields() { Either::Left(fields) => fields.fields.is_empty(), Either::Right(_) => true, }) { return Err(Error::item_empty(span)); } Item::Enum { discriminant, ident, variants, incomparable, } } syn::Data::Union(data) => Data::from_union( span, &derive_wheres, skip_inner, incomparable, ident, &data.fields, ) .map(Item::Item)?, }; // Don't allow generic constraints be the same as generics on item unless there // is a use-case for it. // Count number of generic type parameters. let generics_len = generics .params .iter() .filter(|generic_param| match generic_param { GenericParam::Type(_) => true, GenericParam::Lifetime(_) | GenericParam::Const(_) => false, }) .count(); 'outer: for derive_where in &derive_wheres { // No point in starting to compare both if not even the length is the same. // This can be easily circumvented by doing the following: // `#[derive_where(..; T: Clone)]`, or `#[derive_where(..; T, T)]`, which // apparently is valid Rust syntax: `where T: Clone, T: Clone`, we are only here // to help though. if derive_where.generics.len() != generics_len { continue; } // No point in starting to check if there is no use-case if a custom bound was // used, which is a use-case. if derive_where.any_custom_bound() { continue; } // Check if every generic type parameter present on the item is defined in this // `DeriveWhere`. for generic_param in &generics.params { // Only check generic type parameters. if let GenericParam::Type(type_param) = generic_param { if !derive_where.has_type_param(&type_param.ident) { continue 'outer; } } } // The `for` loop should short-circuit to the `'outer` loop if not all generic // type parameters were found. // Don't allow no use-case compared to std `derive`. for (span, trait_) in derive_where.spans.iter().zip(&derive_where.traits) { // `Default` is used on an enum. if trait_ == Trait::Default && item.is_enum() { continue; } // Any field is skipped with a corresponding `Trait`. if item.any_skip_trait(**trait_) { continue; } // Any variant is marked as incomparable. if found_incomparable { continue; } #[cfg(feature = "zeroize")] { // `Zeroize(crate = ..)` or `ZeroizeOnDrop(crate = ..)` is used. if let DeriveTrait::Zeroize { crate_: Some(_) } | DeriveTrait::ZeroizeOnDrop { crate_: Some(_) } = *trait_ { continue; } // `Zeroize(fqs)` is used on any field. if trait_ == Trait::Zeroize && item.any_fqs() { continue; } } return Err(Error::use_case(*span)); } } let generics = SplitGenerics::new(generics); Ok(Self { derive_wheres, generics, item, }) } } /// Stores output of [`Generics::split_for_impl()`]. pub struct SplitGenerics<'a> { /// Necessary generic definitions. pub imp: ImplGenerics<'a>, /// Generics on the type itself. pub ty: TypeGenerics<'a>, /// `where` clause. pub where_clause: Option<&'a WhereClause>, } impl<'a> SplitGenerics<'a> { /// Creates a [`SplitGenerics`] from [`Generics`]. fn new(generics: &'a Generics) -> Self { let (imp, ty, where_clause) = generics.split_for_impl(); SplitGenerics { imp, ty, where_clause, } } } derive-where-1.2.7/src/item.rs000064400000000000000000000136141046102023000143210ustar 00000000000000//! Intermediate representation of item data. use proc_macro2::{Ident, Span, TokenStream, TokenTree}; use quote::ToTokens; use syn::{punctuated::Punctuated, spanned::Spanned, Attribute, Meta, Result, Token, Variant}; use crate::{Data, Error, Incomparable, Trait}; /// Fields or variants of an item. #[cfg_attr(test, derive(Debug))] #[allow(clippy::large_enum_variant)] pub enum Item<'a> { /// Enum. Enum { /// Type of discriminant used. discriminant: Discriminant, /// [`struct@Ident`] of this enum. ident: &'a Ident, /// [`Incomparable`] attribute of this enum. incomparable: Incomparable, /// Variants of this enum. variants: Vec>, }, /// Struct, tuple struct or union. Item(Data<'a>), } impl Item<'_> { /// Returns [`struct@Ident`] of this [`Item`]. pub fn ident(&self) -> &Ident { match self { Item::Item(data) => data.ident, Item::Enum { ident, .. } => ident, } } /// Returns `true` if this [`Item`] if an enum. pub fn is_enum(&self) -> bool { match self { Item::Enum { .. } => true, Item::Item(_) => false, } } /// Returns `true` if any field is skipped with that [`Trait`]. pub fn any_skip_trait(&self, trait_: Trait) -> bool { match self { Item::Item(data) => data.any_skip_trait(trait_), Item::Enum { variants, .. } => variants.iter().any(|data| data.any_skip_trait(trait_)), } } /// Returns `true` if any field uses `Zeroize(fqs)`. #[cfg(feature = "zeroize")] pub fn any_fqs(&self) -> bool { use crate::Either; match self { Item::Item(data) => match data.fields() { Either::Left(fields) => fields.fields.iter().any(|field| field.attr.zeroize_fqs.0), Either::Right(_) => false, }, Item::Enum { variants, .. } => variants.iter().any(|data| match data.fields() { Either::Left(fields) => fields.fields.iter().any(|field| field.attr.zeroize_fqs.0), Either::Right(_) => false, }), } } /// Returns `true` if all [`Fields`](crate::data::Fields) are empty for this /// [`Trait`]. pub fn is_empty(&self, trait_: Trait) -> bool { match self { Item::Enum { variants, .. } => variants.iter().all(|data| data.is_empty(trait_)), Item::Item(data) => data.is_empty(trait_), } } /// Returns `true` if the item is incomparable or all (≥1) variants are /// incomparable. pub fn is_incomparable(&self) -> bool { match self { Item::Enum { variants, incomparable, .. } => { incomparable.0.is_some() || !variants.is_empty() && variants.iter().all(Data::is_incomparable) } Item::Item(data) => data.is_incomparable(), } } } /// Type of discriminant used. #[derive(Clone, Copy)] #[cfg_attr(test, derive(Debug))] pub enum Discriminant { /// The enum has only a single variant. Single, /// The enum has only unit variants. Unit, /// The enum has a non-unit variant. Data, /// The enum has only unit variants. UnitRepr(Representation), /// The enum has a non-unit variant. DataRepr(Representation), } impl Discriminant { /// Parse the representation of an item. pub fn parse(attrs: &[Attribute], variants: &Punctuated) -> Result { if variants.len() == 1 { return Ok(Self::Single); } let mut has_repr = None; for attr in attrs { if attr.path().is_ident("repr") { if let Meta::List(list) = &attr.meta { let list = list.parse_args_with(Punctuated::::parse_terminated)?; for ident in list { if let Some(repr) = Representation::parse(&ident) { has_repr = Some(repr); break; } else if ident != "C" && ident != "Rust" && ident != "align" { return Err(Error::repr_unknown(ident.span())); } } } else { unreachable!("found invalid `repr` attribute") } } } let is_unit = variants.iter().all(|variant| variant.fields.is_empty()); Ok(if let Some(repr) = has_repr { if is_unit { Self::UnitRepr(repr) } else { Self::DataRepr(repr) } } else if is_unit { Self::Unit } else { let discriminant = variants .iter() .find_map(|variant| variant.discriminant.as_ref()); if let Some(discriminant) = discriminant { return Err(Error::repr_discriminant_invalid(discriminant.1.span())); } Self::Data }) } } /// The type used to represent an enum. #[derive(Clone, Copy)] #[cfg_attr(test, derive(Debug))] pub enum Representation { /// [`u8`]. U8, /// [`u16`]. U16, /// [`u32`]. U32, /// [`u64`]. U64, /// [`u128`]. U128, /// [`usize`]. USize, /// [`i8`]. I8, /// [`i16`]. I16, /// [`i32`]. I32, /// [`i64`]. I64, /// [`i128`]. I128, /// [`isize`]. ISize, } impl Representation { /// Parse an [`struct@Ident`] to a valid representation if it is. fn parse(ident: &Ident) -> Option { Some(if ident == "u8" { Self::U8 } else if ident == "u16" { Self::U16 } else if ident == "u32" { Self::U32 } else if ident == "u64" { Self::U64 } else if ident == "u128" { Self::U128 } else if ident == "usize" { Self::USize } else if ident == "i8" { Self::I8 } else if ident == "i16" { Self::I16 } else if ident == "i32" { Self::I32 } else if ident == "i64" { Self::I64 } else if ident == "i128" { Self::I128 } else if ident == "isize" { Self::ISize } else { return None; }) } /// Convert this [`Representation`] to a [`TokenStream`]. pub fn to_token(self) -> TokenStream { let ident = match self { Representation::U8 => "u8", Representation::U16 => "u16", Representation::U32 => "u32", Representation::U64 => "u64", Representation::U128 => "u128", Representation::USize => "usize", Representation::I8 => "i8", Representation::I16 => "i16", Representation::I32 => "i32", Representation::I64 => "i64", Representation::I128 => "i128", Representation::ISize => "isize", }; TokenTree::from(Ident::new(ident, Span::call_site())).into() } } impl ToTokens for Representation { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.extend(self.to_token()); } } derive-where-1.2.7/src/lib.rs000075500000000000000000000565651046102023000141500ustar 00000000000000#![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(PhantomData); //! ``` //! //! 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(PhantomData); //! ``` //! //! 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(PhantomData); //! ``` //! //! 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, PhantomData); //! ``` //! //! 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(PhantomData); //! ``` //! //! 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::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(PhantomData, PhantomData); //! ``` //! //! ## 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 { //! #[derive_where(default)] //! A(PhantomData), //! } //! ``` //! //! ## 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(#[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); //! //! assert_eq!(format!("{:?}", StructExample(42)), "StructExample"); //! assert_eq!(StructExample(42), StructExample(0)); //! //! #[derive_where(Debug, PartialEq)] //! enum EnumExample { //! #[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(i32, PhantomData); //! //! 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::()); //! # } //! ``` //! //! ## 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 ) //! - MIT license ([LICENSE-MIT] or ) //! //! 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::(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 { 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::() { 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::(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::() .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) { 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 } derive-where-1.2.7/src/test/basic.rs000064400000000000000000000264531046102023000154300ustar 00000000000000use quote::quote; use syn::Result; use super::test_derive; #[test] fn struct_() -> Result<()> { test_derive( quote! { #[derive_where(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] struct Test { field: std::marker::PhantomData, } }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test { #[inline] fn clone(&self) -> Self { *self } } #[automatically_derived] impl ::core::marker::Copy for Test { } #[automatically_derived] impl ::core::fmt::Debug for Test { fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { Test { field: ref __field_field } => { let mut __builder = ::core::fmt::Formatter::debug_struct(__f, "Test"); ::core::fmt::DebugStruct::field(&mut __builder, "field", __field_field); ::core::fmt::DebugStruct::finish(&mut __builder) } } } } #[automatically_derived] impl ::core::default::Default for Test { fn default() -> Self { Test { field: ::core::default::Default::default() } } } #[automatically_derived] impl ::core::cmp::Eq for Test { #[inline] fn assert_receiver_is_total_eq(&self) { struct __AssertEq<__T: ::core::cmp::Eq + ?::core::marker::Sized>(::core::marker::PhantomData<__T>); // For some reason the comparison fails without the extra space at the end. let _: __AssertEq >; } } #[automatically_derived] impl ::core::hash::Hash for Test { fn hash<__H: ::core::hash::Hasher>(&self, __state: &mut __H) { match self { Test { field: ref __field_field } => { ::core::hash::Hash::hash(__field_field, __state); } } } } #[automatically_derived] impl ::core::cmp::Ord for Test { #[inline] fn cmp(&self, __other: &Self) -> ::core::cmp::Ordering { match (self, __other) { (Test { field: ref __field_field }, Test { field: ref __other_field_field }) => match ::core::cmp::Ord::cmp(__field_field, __other_field_field) { ::core::cmp::Ordering::Equal => ::core::cmp::Ordering::Equal, __cmp => __cmp, }, } } } #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { match (self, __other) { (Test { field: ref __field_field }, Test { field: ref __other_field_field }) => true && ::core::cmp::PartialEq::eq(__field_field, __other_field_field), } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { ::core::option::Option::Some(::core::cmp::Ord::cmp(self, __other)) } } }, ) } #[test] fn tuple() -> Result<()> { test_derive( quote! { #[derive_where(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] struct Test(std::marker::PhantomData); }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test { #[inline] fn clone(&self) -> Self { *self } } #[automatically_derived] impl ::core::marker::Copy for Test { } #[automatically_derived] impl ::core::fmt::Debug for Test { fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { Test(ref __field_0) => { let mut __builder = ::core::fmt::Formatter::debug_tuple(__f, "Test"); ::core::fmt::DebugTuple::field(&mut __builder, __field_0); ::core::fmt::DebugTuple::finish(&mut __builder) } } } } #[automatically_derived] impl ::core::default::Default for Test { fn default() -> Self { Test(::core::default::Default::default()) } } #[automatically_derived] impl ::core::cmp::Eq for Test { #[inline] fn assert_receiver_is_total_eq(&self) { struct __AssertEq<__T: ::core::cmp::Eq + ?::core::marker::Sized>(::core::marker::PhantomData<__T>); // For some reason the comparison fails without the extra space at the end. let _: __AssertEq >; } } #[automatically_derived] impl ::core::hash::Hash for Test { fn hash<__H: ::core::hash::Hasher>(&self, __state: &mut __H) { match self { Test(ref __field_0) => { ::core::hash::Hash::hash(__field_0, __state); } } } } #[automatically_derived] impl ::core::cmp::Ord for Test { #[inline] fn cmp(&self, __other: &Self) -> ::core::cmp::Ordering { match (self, __other) { (Test(ref __field_0), Test(ref __other_field_0)) => match ::core::cmp::Ord::cmp(__field_0, __other_field_0) { ::core::cmp::Ordering::Equal => ::core::cmp::Ordering::Equal, __cmp => __cmp, }, } } } #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { match (self, __other) { (Test(ref __field_0), Test(ref __other_field_0)) => true && ::core::cmp::PartialEq::eq(__field_0, __other_field_0), } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { ::core::option::Option::Some(::core::cmp::Ord::cmp(self, __other)) } } }, ) } #[test] fn enum_() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let ord = quote! { ::core::cmp::Ord::cmp(&__self_disc, &__other_disc) }; #[cfg(not(feature = "nightly"))] let ord = quote! { const fn __discriminant(__this: &Test) -> isize { match __this { Test::A { field: ref __field_field } => 0, Test::B { } => 1, Test::C(ref __field_0) => 2, Test::D() => 3, Test::E => 4 } } ::core::cmp::Ord::cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] enum Test { A { field: std::marker::PhantomData}, B { }, C(std::marker::PhantomData), D(), #[derive_where(default)] E, } }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test { #[inline] fn clone(&self) -> Self { *self } } #[automatically_derived] impl ::core::marker::Copy for Test { } #[automatically_derived] impl ::core::fmt::Debug for Test { fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { Test::A { field: ref __field_field } => { let mut __builder = ::core::fmt::Formatter::debug_struct(__f, "A"); ::core::fmt::DebugStruct::field(&mut __builder, "field", __field_field); ::core::fmt::DebugStruct::finish(&mut __builder) } Test::B { } => { let mut __builder = ::core::fmt::Formatter::debug_struct(__f, "B"); ::core::fmt::DebugStruct::finish(&mut __builder) } Test::C(ref __field_0) => { let mut __builder = ::core::fmt::Formatter::debug_tuple(__f, "C"); ::core::fmt::DebugTuple::field(&mut __builder, __field_0); ::core::fmt::DebugTuple::finish(&mut __builder) } Test::D() => { let mut __builder = ::core::fmt::Formatter::debug_tuple(__f, "D"); ::core::fmt::DebugTuple::finish(&mut __builder) } Test::E => ::core::fmt::Formatter::write_str(__f, "E"), } } } #[automatically_derived] impl ::core::default::Default for Test { fn default() -> Self { Test::E } } #[automatically_derived] impl ::core::cmp::Eq for Test { #[inline] fn assert_receiver_is_total_eq(&self) { struct __AssertEq<__T: ::core::cmp::Eq + ?::core::marker::Sized>(::core::marker::PhantomData<__T>); // For some reason the comparison fails without the extra space at the end. let _: __AssertEq >; let _: __AssertEq >; } } #[automatically_derived] impl ::core::hash::Hash for Test { fn hash<__H: ::core::hash::Hasher>(&self, __state: &mut __H) { match self { Test::A { field: ref __field_field } => { ::core::hash::Hash::hash(&::core::mem::discriminant(self), __state); ::core::hash::Hash::hash(__field_field, __state); } Test::B { } => { ::core::hash::Hash::hash(&::core::mem::discriminant(self), __state); } Test::C(ref __field_0) => { ::core::hash::Hash::hash(&::core::mem::discriminant(self), __state); ::core::hash::Hash::hash(__field_0, __state); } Test::D() => { ::core::hash::Hash::hash(&::core::mem::discriminant(self), __state); } Test::E => { ::core::hash::Hash::hash(&::core::mem::discriminant(self), __state); } } } } #[automatically_derived] impl ::core::cmp::Ord for Test { #[inline] fn cmp(&self, __other: &Self) -> ::core::cmp::Ordering { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A { field: ref __field_field }, Test::A { field: ref __other_field_field }) => match ::core::cmp::Ord::cmp(__field_field, __other_field_field) { ::core::cmp::Ordering::Equal => ::core::cmp::Ordering::Equal, __cmp => __cmp, }, (Test::C(ref __field_0), Test::C(ref __other_field_0)) => match ::core::cmp::Ord::cmp(__field_0, __other_field_0) { ::core::cmp::Ordering::Equal => ::core::cmp::Ordering::Equal, __cmp => __cmp, }, _ => ::core::cmp::Ordering::Equal, } } else { #ord } } } #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { if ::core::mem::discriminant(self) == ::core::mem::discriminant(__other) { match (self, __other) { (Test::A { field: ref __field_field }, Test::A { field: ref __other_field_field }) => true && ::core::cmp::PartialEq::eq(__field_field, __other_field_field), (Test::C(ref __field_0), Test::C(ref __other_field_0)) => true && ::core::cmp::PartialEq::eq(__field_0, __other_field_0), _ => true, } } else { false } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { ::core::option::Option::Some(::core::cmp::Ord::cmp(self, __other)) } } }, ) } #[test] fn union_() -> Result<()> { test_derive( quote! { #[derive_where(Clone, Copy)] union Test { a: std::marker::PhantomData, b: u8, } }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test { #[inline] fn clone(&self) -> Self { *self } } #[automatically_derived] impl ::core::marker::Copy for Test { } }, ) } derive-where-1.2.7/src/test/bound.rs000064400000000000000000000270501046102023000154500ustar 00000000000000use quote::quote; use syn::Result; use super::test_derive; #[test] fn bound() -> Result<()> { test_derive( quote! { #[derive_where(Clone; T)] struct Test(T, std::marker::PhantomData); }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test where T: ::core::clone::Clone { #[inline] fn clone(&self) -> Self { match self { Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)), } } } }, ) } #[test] fn bound_multiple() -> Result<()> { test_derive( quote! { #[derive_where(Clone; T, U)] struct Test((T, U), std::marker::PhantomData); }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test where T: ::core::clone::Clone, U: ::core::clone::Clone { #[inline] fn clone(&self) -> Self { match self { Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)), } } } }, ) } #[test] fn custom_bound() -> Result<()> { test_derive( quote! { #[derive_where(Clone; T: Copy)] struct Test(T); }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test where T: Copy { #[inline] fn clone(&self) -> Self { match self { Test(ref __field_0) => Test(::core::clone::Clone::clone(__field_0)), } } } }, ) } #[test] fn where_() -> Result<()> { test_derive( quote! { #[derive_where(Clone; T)] struct Test(T, std::marker::PhantomData) where T: std::fmt::Debug; }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test where T: std::fmt::Debug, T: ::core::clone::Clone { #[inline] fn clone(&self) -> Self { match self { Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)), } } } }, ) } #[test] fn associated_type() -> Result<()> { test_derive( quote! { #[derive_where(Clone; ::Target)] struct Test(::Target); }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test where ::Target: ::core::clone::Clone { #[inline] fn clone(&self) -> Self { match self { Test(ref __field_0) => Test(::core::clone::Clone::clone(__field_0)), } } } }, ) } #[test] fn associated_type_custom_bound() -> Result<()> { test_derive( quote! { #[derive_where(Clone; ::Target: Copy)] struct Test(::Target); }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test where ::Target: Copy { #[inline] fn clone(&self) -> Self { match self { Test(ref __field_0) => Test(::core::clone::Clone::clone(__field_0)), } } } }, ) } #[test] fn check_trait_bounds() -> Result<()> { test_derive( quote! { #[derive_where(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd; T)] struct Test(T, std::marker::PhantomData); }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test where T: ::core::clone::Clone { #[inline] fn clone(&self) -> Self { match self { Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)), } } } #[automatically_derived] impl ::core::marker::Copy for Test where T: ::core::marker::Copy { } #[automatically_derived] impl ::core::fmt::Debug for Test where T: ::core::fmt::Debug { fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { Test(ref __field_0, ref __field_1) => { let mut __builder = ::core::fmt::Formatter::debug_tuple(__f, "Test"); ::core::fmt::DebugTuple::field(&mut __builder, __field_0); ::core::fmt::DebugTuple::field(&mut __builder, __field_1); ::core::fmt::DebugTuple::finish(&mut __builder) } } } } #[automatically_derived] impl ::core::default::Default for Test where T: ::core::default::Default { fn default() -> Self { Test(::core::default::Default::default(), ::core::default::Default::default()) } } #[automatically_derived] impl ::core::cmp::Eq for Test where T: ::core::cmp::Eq { #[inline] fn assert_receiver_is_total_eq(&self) { struct __AssertEq<__T: ::core::cmp::Eq + ?::core::marker::Sized>(::core::marker::PhantomData<__T>); // For some reason the comparison fails without the extra space at the end. let _: __AssertEq; let _: __AssertEq >; } } #[automatically_derived] impl ::core::hash::Hash for Test where T: ::core::hash::Hash { fn hash<__H: ::core::hash::Hasher>(&self, __state: &mut __H) { match self { Test(ref __field_0, ref __field_1) => { ::core::hash::Hash::hash(__field_0, __state); ::core::hash::Hash::hash(__field_1, __state); } } } } #[automatically_derived] impl ::core::cmp::Ord for Test where T: ::core::cmp::Ord { #[inline] fn cmp(&self, __other: &Self) -> ::core::cmp::Ordering { match (self, __other) { (Test(ref __field_0, ref __field_1), Test(ref __other_field_0, ref __other_field_1)) => match ::core::cmp::Ord::cmp(__field_0, __other_field_0) { ::core::cmp::Ordering::Equal => match ::core::cmp::Ord::cmp(__field_1, __other_field_1) { ::core::cmp::Ordering::Equal => ::core::cmp::Ordering::Equal, __cmp => __cmp, }, __cmp => __cmp, }, } } } #[automatically_derived] impl ::core::cmp::PartialEq for Test where T: ::core::cmp::PartialEq { #[inline] fn eq(&self, __other: &Self) -> bool { match (self, __other) { (Test(ref __field_0, ref __field_1), Test(ref __other_field_0, ref __other_field_1)) => true && ::core::cmp::PartialEq::eq(__field_0, __other_field_0) && ::core::cmp::PartialEq::eq(__field_1, __other_field_1), } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test where T: ::core::cmp::PartialOrd { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { match (self, __other) { (Test(ref __field_0, ref __field_1), Test(ref __other_field_0, ref __other_field_1)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => match ::core::cmp::PartialOrd::partial_cmp(__field_1, __other_field_1) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, __cmp => __cmp, }, } } } }, ) } #[test] fn check_multiple_trait_bounds() -> Result<()> { test_derive( quote! { #[derive_where(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd; T, U)] struct Test(T, std::marker::PhantomData<(U, V)>); }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test where T: ::core::clone::Clone, U: ::core::clone::Clone { #[inline] fn clone(&self) -> Self { match self { Test(ref __field_0, ref __field_1) => Test(::core::clone::Clone::clone(__field_0), ::core::clone::Clone::clone(__field_1)), } } } #[automatically_derived] impl ::core::marker::Copy for Test where T: ::core::marker::Copy, U: ::core::marker::Copy { } #[automatically_derived] impl ::core::fmt::Debug for Test where T: ::core::fmt::Debug, U: ::core::fmt::Debug { fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { Test(ref __field_0, ref __field_1) => { let mut __builder = ::core::fmt::Formatter::debug_tuple(__f, "Test"); ::core::fmt::DebugTuple::field(&mut __builder, __field_0); ::core::fmt::DebugTuple::field(&mut __builder, __field_1); ::core::fmt::DebugTuple::finish(&mut __builder) } } } } #[automatically_derived] impl ::core::default::Default for Test where T: ::core::default::Default, U: ::core::default::Default { fn default() -> Self { Test(::core::default::Default::default(), ::core::default::Default::default()) } } #[automatically_derived] impl ::core::cmp::Eq for Test where T: ::core::cmp::Eq, U: ::core::cmp::Eq { #[inline] fn assert_receiver_is_total_eq(&self) { struct __AssertEq<__T: ::core::cmp::Eq + ?::core::marker::Sized>(::core::marker::PhantomData<__T>); // For some reason the comparison fails without the extra space at the end. let _: __AssertEq; let _: __AssertEq >; } } #[automatically_derived] impl ::core::hash::Hash for Test where T: ::core::hash::Hash, U: ::core::hash::Hash { fn hash<__H: ::core::hash::Hasher>(&self, __state: &mut __H) { match self { Test(ref __field_0, ref __field_1) => { ::core::hash::Hash::hash(__field_0, __state); ::core::hash::Hash::hash(__field_1, __state); } } } } #[automatically_derived] impl ::core::cmp::Ord for Test where T: ::core::cmp::Ord, U: ::core::cmp::Ord { #[inline] fn cmp(&self, __other: &Self) -> ::core::cmp::Ordering { match (self, __other) { (Test(ref __field_0, ref __field_1), Test(ref __other_field_0, ref __other_field_1)) => match ::core::cmp::Ord::cmp(__field_0, __other_field_0) { ::core::cmp::Ordering::Equal => match ::core::cmp::Ord::cmp(__field_1, __other_field_1) { ::core::cmp::Ordering::Equal => ::core::cmp::Ordering::Equal, __cmp => __cmp, }, __cmp => __cmp, }, } } } #[automatically_derived] impl ::core::cmp::PartialEq for Test where T: ::core::cmp::PartialEq, U: ::core::cmp::PartialEq { #[inline] fn eq(&self, __other: &Self) -> bool { match (self, __other) { (Test(ref __field_0, ref __field_1), Test(ref __other_field_0, ref __other_field_1)) => true && ::core::cmp::PartialEq::eq(__field_0, __other_field_0) && ::core::cmp::PartialEq::eq(__field_1, __other_field_1), } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test where T: ::core::cmp::PartialOrd, U: ::core::cmp::PartialOrd { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { match (self, __other) { (Test(ref __field_0, ref __field_1), Test(ref __other_field_0, ref __other_field_1)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => match ::core::cmp::PartialOrd::partial_cmp(__field_1, __other_field_1) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, __cmp => __cmp, }, } } } }, ) } derive-where-1.2.7/src/test/clone.rs000064400000000000000000000037471046102023000154500ustar 00000000000000use quote::quote; use syn::Result; use super::test_derive; #[test] fn struct_() -> Result<()> { test_derive( quote! { #[derive_where(Clone)] struct Test { field: std::marker::PhantomData, } }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test { #[inline] fn clone(&self) -> Self { match self { Test { field: ref __field_field } => Test { field: ::core::clone::Clone::clone(__field_field) }, } } } }, ) } #[test] fn tuple() -> Result<()> { test_derive( quote! { #[derive_where(Clone)] struct Test(std::marker::PhantomData); }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test { #[inline] fn clone(&self) -> Self { match self { Test(ref __field_0) => Test(::core::clone::Clone::clone(__field_0)), } } } }, ) } #[test] fn enum_() -> Result<()> { test_derive( quote! { #[derive_where(Clone)] enum Test { A { field: std::marker::PhantomData}, B { }, C(std::marker::PhantomData), D(), E, } }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test { #[inline] fn clone(&self) -> Self { match self { Test::A { field: ref __field_field } => Test::A { field: ::core::clone::Clone::clone(__field_field) }, Test::B { } => Test::B { }, Test::C(ref __field_0) => Test::C(::core::clone::Clone::clone(__field_0)), Test::D() => Test::D(), Test::E => Test::E, } } } }, ) } #[test] fn union_() -> Result<()> { test_derive( quote! { #[derive_where(Clone)] union Test { a: std::marker::PhantomData, b: u8, } }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test { #[inline] fn clone(&self) -> Self { struct __AssertCopy<__T: ::core::marker::Copy + ?::core::marker::Sized>(::core::marker::PhantomData<__T>); let _: __AssertCopy; } } }, ) } derive-where-1.2.7/src/test/discriminant.rs000064400000000000000000001424501046102023000170270ustar 00000000000000use quote::quote; use syn::Result; use super::test_derive; #[test] fn default() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { const __VALIDATE_ISIZE_A: isize = 0; const __VALIDATE_ISIZE_B: isize = (0) + 1; const __VALIDATE_ISIZE_C: isize = (0) + 2; match __this { Test::A => __VALIDATE_ISIZE_A, Test::B => __VALIDATE_ISIZE_B, Test::C => __VALIDATE_ISIZE_C } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] enum Test { A = 0, B, #[derive_where(incomparable)] C, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn default_clone() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const __VALIDATE_ISIZE_A: isize = 0; const __VALIDATE_ISIZE_B: isize = (0) + 1; const __VALIDATE_ISIZE_C: isize = (0) + 2; ::core::cmp::PartialOrd::partial_cmp(&(::core::clone::Clone::clone(self) as isize), &(::core::clone::Clone::clone(__other) as isize)) }; test_derive( quote! { #[derive_where(Clone, PartialOrd)] enum Test { A = 0, B, #[derive_where(incomparable)] C, } }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test { #[inline] fn clone(&self) -> Self { match self { Test::A => Test::A, Test::B => Test::B, Test::C => Test::C, } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn default_copy() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const __VALIDATE_ISIZE_A: isize = 0; const __VALIDATE_ISIZE_B: isize = (0) + 1; const __VALIDATE_ISIZE_C: isize = (0) + 2; ::core::cmp::PartialOrd::partial_cmp(&(*self as isize), &(*__other as isize)) }; test_derive( quote! { #[derive_where(Copy, PartialOrd)] enum Test { A = 0, B, #[derive_where(incomparable)] C, } }, quote! { #[automatically_derived] impl ::core::marker::Copy for Test { } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn default_reverse() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { const __VALIDATE_ISIZE_A: isize = 2; const __VALIDATE_ISIZE_B: isize = 1; const __VALIDATE_ISIZE_C: isize = 0; match __this { Test::A => __VALIDATE_ISIZE_A, Test::B => __VALIDATE_ISIZE_B, Test::C => __VALIDATE_ISIZE_C } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] enum Test { A = 2, B = 1, #[derive_where(incomparable)] C = 0 } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn default_mix() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { const __VALIDATE_ISIZE_A: isize = 1; const __VALIDATE_ISIZE_B: isize = 0; const __VALIDATE_ISIZE_C: isize = 2; const __VALIDATE_ISIZE_D: isize = (2) + 1; match __this { Test::A => __VALIDATE_ISIZE_A, Test::B => __VALIDATE_ISIZE_B, Test::C => __VALIDATE_ISIZE_C, Test::D => __VALIDATE_ISIZE_D } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] enum Test { A = 1, B = 0, C = 2, #[derive_where(incomparable)] D } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::D) || ::core::matches!(__other, Test::D) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn default_skip() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { const __VALIDATE_ISIZE_A: isize = 0; const __VALIDATE_ISIZE_B: isize = 3; const __VALIDATE_ISIZE_C: isize = (3) + 1; const __VALIDATE_ISIZE_D: isize = (3) + 2; const __VALIDATE_ISIZE_E: isize = (3) + 3; match __this { Test::A => __VALIDATE_ISIZE_A, Test::B => __VALIDATE_ISIZE_B, Test::C => __VALIDATE_ISIZE_C, Test::D => __VALIDATE_ISIZE_D, Test::E => __VALIDATE_ISIZE_E } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] enum Test { A, B = 3, C, #[derive_where(incomparable)] D, E, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::D) || ::core::matches!(__other, Test::D) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn default_expr() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { const __VALIDATE_ISIZE_A: isize = isize::MAX - 2; const __VALIDATE_ISIZE_B: isize = (isize::MAX - 2) + 1; const __VALIDATE_ISIZE_C: isize = (isize::MAX - 2) + 2; match __this { Test::A => __VALIDATE_ISIZE_A, Test::B => __VALIDATE_ISIZE_B, Test::C => __VALIDATE_ISIZE_C } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] enum Test { A = isize::MAX - 2, B, #[derive_where(incomparable)] C, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_c() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { const __VALIDATE_ISIZE_A: isize = 0; const __VALIDATE_ISIZE_B: isize = (0) + 1; const __VALIDATE_ISIZE_C: isize = (0) + 2; match __this { Test::A => __VALIDATE_ISIZE_A, Test::B => __VALIDATE_ISIZE_B, Test::C => __VALIDATE_ISIZE_C } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(C)] enum Test { A = 0, B, #[derive_where(incomparable)] C, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_c_without_discriminant() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { match __this { Test::A => 0, Test::B => 1, Test::C => 2 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(C)] enum Test { A, B, #[derive_where(incomparable)] C, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_c_clone() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const __VALIDATE_ISIZE_A: isize = 0; const __VALIDATE_ISIZE_B: isize = (0) + 1; const __VALIDATE_ISIZE_C: isize = (0) + 2; ::core::cmp::PartialOrd::partial_cmp(&(::core::clone::Clone::clone(self) as isize), &(::core::clone::Clone::clone(__other) as isize)) }; test_derive( quote! { #[derive_where(Clone, PartialOrd)] #[repr(C)] enum Test { A = 0, B, #[derive_where(incomparable)] C, } }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test { #[inline] fn clone(&self) -> Self { match self { Test::A => Test::A, Test::B => Test::B, Test::C => Test::C, } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_c_clone_without_discriminant() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&(::core::clone::Clone::clone(self) as isize), &(::core::clone::Clone::clone(__other) as isize)) }; test_derive( quote! { #[derive_where(Clone, PartialOrd)] #[repr(C)] enum Test { A, B, #[derive_where(incomparable)] C, } }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test { #[inline] fn clone(&self) -> Self { match self { Test::A => Test::A, Test::B => Test::B, Test::C => Test::C, } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_c_copy() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const __VALIDATE_ISIZE_A: isize = 0; const __VALIDATE_ISIZE_B: isize = (0) + 1; const __VALIDATE_ISIZE_C: isize = (0) + 2; ::core::cmp::PartialOrd::partial_cmp(&(*self as isize), &(*__other as isize)) }; test_derive( quote! { #[derive_where(Copy, PartialOrd)] #[repr(C)] enum Test { A = 0, B, #[derive_where(incomparable)] C, } }, quote! { #[automatically_derived] impl ::core::marker::Copy for Test { } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_c_copy_without_discriminant() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&(*self as isize), &(*__other as isize)) }; test_derive( quote! { #[derive_where(Copy, PartialOrd)] #[repr(C)] enum Test { A, B, #[derive_where(incomparable)] C, } }, quote! { #[automatically_derived] impl ::core::marker::Copy for Test { } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_c_reverse() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { const __VALIDATE_ISIZE_A: isize = 2; const __VALIDATE_ISIZE_B: isize = 1; const __VALIDATE_ISIZE_C: isize = 0; match __this { Test::A => __VALIDATE_ISIZE_A, Test::B => __VALIDATE_ISIZE_B, Test::C => __VALIDATE_ISIZE_C } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(C)] enum Test { A = 2, B = 1, #[derive_where(incomparable)] C = 0, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_c_mix() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { const __VALIDATE_ISIZE_A: isize = 1; const __VALIDATE_ISIZE_B: isize = 0; const __VALIDATE_ISIZE_C: isize = 2; const __VALIDATE_ISIZE_D: isize = (2) + 1; match __this { Test::A => __VALIDATE_ISIZE_A, Test::B => __VALIDATE_ISIZE_B, Test::C => __VALIDATE_ISIZE_C, Test::D => __VALIDATE_ISIZE_D } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(C)] enum Test { A = 1, B = 0, C = 2, #[derive_where(incomparable)] D, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::D) || ::core::matches!(__other, Test::D) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_c_skip() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { const __VALIDATE_ISIZE_A: isize = 0; const __VALIDATE_ISIZE_B: isize = 3; const __VALIDATE_ISIZE_C: isize = (3) + 1; const __VALIDATE_ISIZE_D: isize = (3) + 2; const __VALIDATE_ISIZE_E: isize = (3) + 3; match __this { Test::A => __VALIDATE_ISIZE_A, Test::B => __VALIDATE_ISIZE_B, Test::C => __VALIDATE_ISIZE_C, Test::D => __VALIDATE_ISIZE_D, Test::E => __VALIDATE_ISIZE_E } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(C)] enum Test { A, B = 3, C, #[derive_where(incomparable)] D, E, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::D) || ::core::matches!(__other, Test::D) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_c_expr() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { const __VALIDATE_ISIZE_A: isize = isize::MAX - 2; const __VALIDATE_ISIZE_B: isize = (isize::MAX - 2) + 1; const __VALIDATE_ISIZE_C: isize = (isize::MAX - 2) + 2; match __this { Test::A => __VALIDATE_ISIZE_A, Test::B => __VALIDATE_ISIZE_B, Test::C => __VALIDATE_ISIZE_C } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(C)] enum Test { A = isize::MAX - 2, B, #[derive_where(incomparable)] C, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_c_with_value() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc) }; #[cfg(not(any(feature = "nightly", feature = "safe")))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &unsafe { *<*const _>::from(self).cast::() }, &unsafe { *<*const _>::from(__other).cast::() }, ) }; #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> u8 { match __this { Test::A(ref __field_0) => 0, Test::B => (0) + 1, Test::C => (0) + 2 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(C, u8)] enum Test { A(std::marker::PhantomData) = 0, B, C, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => ::core::option::Option::Some(::core::cmp::Ordering::Equal), } } else { #partial_ord } } } }, ) } #[test] fn repr_c_with_value_reverse() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc) }; #[cfg(not(any(feature = "nightly", feature = "safe")))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &unsafe { *<*const _>::from(self).cast::() }, &unsafe { *<*const _>::from(__other).cast::() }, ) }; #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> u8 { match __this { Test::A(ref __field_0) => 2, Test::B => 1, Test::C => 0 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(C, u8)] enum Test { A(std::marker::PhantomData) = 2, B = 1, C = 0, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => ::core::option::Option::Some(::core::cmp::Ordering::Equal), } } else { #partial_ord } } } }, ) } #[test] fn repr_c_with_value_mix() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc) }; #[cfg(not(any(feature = "nightly", feature = "safe")))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &unsafe { *<*const _>::from(self).cast::() }, &unsafe { *<*const _>::from(__other).cast::() }, ) }; #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> u8 { match __this { Test::A(ref __field_0) => 1, Test::B => 0, Test::C => 2 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(C, u8)] enum Test { A(std::marker::PhantomData) = 1, B = 0, C = 2, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => ::core::option::Option::Some(::core::cmp::Ordering::Equal), } } else { #partial_ord } } } }, ) } #[test] fn repr_c_with_value_skip() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc) }; #[cfg(not(any(feature = "nightly", feature = "safe")))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &unsafe { *<*const _>::from(self).cast::() }, &unsafe { *<*const _>::from(__other).cast::() }, ) }; #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> u8 { match __this { Test::A(ref __field_0) => 0, Test::B => 3, Test::C => (3) + 1, Test::D => (3) + 2 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(C, u8)] enum Test { A(std::marker::PhantomData), B = 3, C, D, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => ::core::option::Option::Some(::core::cmp::Ordering::Equal), } } else { #partial_ord } } } }, ) } #[test] fn repr_c_with_value_expr() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc) }; #[cfg(not(any(feature = "nightly", feature = "safe")))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &unsafe { *<*const _>::from(self).cast::() }, &unsafe { *<*const _>::from(__other).cast::() }, ) }; #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> u8 { match __this { Test::A(ref __field_0) => isize::MAX - 2, Test::B => (isize::MAX - 2) + 1, Test::C => (isize::MAX - 2) + 2 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(C, u8)] enum Test { A(std::marker::PhantomData) = isize::MAX - 2, B, C, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => ::core::option::Option::Some(::core::cmp::Ordering::Equal), } } else { #partial_ord } } } }, ) } #[test] fn repr() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(any(feature = "nightly", feature = "safe")))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &unsafe { *<*const _>::from(self).cast::() }, &unsafe { *<*const _>::from(__other).cast::() }, ) }; #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> u64 { match __this { Test::A => 0, Test::B => (0) + 1, Test::C => (0) + 2 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(u64)] enum Test { A = 0, B, #[derive_where(incomparable)] C, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_clone() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&(::core::clone::Clone::clone(self) as u64), &(::core::clone::Clone::clone(__other) as u64)) }; test_derive( quote! { #[derive_where(Clone, PartialOrd)] #[repr(u64)] enum Test { A = 0, B, #[derive_where(incomparable)] C, } }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test { #[inline] fn clone(&self) -> Self { match self { Test::A => Test::A, Test::B => Test::B, Test::C => Test::C, } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_copy() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&(*self as u64), &(*__other as u64)) }; test_derive( quote! { #[derive_where(Copy, PartialOrd)] #[repr(u64)] enum Test { A = 0, B, #[derive_where(incomparable)] C, } }, quote! { #[automatically_derived] impl ::core::marker::Copy for Test { } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_reverse() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(any(feature = "nightly", feature = "safe")))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &unsafe { *<*const _>::from(self).cast::() }, &unsafe { *<*const _>::from(__other).cast::() }, ) }; #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> u64 { match __this { Test::A => 2, Test::B => 1, Test::C => 0 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(u64)] enum Test { A = 2, B = 1, #[derive_where(incomparable)] C = 0, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_mix() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(any(feature = "nightly", feature = "safe")))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &unsafe { *<*const _>::from(self).cast::() }, &unsafe { *<*const _>::from(__other).cast::() }, ) }; #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> u64 { match __this { Test::A => 1, Test::B => 0, Test::C => 2, Test::D => (2) + 1 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(u64)] enum Test { A = 1, B = 0, C = 2, #[derive_where(incomparable)] D, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::D) || ::core::matches!(__other, Test::D) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_skip() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(any(feature = "nightly", feature = "safe")))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &unsafe { *<*const _>::from(self).cast::() }, &unsafe { *<*const _>::from(__other).cast::() }, ) }; #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> u64 { match __this { Test::A => 0, Test::B => 3, Test::C => (3) + 1, Test::D => (3) + 2, Test::E => (3) + 3 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(u64)] enum Test { A, B = 3, C, #[derive_where(incomparable)] D, E } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::D) || ::core::matches!(__other, Test::D) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_expr() -> Result<()> { #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(any(feature = "nightly", feature = "safe")))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &unsafe { *<*const _>::from(self).cast::() }, &unsafe { *<*const _>::from(__other).cast::() }, ) }; #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> u64 { match __this { Test::A => u64::MAX - 2, Test::B => (u64::MAX - 2) + 1, Test::C => (u64::MAX - 2) + 2 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(u64)] enum Test { A = u64::MAX - 2, B, #[derive_where(incomparable)] C, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::C) || ::core::matches!(__other, Test::C) { return ::core::option::Option::None; } #partial_ord } } }, ) } #[test] fn repr_with_value() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc) }; #[cfg(not(any(feature = "nightly", feature = "safe")))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &unsafe { *<*const _>::from(self).cast::() }, &unsafe { *<*const _>::from(__other).cast::() }, ) }; #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> u8 { match __this { Test::A(ref __field_0) => 0, Test::B => (0) + 1, Test::C => (0) + 2 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(u8)] enum Test { A(std::marker::PhantomData) = 0, B, C, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => ::core::option::Option::Some(::core::cmp::Ordering::Equal), } } else { #partial_ord } } } }, ) } #[test] fn repr_with_value_reverse() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc) }; #[cfg(not(any(feature = "nightly", feature = "safe")))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &unsafe { *<*const _>::from(self).cast::() }, &unsafe { *<*const _>::from(__other).cast::() }, ) }; #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> u8 { match __this { Test::A(ref __field_0) => 2, Test::B => 1, Test::C => 0 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(u8)] enum Test { A(std::marker::PhantomData) = 2, B = 1, C = 0, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => ::core::option::Option::Some(::core::cmp::Ordering::Equal), } } else { #partial_ord } } } }, ) } #[test] fn repr_with_value_mix() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc) }; #[cfg(not(any(feature = "nightly", feature = "safe")))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &unsafe { *<*const _>::from(self).cast::() }, &unsafe { *<*const _>::from(__other).cast::() }, ) }; #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> u8 { match __this { Test::A(ref __field_0) => 1, Test::B => 0, Test::C => 2 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(u8)] enum Test { A(std::marker::PhantomData) = 1, B = 0, C = 2, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => ::core::option::Option::Some(::core::cmp::Ordering::Equal), } } else { #partial_ord } } } }, ) } #[test] fn repr_with_value_skip() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc) }; #[cfg(not(any(feature = "nightly", feature = "safe")))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &unsafe { *<*const _>::from(self).cast::() }, &unsafe { *<*const _>::from(__other).cast::() }, ) }; #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> u8 { match __this { Test::A(ref __field_0) => 0, Test::B => 3, Test::C => (3) + 1, Test::D => (3) + 2 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(u8)] enum Test { A(std::marker::PhantomData), B = 3, C, D, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => ::core::option::Option::Some(::core::cmp::Ordering::Equal), } } else { #partial_ord } } } }, ) } #[test] fn repr_with_value_expr() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc) }; #[cfg(not(any(feature = "nightly", feature = "safe")))] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp( &unsafe { *<*const _>::from(self).cast::() }, &unsafe { *<*const _>::from(__other).cast::() }, ) }; #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> u8 { match __this { Test::A(ref __field_0) => isize::MAX - 2, Test::B => (isize::MAX - 2) + 1, Test::C => (isize::MAX - 2) + 2 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] #[repr(u8)] enum Test { A(std::marker::PhantomData) = isize::MAX - 2, B, C, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => ::core::option::Option::Some(::core::cmp::Ordering::Equal), } } else { #partial_ord } } } }, ) } derive-where-1.2.7/src/test/enum_.rs000064400000000000000000000265511046102023000154510ustar 00000000000000use quote::quote; use syn::Result; use super::test_derive; #[test] fn default_struct() -> Result<()> { test_derive( quote! { #[derive_where(Default; T)] enum Test { #[derive_where(default)] A { field: T }, } }, quote! { #[automatically_derived] impl ::core::default::Default for Test where T: ::core::default::Default { fn default() -> Self { Test::A { field: ::core::default::Default::default() } } } }, ) } #[test] fn default_tuple() -> Result<()> { test_derive( quote! { #[derive_where(Default; T)] enum Test { #[derive_where(default)] A(T), } }, quote! { #[automatically_derived] impl ::core::default::Default for Test where T: ::core::default::Default { fn default() -> Self { Test::A(::core::default::Default::default()) } } }, ) } #[test] fn default_unit() -> Result<()> { test_derive( quote! { #[derive_where(Default; T)] enum Test { #[derive_where(default)] A, B(T), } }, quote! { #[automatically_derived] impl ::core::default::Default for Test where T: ::core::default::Default { fn default() -> Self { Test::A } } }, ) } #[test] fn one_data() -> Result<()> { test_derive( quote! { #[derive_where(PartialEq, PartialOrd)] enum Test { A(std::marker::PhantomData), } }, quote! { #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => true && ::core::cmp::PartialEq::eq(__field_0, __other_field_0), } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, } } } }, ) } #[test] fn two_data() -> Result<()> { #[cfg(not(feature = "safe"))] let unreachable = quote! { unsafe { ::core::hint::unreachable_unchecked() } }; #[cfg(feature = "safe")] let unreachable = quote! { ::core::unreachable!("comparing variants yielded unexpected results") }; #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { match __this { Test::A(ref __field_0) => 0, Test::B(ref __field_0) => 1 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialEq, PartialOrd)] enum Test { A(std::marker::PhantomData), B(std::marker::PhantomData), } }, quote! { #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { if ::core::mem::discriminant(self) == ::core::mem::discriminant(__other) { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => true && ::core::cmp::PartialEq::eq(__field_0, __other_field_0), (Test::B(ref __field_0), Test::B(ref __other_field_0)) => true && ::core::cmp::PartialEq::eq(__field_0, __other_field_0), _ => #unreachable, } } else { false } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, (Test::B(ref __field_0), Test::B(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => #unreachable, } } else { #partial_ord } } } }, ) } #[test] fn unit() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { match __this { Test::A(ref __field_0) => 0, Test::B => 1 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialEq, PartialOrd)] enum Test { A(std::marker::PhantomData), B, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { if ::core::mem::discriminant(self) == ::core::mem::discriminant(__other) { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => true && ::core::cmp::PartialEq::eq(__field_0, __other_field_0), _ => true, } } else { false } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => ::core::option::Option::Some(::core::cmp::Ordering::Equal), } } else { #partial_ord } } } }, ) } #[test] fn struct_unit() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { match __this { Test::A(ref __field_0) => 0, Test::B { } => 1 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialEq, PartialOrd)] enum Test { A(std::marker::PhantomData), B { }, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { if ::core::mem::discriminant(self) == ::core::mem::discriminant(__other) { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => true && ::core::cmp::PartialEq::eq(__field_0, __other_field_0), _ => true, } } else { false } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => ::core::option::Option::Some(::core::cmp::Ordering::Equal), } } else { #partial_ord } } } }, ) } #[test] fn tuple_unit() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { match __this { Test::A(ref __field_0) => 0, Test::B() => 1 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialEq, PartialOrd)] enum Test { A(std::marker::PhantomData), B(), } }, quote! { #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { if ::core::mem::discriminant(self) == ::core::mem::discriminant(__other) { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => true && ::core::cmp::PartialEq::eq(__field_0, __other_field_0), _ => true, } } else { false } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A(ref __field_0), Test::A(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => ::core::option::Option::Some(::core::cmp::Ordering::Equal), } } else { #partial_ord } } } }, ) } derive-where-1.2.7/src/test/incomparable.rs000064400000000000000000000231011046102023000167660ustar 00000000000000use quote::quote; use syn::Result; use super::test_derive; #[test] fn variants() -> Result<()> { test_derive( quote! { #[derive_where(PartialEq, PartialOrd)] enum Test{ A, #[derive_where(incomparable)] B(Box), C(String), #[derive_where(incomparable)] D, #[derive_where(incomparable)] E { test: u8 } } }, quote! { #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { if ::core::mem::discriminant(self) == ::core::mem::discriminant(__other) { match (self, __other) { (Test::C(ref __field_0), Test::C(ref __other_field_0)) => true && ::core::cmp::PartialEq::eq(__field_0, __other_field_0), (Test::B(..) | Test::D | Test::E {..}, ..) => false, _ => true, } } else { false } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::B(..) | Test::D | Test::E { .. }) || ::core::matches!(__other, Test::B(..) | Test::D | Test::E { .. }) { return ::core::option::Option::None; } let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); if __self_disc == __other_disc { match (self, __other) { (Test::C(ref __field_0), Test::C(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => ::core::option::Option::Some(::core::cmp::Ordering::Equal), } } else { const fn __discriminant(__this: &Test) -> isize { match __this { Test::A => 0, Test::B(ref __field_0) => 1, Test::C(ref __field_0) => 2, Test::D => 3, Test::E { test: ref __field_test } => 4 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) } } } }, ) } #[test] fn enum_empty_and_empty_incomparable_variants() -> Result<()> { test_derive( quote! { #[derive_where(PartialEq, PartialOrd)] enum Test{ #[derive_where(incomparable)] A, B } }, quote! { #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { if ::core::mem::discriminant(self) == ::core::mem::discriminant(__other) { if ::core::matches!(self, Test::A) { return false; } true } else { false } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::A) || ::core::matches!(__other, Test::A) { ::core::option::Option::None } else { ::core::option::Option::Some(::core::cmp::Ordering::Equal) } } } }, ) } #[test] fn enum_empty_and_non_empty_incomparable_variants() -> Result<()> { test_derive( quote! { #[derive_where(PartialEq, PartialOrd)] enum Test{ #[derive_where(incomparable)] A(String), B } }, quote! { #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { if ::core::mem::discriminant(self) == ::core::mem::discriminant(__other) { match (self , __other) { (Test::A (..) , ..) => false, _ => true, } } else { false } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::A(..)) || ::core::matches!(__other, Test::A(..)) { ::core::option::Option::None } else { ::core::option::Option::Some(::core::cmp::Ordering::Equal) } } } }, ) } #[test] fn enum_empty_and_multiple_incomparable_variants() -> Result<()> { test_derive( quote! { #[derive_where(PartialEq, PartialOrd)] enum Test{ #[derive_where(incomparable)] A, B, #[derive_where(incomparable)] C(String) } }, quote! { #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { if ::core::mem::discriminant(self) == ::core::mem::discriminant(__other) { match (self, __other) { (Test::A | Test::C(..), ..) => false, _ => true, } } else { false } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::A | Test::C(..)) || ::core::matches!(__other, Test::A | Test::C(..)) { ::core::option::Option::None } else { ::core::option::Option::Some(::core::cmp::Ordering::Equal) } } } }, ) } #[test] fn enum_skipped_and_incomparable_variant() -> Result<()> { test_derive( quote! { #[derive_where(PartialEq, PartialOrd)] enum Test{ #[derive_where(incomparable)] A, #[derive_where(skip_inner)] B(String), } }, quote! { #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { if ::core::mem::discriminant(self) == ::core::mem::discriminant(__other) { if ::core::matches!(self, Test::A) { return false; } true } else { false } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::A) || ::core::matches!(__other, Test::A) { ::core::option::Option::None } else { ::core::option::Option::Some(::core::cmp::Ordering::Equal) } } } }, ) } #[test] fn enum_non_empty_and_incomparable_variant() -> Result<()> { test_derive( quote! { #[derive_where(PartialEq, PartialOrd)] enum Test{ #[derive_where(incomparable)] A, B(String), } }, quote! { #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { if ::core::mem::discriminant(self) == ::core::mem::discriminant(__other) { match (self, __other) { (Test::B(ref __field_0), Test::B(ref __other_field_0)) => true && ::core::cmp::PartialEq::eq(__field_0, __other_field_0), (Test::A, ..) => false, _ => unsafe { ::core::hint::unreachable_unchecked() }, } } else { false } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::A) || ::core::matches!(__other, Test::A) { ::core::option::Option::None } else { match (self, __other) { (Test::B(ref __field_0), Test::B(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => unsafe { ::core::hint::unreachable_unchecked() }, } } } } }, ) } #[test] fn enum_incomparable_and_skipped_variant() -> Result<()> { test_derive( quote! { #[derive_where(PartialEq, PartialOrd)] enum Test{ #[derive_where(incomparable, skip_inner)] A(String), B } }, quote! { #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { if ::core::mem::discriminant(self) == ::core::mem::discriminant(__other) { if ::core::matches!(self, Test::A(..)) { return false; } true } else { false } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { if ::core::matches!(self, Test::A(..)) || ::core::matches!(__other, Test::A(..)) { ::core::option::Option::None } else { ::core::option::Option::Some(::core::cmp::Ordering::Equal) } } } }, ) } #[test] fn items() -> Result<()> { test_derive( quote! { #[derive_where(PartialEq, PartialOrd)] #[derive_where(incomparable)] enum Test{} }, quote! { #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { false } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { ::core::option::Option::None } } }, )?; test_derive( quote! { #[derive_where(PartialEq, PartialOrd)] #[derive_where(incomparable)] struct Test; }, quote! { #[automatically_derived] impl ::core::cmp::PartialEq for Test { #[inline] fn eq(&self, __other: &Self) -> bool { false } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { ::core::option::Option::None } } }, ) } derive-where-1.2.7/src/test/misc.rs000064400000000000000000000010501046102023000152640ustar 00000000000000use quote::quote; use syn::Result; use super::test_derive; #[test] fn ignore_foreign_attribute() -> Result<()> { test_derive( quote! { #[derive_where(Default; T)] #[foreign(default)] enum Test { #[foreign(default)] A { field: T }, #[derive_where(default)] B { field: T }, } }, quote! { #[automatically_derived] impl ::core::default::Default for Test where T: ::core::default::Default { fn default() -> Self { Test::B { field: ::core::default::Default::default() } } } }, ) } derive-where-1.2.7/src/test/mod.rs000064400000000000000000000024251046102023000151170ustar 00000000000000mod basic; mod bound; mod discriminant; mod enum_; #[cfg(not(any(feature = "nightly", feature = "safe")))] mod incomparable; mod misc; mod partial_ord; mod skip; mod use_case; #[cfg(feature = "zeroize")] mod zeroize; use std::iter; use pretty_assertions::assert_eq; use proc_macro2::TokenStream; use quote::quote; use syn::{spanned::Spanned, DeriveInput, Result}; use crate::{generate_impl, input::Input}; fn test_derive(input: TokenStream, expected: TokenStream) -> Result<()> { let left = derive_where_internal(input)?.to_string(); let right = quote! {#expected}.to_string(); assert_eq!(left, right); Ok(()) } fn compiles(input: TokenStream) -> Result<()> { derive_where_internal(input).map(|_| ()) } /// Internal derive function for testing fn derive_where_internal(input: TokenStream) -> Result { // Save `Span` before we consume `input` when parsing it. let span = input.span(); let item = syn::parse2::(input).expect("derive on unparsable item"); let Input { derive_wheres, generics, item, .. } = Input::from_input(span, &item)?; Ok(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()) } derive-where-1.2.7/src/test/partial_ord.rs000064400000000000000000000131111046102023000166320ustar 00000000000000use quote::quote; use syn::Result; use super::test_derive; #[test] fn struct_() -> Result<()> { test_derive( quote! { #[derive_where(PartialOrd)] struct Test { field: std::marker::PhantomData, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { match (self, __other) { (Test { field: ref __field_field }, Test { field: ref __other_field_field }) => match ::core::cmp::PartialOrd::partial_cmp(__field_field, __other_field_field) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, } } } }, ) } #[test] fn tuple() -> Result<()> { test_derive( quote! { #[derive_where(PartialOrd)] struct Test(std::marker::PhantomData); }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { match (self, __other) { (Test(ref __field_0), Test(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, } } } }, ) } #[test] fn enum_() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let partial_ord = quote! { ::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc) }; #[cfg(not(feature = "nightly"))] let partial_ord = quote! { const fn __discriminant(__this: &Test) -> isize { match __this { Test::A { field: ref __field_field } => 0, Test::B { } => 1, Test::C(ref __field_0) => 2, Test::D() => 3, Test::E => 4 } } ::core::cmp::PartialOrd::partial_cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(PartialOrd)] enum Test { A { field: std::marker::PhantomData}, B { }, C(std::marker::PhantomData), D(), E, } }, quote! { #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #discriminant if __self_disc == __other_disc { match (self, __other) { (Test::A { field: ref __field_field }, Test::A { field: ref __other_field_field }) => match ::core::cmp::PartialOrd::partial_cmp(__field_field, __other_field_field) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, (Test::C(ref __field_0), Test::C(ref __other_field_0)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, _ => ::core::option::Option::Some(::core::cmp::Ordering::Equal), } } else { #partial_ord } } } }, ) } #[test] fn union_() -> Result<()> { test_derive( quote! { #[derive_where(Clone, Copy)] union Test { a: std::marker::PhantomData, b: u8, } }, quote! { #[automatically_derived] impl ::core::clone::Clone for Test { #[inline] fn clone(&self) -> Self { *self } } #[automatically_derived] impl ::core::marker::Copy for Test { } }, ) } #[test] fn bound() -> Result<()> { test_derive( quote! { #[derive_where(Ord; T)] #[derive_where(PartialOrd)] struct Test(T, std::marker::PhantomData); }, quote! { #[automatically_derived] impl ::core::cmp::Ord for Test where T: ::core::cmp::Ord { #[inline] fn cmp(&self, __other: &Self) -> ::core::cmp::Ordering { match (self, __other) { (Test(ref __field_0, ref __field_1), Test(ref __other_field_0, ref __other_field_1)) => match ::core::cmp::Ord::cmp(__field_0, __other_field_0) { ::core::cmp::Ordering::Equal => match ::core::cmp::Ord::cmp(__field_1, __other_field_1) { ::core::cmp::Ordering::Equal => ::core::cmp::Ordering::Equal, __cmp => __cmp, }, __cmp => __cmp, }, } } } #[automatically_derived] impl ::core::cmp::PartialOrd for Test { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { match (self, __other) { (Test(ref __field_0, ref __field_1), Test(ref __other_field_0, ref __other_field_1)) => match ::core::cmp::PartialOrd::partial_cmp(__field_0, __other_field_0) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => match ::core::cmp::PartialOrd::partial_cmp(__field_1, __other_field_1) { ::core::option::Option::Some(::core::cmp::Ordering::Equal) => ::core::option::Option::Some(::core::cmp::Ordering::Equal), __cmp => __cmp, }, __cmp => __cmp, }, } } } }, ) } derive-where-1.2.7/src/test/skip.rs000064400000000000000000000107241046102023000153070ustar 00000000000000use quote::quote; use syn::Result; use super::test_derive; #[test] fn struct_inner() -> Result<()> { test_derive( quote! { #[derive_where(Debug)] #[derive_where(skip_inner)] struct Test(std::marker::PhantomData); }, quote! { #[automatically_derived] impl ::core::fmt::Debug for Test { fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { Test(ref __field_0) => { let mut __builder = ::core::fmt::Formatter::debug_tuple(__f, "Test"); ::core::fmt::DebugTuple::finish(&mut __builder) } } } } }, ) } #[test] fn enum_inner() -> Result<()> { test_derive( quote! { #[derive_where(Debug)] enum Test { #[derive_where(skip_inner)] A(std::marker::PhantomData), } }, quote! { #[automatically_derived] impl ::core::fmt::Debug for Test { fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { Test::A(ref __field_0) => { let mut __builder = ::core::fmt::Formatter::debug_tuple(__f, "A"); ::core::fmt::DebugTuple::finish(&mut __builder) } } } } }, ) } #[test] fn struct_empty() -> Result<()> { test_derive( quote! { #[derive_where(Ord)] #[derive_where(skip_inner)] struct Test(std::marker::PhantomData); }, quote! { #[automatically_derived] impl ::core::cmp::Ord for Test { #[inline] fn cmp(&self, __other: &Self) -> ::core::cmp::Ordering { ::core::cmp::Ordering::Equal } } }, ) } #[test] fn variant_empty() -> Result<()> { test_derive( quote! { #[derive_where(Ord)] enum Test { #[derive_where(skip_inner)] A(std::marker::PhantomData), } }, quote! { #[automatically_derived] impl ::core::cmp::Ord for Test { #[inline] fn cmp(&self, __other: &Self) -> ::core::cmp::Ordering { ::core::cmp::Ordering::Equal } } }, ) } #[test] fn variants_empty() -> Result<()> { #[cfg(feature = "nightly")] let ord = quote! { ::core::cmp::Ord::cmp( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) }; #[cfg(not(feature = "nightly"))] let ord = quote! { const fn __discriminant(__this: &Test) -> isize { match __this { Test::A(ref __field_0) => 0, Test::B(ref __field_0) => 1 } } ::core::cmp::Ord::cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(Ord)] enum Test { #[derive_where(skip_inner)] A(std::marker::PhantomData), #[derive_where(skip_inner)] B(std::marker::PhantomData), } }, quote! { #[automatically_derived] impl ::core::cmp::Ord for Test { #[inline] fn cmp(&self, __other: &Self) -> ::core::cmp::Ordering { #ord } } }, ) } #[test] fn variants_partly_empty() -> Result<()> { #[cfg(feature = "nightly")] let discriminant = quote! { let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); }; #[cfg(not(feature = "nightly"))] let discriminant = quote! { let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); }; #[cfg(feature = "nightly")] let ord = quote! { ::core::cmp::Ord::cmp(&__self_disc, &__other_disc) }; #[cfg(not(feature = "nightly"))] let ord = quote! { const fn __discriminant(__this: &Test) -> isize { match __this { Test::A(ref __field_0) => 0, Test::B(ref __field_0, ref __field_1) => 1 } } ::core::cmp::Ord::cmp(&__discriminant(self), &__discriminant(__other)) }; test_derive( quote! { #[derive_where(Ord)] enum Test { #[derive_where(skip_inner)] A(std::marker::PhantomData), B(#[derive_where(skip)] std::marker::PhantomData, std::marker::PhantomData), } }, quote! { #[automatically_derived] impl ::core::cmp::Ord for Test { #[inline] fn cmp(&self, __other: &Self) -> ::core::cmp::Ordering { #discriminant if __self_disc == __other_disc { match (self , __other) { (Test::B(ref __field_0, ref __field_1), Test::B(ref __other_field_0, ref __other_field_1)) => match ::core::cmp::Ord::cmp(__field_1 ,__other_field_1) { ::core::cmp::Ordering::Equal => ::core::cmp::Ordering::Equal, __cmp => __cmp, }, _ => ::core::cmp::Ordering::Equal, } } else { #ord } } } }, ) } derive-where-1.2.7/src/test/use_case.rs000064400000000000000000000046641046102023000161360ustar 00000000000000use quote::quote; use syn::Result; use super::compiles; #[test] fn struct_skip() -> Result<()> { compiles(quote! { #[derive_where(Debug)] struct Test(#[derive_where(skip)] u8); }) } #[test] fn struct_skip_multiple() -> Result<()> { compiles(quote! { #[derive_where(Debug)] struct Test(#[derive_where(skip)] u8, u8); }) } #[test] fn struct_skip_trait() -> Result<()> { compiles(quote! { #[derive_where(Debug)] struct Test(#[derive_where(skip(Debug))] u8); }) } #[test] fn struct_skip_trait_multiple() -> Result<()> { compiles(quote! { #[derive_where(Debug)] struct Test(#[derive_where(skip(Debug))] u8, u8); }) } #[test] fn struct_skip_inner() -> Result<()> { compiles(quote! { #[derive_where(Debug)] #[derive_where(skip_inner)] struct Test(u8); }) } #[test] fn struct_skip_inner_trait() -> Result<()> { compiles(quote! { #[derive_where(Debug)] #[derive_where(skip_inner(Debug))] struct Test(u8); }) } #[test] fn enum_skip() -> Result<()> { compiles(quote! { #[derive_where(Debug)] enum Test { A(#[derive_where(skip)] u8), } }) } #[test] fn enum_skip_multiple() -> Result<()> { compiles(quote! { #[derive_where(Debug)] enum Test { A(#[derive_where(skip)] u8), B, } }) } #[test] fn enum_skip_trait() -> Result<()> { compiles(quote! { #[derive_where(Debug)] enum Test { A(#[derive_where(skip(Debug))] u8), } }) } #[test] fn enum_skip_trait_multiple() -> Result<()> { compiles(quote! { #[derive_where(Debug)] enum Test { A(#[derive_where(skip(Debug))] u8), B, } }) } #[test] fn enum_skip_inner() -> Result<()> { compiles(quote! { #[derive_where(Debug)] enum Test { #[derive_where(skip_inner)] A(u8), } }) } #[test] fn enum_skip_inner_trait() -> Result<()> { compiles(quote! { #[derive_where(Debug)] enum Test { #[derive_where(skip_inner(Debug))] A(u8), } }) } #[test] fn default() -> Result<()> { compiles(quote! { #[derive_where(Default)] enum Test { #[derive_where(default)] A, } }) } #[test] #[cfg(feature = "zeroize")] fn zeroize_crate() -> Result<()> { compiles(quote! { #[derive_where(Zeroize(crate = zeroize_))] struct Test(u8); }) } #[test] #[cfg(feature = "zeroize")] fn zeroize_fqs() -> Result<()> { compiles(quote! { #[derive_where(Zeroize)] struct Test(#[derive_where(Zeroize(fqs))] u8); }) } #[test] fn custom_bound() -> Result<()> { compiles(quote! { #[derive_where(Debug; T: Clone)] struct Test(T); }) } derive-where-1.2.7/src/test/zeroize.rs000064400000000000000000000110271046102023000160250ustar 00000000000000use quote::quote; use syn::Result; use super::test_derive; #[test] fn basic() -> Result<()> { test_derive( quote! { #[derive_where(Zeroize)] struct Test(std::marker::PhantomData); }, quote! { #[automatically_derived] impl ::zeroize::Zeroize for Test { fn zeroize(&mut self) { use ::zeroize::Zeroize; match self { Test(ref mut __field_0) => { __field_0.zeroize(); } } } } }, ) } #[test] fn drop() -> Result<()> { test_derive( quote! { #[derive_where(ZeroizeOnDrop; T)] struct Test(T, std::marker::PhantomData); }, #[cfg(not(feature = "zeroize-on-drop"))] quote! { #[automatically_derived] impl ::core::ops::Drop for Test where T: ::zeroize::ZeroizeOnDrop { fn drop(&mut self) { ::zeroize::Zeroize::zeroize(self); } } }, #[cfg(feature = "zeroize-on-drop")] quote! { #[automatically_derived] impl ::core::ops::Drop for Test where T: ::zeroize::ZeroizeOnDrop { fn drop(&mut self) { use ::zeroize::__internal::AssertZeroize; use ::zeroize::__internal::AssertZeroizeOnDrop; match self { Test(ref mut __field_0, ref mut __field_1) => { __field_0.zeroize_or_on_drop(); __field_1.zeroize_or_on_drop(); } } } } #[automatically_derived] impl ::zeroize::ZeroizeOnDrop for Test where T: ::zeroize::ZeroizeOnDrop { } }, ) } #[test] fn both() -> Result<()> { #[cfg(not(feature = "zeroize-on-drop"))] let drop = quote! { #[automatically_derived] impl ::core::ops::Drop for Test where T: ::zeroize::ZeroizeOnDrop { fn drop(&mut self) { ::zeroize::Zeroize::zeroize(self); } } }; #[cfg(feature = "zeroize-on-drop")] let drop = quote! { #[automatically_derived] impl ::core::ops::Drop for Test where T: ::zeroize::ZeroizeOnDrop { fn drop(&mut self) { use ::zeroize::__internal::AssertZeroize; use ::zeroize::__internal::AssertZeroizeOnDrop; match self { Test(ref mut __field_0, ref mut __field_1) => { __field_0.zeroize_or_on_drop(); __field_1.zeroize_or_on_drop(); } } } } #[automatically_derived] impl ::zeroize::ZeroizeOnDrop for Test where T: ::zeroize::ZeroizeOnDrop { } }; test_derive( quote! { #[derive_where(Zeroize, ZeroizeOnDrop; T)] struct Test(T, std::marker::PhantomData); }, quote! { #[automatically_derived] impl ::zeroize::Zeroize for Test where T: ::zeroize::Zeroize { fn zeroize(&mut self) { use ::zeroize::Zeroize; match self { Test(ref mut __field_0, ref mut __field_1) => { __field_0.zeroize(); __field_1.zeroize(); } } } } #drop }, ) } #[test] fn crate_() -> Result<()> { test_derive( quote! { #[derive_where(Zeroize(crate = zeroize_); T)] struct Test(T); }, quote! { #[automatically_derived] impl zeroize_::Zeroize for Test where T: zeroize_::Zeroize { fn zeroize(&mut self) { use zeroize_::Zeroize; match self { Test(ref mut __field_0) => { __field_0.zeroize(); } } } } }, ) } #[test] fn crate_drop() -> Result<()> { test_derive( quote! { #[derive_where(ZeroizeOnDrop(crate = zeroize_); T)] struct Test(T); }, #[cfg(not(feature = "zeroize-on-drop"))] quote! { #[automatically_derived] impl ::core::ops::Drop for Test where T: zeroize_::ZeroizeOnDrop { fn drop(&mut self) { zeroize_::Zeroize::zeroize(self); } } }, #[cfg(feature = "zeroize-on-drop")] quote! { #[automatically_derived] impl ::core::ops::Drop for Test where T: zeroize_::ZeroizeOnDrop { fn drop(&mut self) { use zeroize_::__internal::AssertZeroize; use zeroize_::__internal::AssertZeroizeOnDrop; match self { Test(ref mut __field_0) => { __field_0.zeroize_or_on_drop(); } } } } #[automatically_derived] impl zeroize_::ZeroizeOnDrop for Test where T: zeroize_::ZeroizeOnDrop { } }, ) } #[test] fn fqs() -> Result<()> { test_derive( quote! { #[derive_where(Zeroize)] struct Test(#[derive_where(Zeroize(fqs))] std::marker::PhantomData); }, quote! { #[automatically_derived] impl ::zeroize::Zeroize for Test { fn zeroize(&mut self) { use ::zeroize::Zeroize; match self { Test(ref mut __field_0) => { ::zeroize::Zeroize::zeroize(__field_0); } } } } }, ) } derive-where-1.2.7/src/trait_/clone.rs000064400000000000000000000055221046102023000157440ustar 00000000000000//! [`Clone`](trait@std::clone::Clone) implementation. use proc_macro2::TokenStream; use quote::quote; use syn::{TraitBound, TraitBoundModifier, TypeParamBound}; use crate::{Data, DataType, DeriveTrait, Item, SimpleType, SplitGenerics, Trait, TraitImpl}; /// Dummy-struct implement [`Trait`] for [`Clone`](trait@std::clone::Clone). pub struct Clone; impl TraitImpl for Clone { fn as_str(&self) -> &'static str { "Clone" } fn default_derive_trait(&self) -> DeriveTrait { DeriveTrait::Clone } fn supports_union(&self) -> bool { true } fn additional_where_bounds(&self, data: &Item) -> Option { // `Clone` for unions requires the `Copy` bound. if let Item::Item(Data { type_: DataType::Union(..), .. }) = data { Some(TypeParamBound::Trait(TraitBound { paren_token: None, modifier: TraitBoundModifier::None, lifetimes: None, path: Trait::Copy.default_derive_trait().path(), })) } else { None } } fn build_signature( &self, any_bound: bool, item: &Item, _generics: &SplitGenerics<'_>, traits: &[DeriveTrait], _trait_: &DeriveTrait, body: &TokenStream, ) -> TokenStream { // Special implementation for items also implementing `Copy`. if !any_bound && traits.iter().any(|trait_| trait_ == Trait::Copy) { return quote! { #[inline] fn clone(&self) -> Self { *self } }; } // Special implementation for unions. if let Item::Item(Data { type_: DataType::Union(..), .. }) = item { quote! { #[inline] fn clone(&self) -> Self { struct __AssertCopy<__T: ::core::marker::Copy + ?::core::marker::Sized>(::core::marker::PhantomData<__T>); let _: __AssertCopy; *self } } } else { quote! { #[inline] fn clone(&self) -> Self { match self { #body } } } } } fn build_body( &self, any_bound: bool, traits: &[DeriveTrait], trait_: &DeriveTrait, data: &Data, ) -> TokenStream { if !any_bound && traits.iter().any(|trait_| trait_ == Trait::Copy) { return TokenStream::new(); } match data.simple_type() { SimpleType::Struct(fields) => { let self_pattern = &fields.self_pattern; let item_path = &data.path; let self_ident = data.iter_self_ident(**trait_); let fields = data.iter_field_ident(**trait_); let trait_path = trait_.path(); quote! { #self_pattern => #item_path { #(#fields: #trait_path::clone(#self_ident)),* }, } } SimpleType::Tuple(fields) => { let self_pattern = &fields.self_pattern; let item_path = &data.path; let self_ident = data.iter_self_ident(**trait_); let trait_path = trait_.path(); quote! { #self_pattern => #item_path(#(#trait_path::clone(#self_ident)),*), } } SimpleType::Unit(pattern) => { quote! { #pattern => #pattern, } } SimpleType::Union(_) => TokenStream::new(), } } } derive-where-1.2.7/src/trait_/common_ord.rs000064400000000000000000000311721046102023000170000ustar 00000000000000//! Common implementation help for [`PartialOrd`] and [`Ord`]. #[cfg(not(feature = "nightly"))] use std::{borrow::Cow, ops::Deref}; use proc_macro2::TokenStream; #[cfg(not(feature = "nightly"))] use proc_macro2::{Literal, Span}; #[cfg(not(feature = "nightly"))] use quote::format_ident; use quote::quote; #[cfg(not(feature = "nightly"))] use syn::{parse_quote, Expr, ExprLit, LitInt, Path}; #[cfg(not(feature = "nightly"))] use crate::{item::Representation, Discriminant, Trait}; use crate::{Data, DeriveTrait, Item, SimpleType, SplitGenerics}; /// Build signature for [`PartialOrd`] and [`Ord`]. pub fn build_ord_signature( item: &Item, #[cfg_attr(feature = "nightly", allow(unused_variables))] generics: &SplitGenerics<'_>, #[cfg_attr(feature = "nightly", allow(unused_variables))] traits: &[DeriveTrait], trait_: &DeriveTrait, body: &TokenStream, ) -> TokenStream { let mut equal = quote! { ::core::cmp::Ordering::Equal }; // Add `Option` to `Ordering` if we are implementing `PartialOrd`. if let DeriveTrait::PartialOrd = trait_ { equal = quote! { ::core::option::Option::Some(#equal) }; } match item { // If the item is incomparable return `None` item if item.is_incomparable() => { quote! { ::core::option::Option::None } } // If there is more than one variant, check for the discriminant. Item::Enum { #[cfg(not(feature = "nightly"))] discriminant, variants, .. } if variants.len() > 1 => { // In case the discriminant matches: // If all variants are empty, return `Equal`. let body_equal = if item.is_empty(**trait_) { None } // Compare variant data and return `Equal` in the rest pattern if there are any empty // variants that are comparable. else if variants .iter() .any(|variant| variant.is_empty(**trait_) && !variant.is_incomparable()) { Some(quote! { match (self, __other) { #body _ => #equal, } }) } // Insert `unreachable!` in the rest pattern if no variants are empty. else { #[cfg(not(feature = "safe"))] // This follows the standard implementation. let rest = quote! { unsafe { ::core::hint::unreachable_unchecked() } }; #[cfg(feature = "safe")] let rest = quote! { ::core::unreachable!("comparing variants yielded unexpected results") }; Some(quote! { match (self, __other) { #body _ => #rest, } }) }; let incomparable = build_incomparable_pattern(variants); // If there is only one comparable variant, it has to be it when it is non // incomparable. let mut comparable = variants.iter().filter(|v| !v.is_incomparable()); // Takes the first value from the iterator, but only when there is only one // (second yields none). if let (Some(comparable), None) = (comparable.next(), comparable.next()) { let incomparable = incomparable.expect("there should be > 1 variants"); // Either compare the single variant or return `Equal` when it is empty let equal = if comparable.is_empty(**trait_) { equal } else { body_equal.unwrap_or(equal) }; quote! { if ::core::matches!(self, #incomparable) || ::core::matches!(__other, #incomparable) { ::core::option::Option::None } else { #equal } } } else { let incomparable = incomparable.into_iter(); let incomparable = quote! { #(if ::core::matches!(self, #incomparable) || ::core::matches!(__other, #incomparable) { return ::core::option::Option::None; })* }; let path = trait_.path(); let method = match trait_ { DeriveTrait::PartialOrd => quote! { partial_cmp }, DeriveTrait::Ord => quote! { cmp }, _ => unreachable!("unsupported trait in `prepare_ord`"), }; // Nightly implementation. #[cfg(feature = "nightly")] if let Some(body_equal) = body_equal { quote! { #incomparable let __self_disc = ::core::intrinsics::discriminant_value(self); let __other_disc = ::core::intrinsics::discriminant_value(__other); if __self_disc == __other_disc { #body_equal } else { #path::#method(&__self_disc, &__other_disc) } } } else { quote! { #incomparable #path::#method( &::core::intrinsics::discriminant_value(self), &::core::intrinsics::discriminant_value(__other), ) } } #[cfg(not(feature = "nightly"))] { let body_else = match discriminant { Discriminant::Single => { unreachable!("we should only generate this code with multiple variants") } Discriminant::Unit => { let mut discriminants = None; // Validation is only needed if custom discriminants are defined. let validate = (variants .iter() .any(|variant| variant.discriminant.is_some())) .then(|| { let discriminants = discriminants.insert(build_discriminants(variants)); let discriminants = discriminants.iter().zip(variants).map( |(discriminant, variant)| { let name = format_ident!("__VALIDATE_ISIZE_{}", variant.ident); let discriminant = discriminant.deref(); quote! { const #name: isize = #discriminant; } }, ); quote! { #(#discriminants)* } }); if traits.iter().any(|trait_| trait_ == Trait::Copy) { quote! { #validate #path::#method(&(*self as isize), &(*__other as isize)) } } else if traits.iter().any(|trait_| trait_ == Trait::Clone) { let clone = DeriveTrait::Clone.path(); quote! { #validate #path::#method(&(#clone::clone(self) as isize), &(#clone::clone(__other) as isize)) } } else { let discriminants = discriminants .get_or_insert_with(|| build_discriminants(variants)); build_discriminant_comparison( None, validate, item, generics, variants, discriminants, &path, &method, ) } } Discriminant::Data => { let discriminants = build_discriminants(variants); build_discriminant_comparison( None, None, item, generics, variants, &discriminants, &path, &method, ) } Discriminant::UnitRepr(repr) => { if traits.iter().any(|trait_| trait_ == Trait::Copy) { quote! { #path::#method(&(*self as #repr), &(*__other as #repr)) } } else if traits.iter().any(|trait_| trait_ == Trait::Clone) { let clone = DeriveTrait::Clone.path(); quote! { #path::#method(&(#clone::clone(self) as #repr), &(#clone::clone(__other) as #repr)) } } else { #[cfg(feature = "safe")] let body_else = { let discriminants = build_discriminants(variants); build_discriminant_comparison( Some(*repr), None, item, generics, variants, &discriminants, &path, &method, ) }; #[cfg(not(feature = "safe"))] let body_else = quote! { #path::#method( &unsafe { *<*const _>::from(self).cast::<#repr>() }, &unsafe { *<*const _>::from(__other).cast::<#repr>() }, ) }; body_else } } #[cfg(not(feature = "safe"))] Discriminant::DataRepr(repr) => { quote! { #path::#method( &unsafe { *<*const _>::from(self).cast::<#repr>() }, &unsafe { *<*const _>::from(__other).cast::<#repr>() }, ) } } #[cfg(feature = "safe")] Discriminant::DataRepr(repr) => { let discriminants = build_discriminants(variants); build_discriminant_comparison( Some(*repr), None, item, generics, variants, &discriminants, &path, &method, ) } }; if let Some(body_equal) = body_equal { quote! { #incomparable let __self_disc = ::core::mem::discriminant(self); let __other_disc = ::core::mem::discriminant(__other); if __self_disc == __other_disc { #body_equal } else { #body_else } } } else { quote! { #incomparable #body_else } } } } } // If there is only one variant and it's empty or if the struct is empty, simply // return `Equal`. item if item.is_empty(**trait_) => { quote! { #equal } } _ => { quote! { match (self, __other) { #body } } } } } /// Builds list of discriminant values for all variants. #[cfg(not(feature = "nightly"))] fn build_discriminants<'a>(variants: &'a [Data<'_>]) -> Vec> { let mut discriminants = Vec::>::with_capacity(variants.len()); let mut last_expression: Option<(Option, usize)> = None; for variant in variants { let discriminant = if let Some(discriminant) = variant.discriminant { last_expression = Some((Some(discriminants.len()), 0)); Cow::Borrowed(discriminant) } else { let discriminant = match &mut last_expression { Some((Some(expr_index), counter)) => { let expr = &discriminants[*expr_index]; *counter += 1; let counter = Literal::usize_unsuffixed(*counter); parse_quote! { (#expr) + #counter } } Some((None, counter)) => { *counter += 1; ExprLit { attrs: Vec::new(), lit: LitInt::new(&counter.to_string(), Span::call_site()).into(), } .into() } None => { last_expression = Some((None, 0)); ExprLit { attrs: Vec::new(), lit: LitInt::new("0", Span::call_site()).into(), } .into() } }; Cow::Owned(discriminant) }; discriminants.push(discriminant); } discriminants } /// Uses list of discriminant values to compare variants. #[cfg(not(feature = "nightly"))] #[allow(clippy::too_many_arguments)] fn build_discriminant_comparison( repr: Option, validate: Option, item: &Item, generics: &SplitGenerics<'_>, variants: &[Data<'_>], discriminants: &[Cow<'_, Expr>], path: &Path, method: &TokenStream, ) -> TokenStream { let variants = variants .iter() .zip(discriminants) .map(|(variant, discriminant)| { let pattern = variant.self_pattern(); if validate.is_some() { let discriminant = format_ident!("__VALIDATE_ISIZE_{}", variant.ident); quote! { #pattern => #discriminant } } else { quote! { #pattern => #discriminant } } }); // `isize` is currently used by Rust as the default representation when none is // defined. let repr = repr.unwrap_or(Representation::ISize).to_token(); let item = item.ident(); let SplitGenerics { imp, ty, where_clause, } = generics; quote! { const fn __discriminant #imp(__this: &#item #ty) -> #repr #where_clause { #validate match __this { #(#variants),* } } #path::#method(&__discriminant(self), &__discriminant(__other)) } } /// Build `match` arms for [`PartialOrd`] and [`Ord`]. pub fn build_ord_body(trait_: &DeriveTrait, data: &Data) -> TokenStream { let path = trait_.path(); let mut equal = quote! { ::core::cmp::Ordering::Equal }; // Add `Option` to `Ordering` if we are implementing `PartialOrd`. let method = match trait_ { DeriveTrait::PartialOrd => { equal = quote! { ::core::option::Option::Some(#equal) }; quote! { partial_cmp } } DeriveTrait::Ord => quote! { cmp }, _ => unreachable!("unsupported trait in `build_ord`"), }; // The match arm starts with `Ordering::Equal`. This will become the // whole `match` arm if no fields are present. let mut body = quote! { #equal }; // Builds `match` arms backwards, using the `match` arm of the field coming // afterwards. `rev` has to be called twice separately because it can't be // called on `zip` for (field_temp, field_other) in data .iter_self_ident(**trait_) .rev() .zip(data.iter_other_ident(**trait_).rev()) { body = quote! { match #path::#method(#field_temp, #field_other) { #equal => #body, __cmp => __cmp, } }; } body } /// Generate a match arm that returns `body` for all incomparable `variants` pub fn build_incomparable_pattern(variants: &[Data]) -> Option { let mut incomparable = variants .iter() .filter(|variant| variant.is_incomparable()) .map(|variant @ Data { path, .. }| match variant.simple_type() { SimpleType::Struct(_) => quote!(#path{..}), SimpleType::Tuple(_) => quote!(#path(..)), SimpleType::Union(_) => unreachable!("enum variants cannot be unions"), SimpleType::Unit(_) => quote!(#path), }) .peekable(); if incomparable.peek().is_some() { Some(quote! { #(#incomparable)|* }) } else { None } } derive-where-1.2.7/src/trait_/copy.rs000064400000000000000000000006211046102023000156110ustar 00000000000000//! [`Copy`](trait@std::marker::Copy) implementation. use crate::{DeriveTrait, TraitImpl}; /// Dummy-struct implement [`Trait`](crate::Trait) for /// [`Copy`](trait@std::marker::Copy). pub struct Copy; impl TraitImpl for Copy { fn as_str(&self) -> &'static str { "Copy" } fn default_derive_trait(&self) -> DeriveTrait { DeriveTrait::Copy } fn supports_union(&self) -> bool { true } } derive-where-1.2.7/src/trait_/debug.rs000064400000000000000000000041601046102023000157270ustar 00000000000000//! [`Debug`](trait@std::fmt::Debug) implementation. use proc_macro2::TokenStream; use quote::quote; use crate::{Data, DeriveTrait, Item, SimpleType, SplitGenerics, TraitImpl}; /// Dummy-struct implement [`Trait`](crate::Trait) for /// [`Debug`](trait@std::fmt::Debug). pub struct Debug; impl TraitImpl for Debug { fn as_str(&self) -> &'static str { "Debug" } fn default_derive_trait(&self) -> DeriveTrait { DeriveTrait::Debug } fn build_signature( &self, _any_bound: bool, _item: &Item, _generics: &SplitGenerics<'_>, _traits: &[DeriveTrait], _trait_: &DeriveTrait, body: &TokenStream, ) -> TokenStream { quote! { fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { #body } } } } fn build_body( &self, _any_bound: bool, _traits: &[DeriveTrait], trait_: &DeriveTrait, data: &Data, ) -> TokenStream { let self_pattern = &data.self_pattern(); let debug_name = data.ident.to_string(); match data.simple_type() { SimpleType::Struct(_) => { let self_ident = data.iter_self_ident(**trait_); let debug_fields = data .iter_field_ident(**trait_) .map(|field| field.to_string()); let finish = if data.any_skip_trait(**trait_) { quote! { finish_non_exhaustive } } else { quote! { finish } }; quote! { #self_pattern => { let mut __builder = ::core::fmt::Formatter::debug_struct(__f, #debug_name); #(::core::fmt::DebugStruct::field(&mut __builder, #debug_fields, #self_ident);)* ::core::fmt::DebugStruct::#finish(&mut __builder) } } } SimpleType::Tuple(_) => { let self_ident = data.iter_self_ident(**trait_); quote! { #self_pattern => { let mut __builder = ::core::fmt::Formatter::debug_tuple(__f, #debug_name); #(::core::fmt::DebugTuple::field(&mut __builder, #self_ident);)* ::core::fmt::DebugTuple::finish(&mut __builder) } } } SimpleType::Unit(_) => { quote! { #self_pattern => ::core::fmt::Formatter::write_str(__f, #debug_name), } } SimpleType::Union(_) => unreachable!("unexpected trait for union"), } } } derive-where-1.2.7/src/trait_/default.rs000064400000000000000000000031001046102023000162560ustar 00000000000000//! [`Default`](trait@std::default::Default) implementation. use proc_macro2::TokenStream; use quote::quote; use crate::{Data, DeriveTrait, Item, SimpleType, SplitGenerics, TraitImpl}; /// Dummy-struct implement [`Trait`](crate::Trait) for /// [`Default`](trait@std::default::Default). pub struct Default; impl TraitImpl for Default { fn as_str(&self) -> &'static str { "Default" } fn default_derive_trait(&self) -> DeriveTrait { DeriveTrait::Default } fn build_signature( &self, _any_bound: bool, _item: &Item, _generics: &SplitGenerics<'_>, _traits: &[DeriveTrait], _trait_: &DeriveTrait, body: &TokenStream, ) -> TokenStream { quote! { fn default() -> Self { #body } } } fn build_body( &self, _any_bound: bool, _traits: &[DeriveTrait], trait_: &DeriveTrait, data: &Data, ) -> TokenStream { if data.is_default() { let path = &data.path; match data.simple_type() { SimpleType::Struct(_) => { let fields = data.iter_field_ident(**trait_); let trait_path = trait_.path(); quote! { #path { #(#fields: #trait_path::default()),* } } } SimpleType::Tuple(_) => { let trait_path = trait_.path(); let fields = data .iter_fields(**trait_) .map(|_| quote! { #trait_path::default() }); quote! { #path(#(#fields),*) } } SimpleType::Unit(_) => { quote! { #path } } SimpleType::Union(_) => unreachable!("unexpected trait for union"), } } // Skip `Default` implementation if variant isn't marked with a `default` attribute. else { TokenStream::new() } } } derive-where-1.2.7/src/trait_/eq.rs000064400000000000000000000020301046102023000152400ustar 00000000000000//! [`Eq`](trait@std::cmp::Eq) implementation. use proc_macro2::TokenStream; use quote::quote; use crate::{Data, DeriveTrait, Item, SplitGenerics, TraitImpl}; /// Dummy-struct implement [`Trait`](crate::Trait) for /// [`Eq`](trait@std::cmp::Eq). pub struct Eq; impl TraitImpl for Eq { fn as_str(&self) -> &'static str { "Eq" } fn default_derive_trait(&self) -> DeriveTrait { DeriveTrait::Eq } fn build_signature( &self, _any_bound: bool, _item: &Item, _generics: &SplitGenerics<'_>, _traits: &[DeriveTrait], _trait_: &DeriveTrait, body: &TokenStream, ) -> TokenStream { quote! { #[inline] fn assert_receiver_is_total_eq(&self) { struct __AssertEq<__T: ::core::cmp::Eq + ?::core::marker::Sized>(::core::marker::PhantomData<__T>); #body } } } fn build_body( &self, _any_bound: bool, _traits: &[DeriveTrait], trait_: &DeriveTrait, data: &Data, ) -> TokenStream { let types = data.iter_fields(**trait_).map(|field| field.type_); quote! { #(let _: __AssertEq<#types>;)* } } } derive-where-1.2.7/src/trait_/hash.rs000064400000000000000000000031511046102023000155630ustar 00000000000000//! [`Hash`](trait@std::hash::Hash) implementation. use proc_macro2::TokenStream; use quote::quote; use crate::{Data, DataType, DeriveTrait, Item, SimpleType, SplitGenerics, TraitImpl}; /// Dummy-struct implement [`Trait`](crate::Trait) for /// [`Hash`](trait@std::hash::Hash). pub struct Hash; impl TraitImpl for Hash { fn as_str(&self) -> &'static str { "Hash" } fn default_derive_trait(&self) -> DeriveTrait { DeriveTrait::Hash } fn build_signature( &self, _any_bound: bool, _item: &Item, _generics: &SplitGenerics<'_>, _traits: &[DeriveTrait], _trait_: &DeriveTrait, body: &TokenStream, ) -> TokenStream { quote! { fn hash<__H: ::core::hash::Hasher>(&self, __state: &mut __H) { match self { #body } } } } fn build_body( &self, _any_bound: bool, _traits: &[DeriveTrait], trait_: &DeriveTrait, data: &Data, ) -> TokenStream { let self_pattern = data.self_pattern(); let trait_path = trait_.path(); // Add hashing the variant if this is an enum. let discriminant = if let DataType::Variant { .. } = data.type_ { Some(quote! { #trait_path::hash(&::core::mem::discriminant(self), __state); }) } else { None }; match data.simple_type() { SimpleType::Struct(_) | SimpleType::Tuple(_) => { let self_ident = data.iter_self_ident(**trait_); quote! { #self_pattern => { #discriminant #(#trait_path::hash(#self_ident, __state);)* } } } SimpleType::Unit(_) => { quote! { #self_pattern => { #discriminant } } } SimpleType::Union(_) => unreachable!("unexpected trait for union"), } } } derive-where-1.2.7/src/trait_/ord.rs000064400000000000000000000027111046102023000154250ustar 00000000000000//! [`Ord`](trait@std::cmp::Ord) implementation. use proc_macro2::TokenStream; use quote::quote; use super::common_ord; use crate::{Data, DeriveTrait, Item, SimpleType, SplitGenerics, TraitImpl}; /// Dummy-struct implement [`Trait`](crate::Trait) for /// [`Ord`](trait@std::cmp::Ord). pub struct Ord; impl TraitImpl for Ord { fn as_str(&self) -> &'static str { "Ord" } fn default_derive_trait(&self) -> DeriveTrait { DeriveTrait::Ord } fn build_signature( &self, _any_bound: bool, item: &Item, generics: &SplitGenerics<'_>, traits: &[DeriveTrait], trait_: &DeriveTrait, body: &TokenStream, ) -> TokenStream { let body = common_ord::build_ord_signature(item, generics, traits, trait_, body); quote! { #[inline] fn cmp(&self, __other: &Self) -> ::core::cmp::Ordering { #body } } } fn build_body( &self, _any_bound: bool, _traits: &[DeriveTrait], trait_: &DeriveTrait, data: &Data, ) -> TokenStream { if data.is_empty(**trait_) { TokenStream::new() } else { match data.simple_type() { SimpleType::Struct(fields) | SimpleType::Tuple(fields) => { let self_pattern = &fields.self_pattern; let other_pattern = &fields.other_pattern; let body = common_ord::build_ord_body(trait_, data); quote! { (#self_pattern, #other_pattern) => #body, } } SimpleType::Unit(_) => TokenStream::new(), SimpleType::Union(_) => unreachable!("unexpected trait for union"), } } } } derive-where-1.2.7/src/trait_/partial_eq.rs000064400000000000000000000072541046102023000167710ustar 00000000000000//! [`PartialEq`](trait@std::cmp::PartialEq) implementation. use proc_macro2::TokenStream; use quote::quote; use super::common_ord::build_incomparable_pattern; use crate::{Data, DeriveTrait, Item, SimpleType, SplitGenerics, TraitImpl}; /// Dummy-struct implement [`Trait`](crate::Trait) for /// [`PartialEq`](trait@std::cmp::PartialEq). pub struct PartialEq; impl TraitImpl for PartialEq { fn as_str(&self) -> &'static str { "PartialEq" } fn default_derive_trait(&self) -> DeriveTrait { DeriveTrait::PartialEq } fn build_signature( &self, _any_bound: bool, item: &Item, _generics: &SplitGenerics<'_>, _traits: &[DeriveTrait], trait_: &DeriveTrait, body: &TokenStream, ) -> TokenStream { let body = { match item { // If the whole item is incomparable return false item if item.is_incomparable() => { quote! { false } } // If there is more than one variant and not all variants are empty, check for // discriminant and match on variant data. Item::Enum { variants, .. } if variants.len() > 1 && !item.is_empty(**trait_) => { // Return `true` in the rest pattern if there are any empty variants // that are not incomparable. let rest = if variants .iter() .any(|variant| variant.is_empty(**trait_) && !variant.is_incomparable()) { quote! { true } } else { #[cfg(not(feature = "safe"))] // This follows the standard implementation. quote! { unsafe { ::core::hint::unreachable_unchecked() } } #[cfg(feature = "safe")] quote! { ::core::unreachable!("comparing variants yielded unexpected results") } }; // Return `false` for all incomparable variants let incomparable = build_incomparable_pattern(variants).into_iter(); quote! { if ::core::mem::discriminant(self) == ::core::mem::discriminant(__other) { match (self, __other) { #body #((#incomparable, ..) => false,)* _ => #rest, } } else { false } } } // If there is more than one variant and all are empty, check for // discriminant and simply return `true` if it is not incomparable. Item::Enum { variants, .. } if variants.len() > 1 && item.is_empty(**trait_) => { let incomparable = build_incomparable_pattern(variants).into_iter(); quote! { if ::core::mem::discriminant(self) == ::core::mem::discriminant(__other) { #(if ::core::matches!(self, #incomparable) { return false; })* true } else { false } } } // If there is only one variant and it's empty or if the struct is empty, simply // return `true`. item if item.is_empty(**trait_) => { quote! { true } } _ => { quote! { match (self, __other) { #body } } } } }; quote! { #[inline] fn eq(&self, __other: &Self) -> bool { #body } } } fn build_body( &self, _any_bound: bool, _traits: &[DeriveTrait], trait_: &DeriveTrait, data: &Data, ) -> TokenStream { if data.is_empty(**trait_) || data.is_incomparable() { TokenStream::new() } else { match data.simple_type() { SimpleType::Struct(fields) | SimpleType::Tuple(fields) => { let self_pattern = &fields.self_pattern; let other_pattern = &fields.other_pattern; let trait_path = trait_.path(); let self_ident = data.iter_self_ident(**trait_); let other_ident = data.iter_other_ident(**trait_); quote! { (#self_pattern, #other_pattern) => true #(&& #trait_path::eq(#self_ident, #other_ident))*, } } SimpleType::Unit(_) => TokenStream::new(), SimpleType::Union(_) => unreachable!("unexpected trait for union"), } } } } derive-where-1.2.7/src/trait_/partial_ord.rs000064400000000000000000000034531046102023000171450ustar 00000000000000//! [`PartialOrd`](trait@std::cmp::PartialOrd) implementation. use proc_macro2::TokenStream; use quote::quote; use super::common_ord; use crate::{Data, DeriveTrait, Item, SimpleType, SplitGenerics, Trait, TraitImpl}; /// Dummy-struct implement [`Trait`] for /// [`PartialOrd`](trait@std::cmp::PartialOrd). pub struct PartialOrd; impl TraitImpl for PartialOrd { fn as_str(&self) -> &'static str { "PartialOrd" } fn default_derive_trait(&self) -> DeriveTrait { DeriveTrait::PartialOrd } fn build_signature( &self, any_bound: bool, item: &Item, generics: &SplitGenerics<'_>, traits: &[DeriveTrait], trait_: &DeriveTrait, body: &TokenStream, ) -> TokenStream { let body = if !any_bound && traits.iter().any(|trait_| trait_ == Trait::Ord) { quote! { ::core::option::Option::Some(::core::cmp::Ord::cmp(self, __other)) } } else { common_ord::build_ord_signature(item, generics, traits, trait_, body) }; quote! { #[inline] fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { #body } } } fn build_body( &self, any_bound: bool, traits: &[DeriveTrait], trait_: &DeriveTrait, data: &Data, ) -> TokenStream { if data.is_empty(**trait_) || data.is_incomparable() || (!any_bound && traits.iter().any(|trait_| trait_ == Trait::Ord)) { TokenStream::new() } else { match data.simple_type() { SimpleType::Struct(fields) | SimpleType::Tuple(fields) => { let self_pattern = &fields.self_pattern; let other_pattern = &fields.other_pattern; let body = common_ord::build_ord_body(trait_, data); quote! { (#self_pattern, #other_pattern) => #body, } } SimpleType::Unit(_) => TokenStream::new(), SimpleType::Union(_) => unreachable!("unexpected trait for union"), } } } } derive-where-1.2.7/src/trait_/zeroize.rs000064400000000000000000000067761046102023000163470ustar 00000000000000//! [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html) implementation. use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::{ punctuated::Punctuated, spanned::Spanned, Expr, ExprLit, ExprPath, Lit, Meta, Path, Result, Token, }; use crate::{util, Data, DeriveTrait, Error, Item, SimpleType, SplitGenerics, TraitImpl}; /// Dummy-struct implement [`Trait`](crate::Trait) for [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html) . pub struct Zeroize; impl TraitImpl for Zeroize { fn as_str(&self) -> &'static str { "Zeroize" } fn default_derive_trait(&self) -> DeriveTrait { DeriveTrait::Zeroize { crate_: None } } fn parse_derive_trait( &self, _span: Span, list: Punctuated, ) -> Result { // This is already checked in `DeriveTrait::from_stream`. debug_assert!(!list.is_empty()); let mut crate_ = None; for meta in list { match &meta { Meta::Path(path) => { if path.is_ident("drop") { return Err(Error::deprecated_zeroize_drop(path.span())); } else { return Err(Error::option_trait(path.span(), self.as_str())); } } Meta::NameValue(name_value) => { if name_value.path.is_ident("crate") { // Check for duplicate `crate` option. if crate_.is_none() { let path = match &name_value.value { Expr::Lit(ExprLit { lit: Lit::Str(lit_str), .. }) => match lit_str.parse::() { 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(&["zeroize"]) { return Err(Error::path_unnecessary(path.span(), "::zeroize")); } crate_ = Some(path); } else { return Err(Error::option_duplicate(name_value.span(), "crate")); } } else { return Err(Error::option_trait(name_value.path.span(), self.as_str())); } } _ => { return Err(Error::option_syntax(meta.span())); } } } Ok(DeriveTrait::Zeroize { crate_ }) } fn build_signature( &self, _any_bound: bool, item: &Item, _generics: &SplitGenerics<'_>, _traits: &[DeriveTrait], trait_: &DeriveTrait, body: &TokenStream, ) -> TokenStream { match item { Item::Item(data) if data.is_empty(**trait_) => quote! { fn zeroize(&mut self) { } }, _ => { let trait_path = trait_.path(); quote! { fn zeroize(&mut self) { use #trait_path; match self { #body } } } } } } fn build_body( &self, _any_bound: bool, _traits: &[DeriveTrait], trait_: &DeriveTrait, data: &Data, ) -> TokenStream { if data.is_empty(**trait_) { TokenStream::new() } else { match data.simple_type() { SimpleType::Struct(fields) | SimpleType::Tuple(fields) => { let trait_path = trait_.path(); let self_pattern = fields.self_pattern_mut(); let body = data .iter_fields(**trait_) .zip(data.iter_self_ident(**trait_)) .map(|(field, self_ident)| { if field.attr.zeroize_fqs.0 { quote! { #trait_path::zeroize(#self_ident); } } else { quote! { #self_ident.zeroize(); } } }); quote! { #self_pattern => { #(#body)* } } } SimpleType::Unit(_) => TokenStream::new(), SimpleType::Union(_) => unreachable!("unexpected trait for union"), } } } } derive-where-1.2.7/src/trait_/zeroize_on_drop.rs000064400000000000000000000106071046102023000200530ustar 00000000000000//! [`ZeroizeOnDrop`](https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html) implementation. use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::{ punctuated::Punctuated, spanned::Spanned, Expr, ExprLit, ExprPath, Lit, Meta, Path, Result, Token, }; use crate::{util, Data, DeriveTrait, Error, Item, SimpleType, SplitGenerics, TraitImpl}; /// Dummy-struct implement [`Trait`](crate::Trait) for [`ZeroizeOnDrop`](https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html) . pub struct ZeroizeOnDrop; impl TraitImpl for ZeroizeOnDrop { fn as_str(&self) -> &'static str { "ZeroizeOnDrop" } fn default_derive_trait(&self) -> DeriveTrait { DeriveTrait::ZeroizeOnDrop { crate_: None } } fn parse_derive_trait( &self, _span: Span, list: Punctuated, ) -> Result { // This is already checked in `DeriveTrait::from_stream`. debug_assert!(!list.is_empty()); let mut crate_ = None; for meta in list { match &meta { Meta::Path(path) => return Err(Error::option_trait(path.span(), self.as_str())), Meta::NameValue(name_value) => { if name_value.path.is_ident("crate") { // Check for duplicate `crate` option. if crate_.is_none() { let path = match &name_value.value { Expr::Lit(ExprLit { lit: Lit::Str(lit_str), .. }) => match lit_str.parse::() { 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(&["zeroize"]) { return Err(Error::path_unnecessary(path.span(), "::zeroize")); } crate_ = Some(path); } else { return Err(Error::option_duplicate(name_value.span(), "crate")); } } else { return Err(Error::option_trait(name_value.path.span(), self.as_str())); } } _ => { return Err(Error::option_syntax(meta.span())); } } } Ok(DeriveTrait::ZeroizeOnDrop { crate_ }) } #[allow(unused_variables)] fn additional_impl(&self, trait_: &DeriveTrait) -> Option<(Path, TokenStream)> { #[cfg(feature = "zeroize-on-drop")] return Some((trait_.path(), quote! {})); #[cfg(not(feature = "zeroize-on-drop"))] None } fn impl_path(&self, _trait_: &DeriveTrait) -> Path { util::path_from_strs(&["core", "ops", "Drop"]) } fn build_signature( &self, _any_bound: bool, item: &Item, _generics: &SplitGenerics<'_>, _traits: &[DeriveTrait], trait_: &DeriveTrait, body: &TokenStream, ) -> TokenStream { match item { Item::Item(data) if data.is_empty(**trait_) => quote! { fn drop(&mut self) { } }, _ => { #[cfg(feature = "zeroize-on-drop")] { let crate_ = trait_.crate_(); let internal = util::path_segment("__internal"); let mut assert_zeroize = crate_.clone(); assert_zeroize .segments .extend([internal.clone(), util::path_segment("AssertZeroize")]); let mut assert_zeroize_on_drop = crate_; assert_zeroize_on_drop .segments .extend([internal, util::path_segment("AssertZeroizeOnDrop")]); quote! { fn drop(&mut self) { use #assert_zeroize; use #assert_zeroize_on_drop; match self { #body } } } } #[cfg(not(feature = "zeroize-on-drop"))] quote! { fn drop(&mut self) { #body } } } } } fn build_body( &self, _any_bound: bool, _traits: &[DeriveTrait], trait_: &DeriveTrait, data: &Data, ) -> TokenStream { if data.is_empty(**trait_) { TokenStream::new() } else { match data.simple_type() { SimpleType::Struct(fields) | SimpleType::Tuple(fields) => { #[cfg(feature = "zeroize-on-drop")] { let self_pattern = fields.self_pattern_mut(); let self_ident = data.iter_self_ident(**trait_); quote! { #self_pattern => { #(#self_ident.zeroize_or_on_drop();)* } } } #[cfg(not(feature = "zeroize-on-drop"))] { // Use unused variables. let _ = fields; let path = util::path_from_root_and_strs(trait_.crate_(), &["Zeroize"]); quote! { #path::zeroize(self); } } } SimpleType::Unit(_) => TokenStream::new(), SimpleType::Union(_) => unreachable!("unexpected trait for union"), } } } } derive-where-1.2.7/src/trait_.rs000064400000000000000000000125011046102023000146370ustar 00000000000000//! Individual implementation for all traits. mod clone; mod common_ord; mod copy; mod debug; mod default; mod eq; mod hash; mod ord; mod partial_eq; mod partial_ord; #[cfg(feature = "zeroize")] mod zeroize; #[cfg(feature = "zeroize")] mod zeroize_on_drop; use proc_macro2::{Span, TokenStream}; use syn::{punctuated::Punctuated, spanned::Spanned, Meta, Path, Result, Token, TypeParamBound}; use crate::{Data, DeriveTrait, Error, Item, SplitGenerics}; /// Type implementing [`TraitImpl`] for every trait. #[derive(Clone, Copy, Eq, PartialEq)] #[cfg_attr(test, derive(Debug))] pub enum Trait { /// [`Clone`]. Clone, /// [`Copy`]. Copy, /// [`Debug`](std::fmt::Debug). Debug, /// [`Default`]. Default, /// [`Eq`]. Eq, /// [`Hash`](std::hash::Hash). Hash, /// [`Ord`]. Ord, /// [`PartialEq`]. PartialEq, /// [`PartialOrd`]. PartialOrd, /// [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html). #[cfg(feature = "zeroize")] Zeroize, /// [`ZeroizeOnDrop`](https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html). #[cfg(feature = "zeroize")] ZeroizeOnDrop, } impl Trait { /// Return dummy-struct for the internal implementation. fn implementation(&self) -> &dyn TraitImpl { match self { Trait::Clone => &clone::Clone, Trait::Copy => ©::Copy, Trait::Debug => &debug::Debug, Trait::Default => &default::Default, Trait::Eq => &eq::Eq, Trait::Hash => &hash::Hash, Trait::Ord => &ord::Ord, Trait::PartialEq => &partial_eq::PartialEq, Trait::PartialOrd => &partial_ord::PartialOrd, #[cfg(feature = "zeroize")] Trait::Zeroize => &zeroize::Zeroize, #[cfg(feature = "zeroize")] Trait::ZeroizeOnDrop => &zeroize_on_drop::ZeroizeOnDrop, } } /// Create [`Trait`] from [`Path`]. pub fn from_path(path: &Path) -> Result { if let Some(ident) = path.get_ident() { use Trait::*; match ident.to_string().as_str() { "Clone" => Ok(Clone), "Copy" => Ok(Copy), "Debug" => Ok(Debug), "Default" => Ok(Default), "Eq" => Ok(Eq), "Hash" => Ok(Hash), "Ord" => Ok(Ord), "PartialEq" => Ok(PartialEq), "PartialOrd" => Ok(PartialOrd), #[cfg(feature = "zeroize")] "Zeroize" => Ok(Zeroize), #[cfg(feature = "zeroize")] "ZeroizeOnDrop" => Ok(ZeroizeOnDrop), "crate" => Err(Error::crate_(path.span())), _ => Err(Error::trait_(path.span())), } } else { Err(Error::trait_(path.span())) } } } impl TraitImpl for Trait { fn as_str(&self) -> &'static str { self.implementation().as_str() } fn default_derive_trait(&self) -> DeriveTrait { self.implementation().default_derive_trait() } fn parse_derive_trait( &self, span: Span, list: Punctuated, ) -> Result { self.implementation().parse_derive_trait(span, list) } fn supports_union(&self) -> bool { self.implementation().supports_union() } fn additional_where_bounds(&self, data: &Item) -> Option { self.implementation().additional_where_bounds(data) } fn additional_impl(&self, trait_: &DeriveTrait) -> Option<(Path, TokenStream)> { self.implementation().additional_impl(trait_) } fn impl_path(&self, trait_: &DeriveTrait) -> Path { self.implementation().impl_path(trait_) } fn build_signature( &self, any_bound: bool, item: &Item, generics: &SplitGenerics<'_>, traits: &[DeriveTrait], trait_: &DeriveTrait, body: &TokenStream, ) -> TokenStream { self.implementation() .build_signature(any_bound, item, generics, traits, trait_, body) } fn build_body( &self, any_bound: bool, traits: &[DeriveTrait], trait_: &DeriveTrait, data: &Data, ) -> TokenStream { self.implementation() .build_body(any_bound, traits, trait_, data) } } /// Single trait implementation. Parses attributes and constructs `impl`s. pub trait TraitImpl { /// [`str`] representation of this [`Trait`]. /// Used to compare against [`Ident`](struct@syn::Ident)s and create error /// messages. fn as_str(&self) -> &'static str; /// Associated [`DeriveTrait`]. fn default_derive_trait(&self) -> DeriveTrait; /// Parse a `derive_where` trait with it's options. fn parse_derive_trait( &self, span: Span, _list: Punctuated, ) -> Result { Err(Error::options(span, self.as_str())) } /// Returns `true` if [`Trait`] supports unions. fn supports_union(&self) -> bool { false } /// Additional bounds to add to [`WhereClause`](syn::WhereClause). fn additional_where_bounds(&self, _data: &Item) -> Option { None } /// Additional implementation to add for this [`Trait`]. fn additional_impl(&self, _trait_: &DeriveTrait) -> Option<(Path, TokenStream)> { None } /// Trait to implement. Only used for [`ZeroizeOnDrop`](https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html) /// because it implements [`Drop`] and not itself. fn impl_path(&self, trait_: &DeriveTrait) -> Path { trait_.path() } /// Build method signature for this [`Trait`]. fn build_signature( &self, _any_bound: bool, _item: &Item, _generics: &SplitGenerics<'_>, _traits: &[DeriveTrait], _trait_: &DeriveTrait, _body: &TokenStream, ) -> TokenStream { TokenStream::new() } /// Build method body for this [`Trait`]. fn build_body( &self, _any_bound: bool, _traits: &[DeriveTrait], _trait_: &DeriveTrait, _data: &Data, ) -> TokenStream { TokenStream::new() } } derive-where-1.2.7/src/util.rs000064400000000000000000000035041046102023000143350ustar 00000000000000//! Utility functions. use proc_macro2::Span; use syn::{ punctuated::Punctuated, spanned::Spanned, Ident, Meta, MetaList, Path, PathArguments, PathSegment, Result, Token, }; use crate::error::Error; /// Convenience type to return two possible values. pub enum Either { /// `L` return value. Left(L), /// `R` return value. Right(R), } /// Create [`PathSegment`] from [`str`]. pub fn path_segment(ident: &str) -> PathSegment { PathSegment { ident: Ident::new(ident, Span::call_site()), arguments: PathArguments::None, } } /// Create [`Path`] from `[&str]`s. pub fn path_from_strs(segments: &[&str]) -> Path { Path { leading_colon: Some(::default()), segments: Punctuated::from_iter(segments.iter().map(|segment| path_segment(segment))), } } /// Create [`Path`] from `[&Ident]`s. pub fn path_from_idents(segments: &[&Ident]) -> Path { Path { leading_colon: None, segments: Punctuated::from_iter(segments.iter().map(|ident| PathSegment { ident: (*ident).clone(), arguments: PathArguments::None, })), } } /// Create [`Path`] from a root [`Path`] and `[&str]`s. pub fn path_from_root_and_strs(root: Path, segments: &[&str]) -> Path { Path { leading_colon: root.leading_colon, segments: root .segments .into_iter() .chain(segments.iter().map(|segment| path_segment(segment))) .collect(), } } /// Extension for [`MetaList`]. pub trait MetaListExt { /// Shorthand for parsing a [`MetaList`] into a list of [`Meta`]s. fn parse_non_empty_nested_metas(&self) -> Result>; } impl MetaListExt for MetaList { fn parse_non_empty_nested_metas(&self) -> Result> { let list = self.parse_args_with(Punctuated::::parse_terminated)?; if list.is_empty() { return Err(Error::option_empty(self.span())); } Ok(list) } }