diff --git a/src/data.rs b/src/data.rs index 05060208..be246194 100644 --- a/src/data.rs +++ b/src/data.rs @@ -231,9 +231,9 @@ impl<'a> Data<'a> { /// Returns the destructuring `other` pattern of this [`Data`]. #[cfg(all(not(feature = "nightly"), feature = "safe"))] - pub fn other_pattern(&self) -> &Pat { + pub fn other_pattern_skip(&self) -> &Pat { match self.fields() { - Either::Left(fields) => &fields.other_pattern, + Either::Left(fields) => &fields.other_pattern_skip, Either::Right(pattern) => pattern, } } diff --git a/src/data/fields.rs b/src/data/fields.rs index 04183366..77269adb 100644 --- a/src/data/fields.rs +++ b/src/data/fields.rs @@ -5,6 +5,8 @@ use syn::{ FieldPat, FieldsNamed, FieldsUnnamed, Ident, Pat, PatIdent, PatStruct, PatTuple, PatTupleStruct, Path, Result, Token, }; +#[cfg(all(not(feature = "nightly"), feature = "safe"))] +use {std::iter, syn::punctuated::Punctuated, syn::PatRest}; use crate::{DeriveWhere, Field, Skip, Trait}; @@ -15,6 +17,10 @@ pub struct Fields<'a> { pub self_pattern: Pat, /// [Pattern](Pat) to use in a match arm to destructure `other`. pub other_pattern: Pat, + /// [Pattern](Pat) to use in a match arm to destructure `other` but skipping + /// all fields. + #[cfg(all(not(feature = "nightly"), feature = "safe"))] + pub other_pattern_skip: Pat, /// [`Field`]s of this struct, union or variant. pub fields: Vec>, } @@ -30,11 +36,21 @@ impl<'a> Fields<'a> { let fields = Field::from_named(derive_wheres, skip_inner, fields)?; let self_pattern = Self::struct_pattern(path.clone(), &fields, |field| &field.self_ident); + #[cfg(all(not(feature = "nightly"), feature = "safe"))] + let other_pattern_skip = Pat::Struct(PatStruct { + attrs: Vec::new(), + path: path.clone(), + brace_token: Brace::default(), + fields: Punctuated::new(), + dot2_token: Some(::default()), + }); let other_pattern = Self::struct_pattern(path, &fields, |field| &field.other_ident); Ok(Self { self_pattern, other_pattern, + #[cfg(all(not(feature = "nightly"), feature = "safe"))] + other_pattern_skip, fields, }) } @@ -49,11 +65,27 @@ impl<'a> Fields<'a> { let fields = Field::from_unnamed(derive_wheres, skip_inner, fields)?; let self_pattern = Self::tuple_pattern(path.clone(), &fields, |field| &field.self_ident); + #[cfg(all(not(feature = "nightly"), feature = "safe"))] + let other_pattern_skip = Pat::TupleStruct(PatTupleStruct { + attrs: Vec::new(), + path: path.clone(), + pat: PatTuple { + attrs: Vec::new(), + paren_token: Paren::default(), + elems: iter::once(Pat::Rest(PatRest { + attrs: Vec::new(), + dot2_token: ::default(), + })) + .collect(), + }, + }); let other_pattern = Self::tuple_pattern(path, &fields, |field| &field.other_ident); Ok(Self { self_pattern, other_pattern, + #[cfg(all(not(feature = "nightly"), feature = "safe"))] + other_pattern_skip, fields, }) } diff --git a/src/test/basic.rs b/src/test/basic.rs index f567c298..d74ad07c 100644 --- a/src/test/basic.rs +++ b/src/test/basic.rs @@ -220,46 +220,23 @@ fn enum_() -> Result<()> { #[cfg(all(not(feature = "nightly"), feature = "safe"))] let ord = quote! { match self { - Test::A { field: ref __field } => - match __other { - Test::B { } => ::core::cmp::Ordering::Less, - Test::C(ref __other_0) => ::core::cmp::Ordering::Less, - Test::D() => ::core::cmp::Ordering::Less, - Test::E => ::core::cmp::Ordering::Less, - _ => ::core::unreachable!("comparing variants yielded unexpected results"), - }, + Test::A { field: ref __field } => ::core::cmp::Ordering::Less, Test::B { } => match __other { - Test::A { field: ref __other_field } => ::core::cmp::Ordering::Greater, - Test::C(ref __other_0) => ::core::cmp::Ordering::Less, - Test::D() => ::core::cmp::Ordering::Less, - Test::E => ::core::cmp::Ordering::Less, - _ => ::core::unreachable!("comparing variants yielded unexpected results"), + Test::A { .. } => ::core::cmp::Ordering::Greater, + _ => ::core::cmp::Ordering::Less, }, Test::C(ref __0) => match __other { - Test::A { field: ref __other_field } => ::core::cmp::Ordering::Greater, - Test::B { } => ::core::cmp::Ordering::Greater, - Test::D() => ::core::cmp::Ordering::Less, - Test::E => ::core::cmp::Ordering::Less, - _ => ::core::unreachable!("comparing variants yielded unexpected results"), + Test::A { .. } | Test::B { .. } => ::core::cmp::Ordering::Greater, + _ => ::core::cmp::Ordering::Less, }, Test::D() => match __other { - Test::A { field: ref __other_field } => ::core::cmp::Ordering::Greater, - Test::B { } => ::core::cmp::Ordering::Greater, - Test::C(ref __other_0) => ::core::cmp::Ordering::Greater, - Test::E => ::core::cmp::Ordering::Less, - _ => ::core::unreachable!("comparing variants yielded unexpected results"), - }, - Test::E => - match __other { - Test::A { field: ref __other_field } => ::core::cmp::Ordering::Greater, - Test::B { } => ::core::cmp::Ordering::Greater, - Test::C(ref __other_0) => ::core::cmp::Ordering::Greater, - Test::D() => ::core::cmp::Ordering::Greater, - _ => ::core::unreachable!("comparing variants yielded unexpected results"), + Test::A { .. } | Test::B { .. } | Test::C(..) => ::core::cmp::Ordering::Greater, + _ => ::core::cmp::Ordering::Less, }, + Test::E => ::core::cmp::Ordering::Greater, } }; #[cfg(feature = "nightly")] @@ -276,46 +253,23 @@ fn enum_() -> Result<()> { #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { match self { - Test::A { field: ref __field } => - match __other { - Test::B { } => ::core::option::Option::Some(::core::cmp::Ordering::Less), - Test::C(ref __other_0) => ::core::option::Option::Some(::core::cmp::Ordering::Less), - Test::D() => ::core::option::Option::Some(::core::cmp::Ordering::Less), - Test::E => ::core::option::Option::Some(::core::cmp::Ordering::Less), - _ => ::core::unreachable!("comparing variants yielded unexpected results"), - }, + Test::A { field: ref __field } => ::core::option::Option::Some(::core::cmp::Ordering::Less), Test::B { } => match __other { - Test::A { field: ref __other_field } => ::core::option::Option::Some(::core::cmp::Ordering::Greater), - Test::C(ref __other_0) => ::core::option::Option::Some(::core::cmp::Ordering::Less), - Test::D() => ::core::option::Option::Some(::core::cmp::Ordering::Less), - Test::E => ::core::option::Option::Some(::core::cmp::Ordering::Less), - _ => ::core::unreachable!("comparing variants yielded unexpected results"), + Test::A { .. } => ::core::option::Option::Some(::core::cmp::Ordering::Greater), + _ => ::core::option::Option::Some(::core::cmp::Ordering::Less), }, Test::C(ref __0) => match __other { - Test::A { field: ref __other_field } => ::core::option::Option::Some(::core::cmp::Ordering::Greater), - Test::B { } => ::core::option::Option::Some(::core::cmp::Ordering::Greater), - Test::D() => ::core::option::Option::Some(::core::cmp::Ordering::Less), - Test::E => ::core::option::Option::Some(::core::cmp::Ordering::Less), - _ => ::core::unreachable!("comparing variants yielded unexpected results"), + Test::A { .. } | Test::B { .. } => ::core::option::Option::Some(::core::cmp::Ordering::Greater), + _ => ::core::option::Option::Some(::core::cmp::Ordering::Less), }, Test::D() => match __other { - Test::A { field: ref __other_field } => ::core::option::Option::Some(::core::cmp::Ordering::Greater), - Test::B { } => ::core::option::Option::Some(::core::cmp::Ordering::Greater), - Test::C(ref __other_0) => ::core::option::Option::Some(::core::cmp::Ordering::Greater), - Test::E => ::core::option::Option::Some(::core::cmp::Ordering::Less), - _ => ::core::unreachable!("comparing variants yielded unexpected results"), - }, - Test::E => - match __other { - Test::A { field: ref __other_field } => ::core::option::Option::Some(::core::cmp::Ordering::Greater), - Test::B { } => ::core::option::Option::Some(::core::cmp::Ordering::Greater), - Test::C(ref __other_0) => ::core::option::Option::Some(::core::cmp::Ordering::Greater), - Test::D() => ::core::option::Option::Some(::core::cmp::Ordering::Greater), - _ => ::core::unreachable!("comparing variants yielded unexpected results"), + Test::A { .. } | Test::B { .. } | Test::C(..) => ::core::option::Option::Some(::core::cmp::Ordering::Greater), + _ => ::core::option::Option::Some(::core::cmp::Ordering::Less), }, + Test::E => ::core::option::Option::Some(::core::cmp::Ordering::Greater), } }; diff --git a/src/test/enum_.rs b/src/test/enum_.rs index 678a9ab7..dbd1bff1 100644 --- a/src/test/enum_.rs +++ b/src/test/enum_.rs @@ -136,16 +136,8 @@ fn two_data() -> Result<()> { #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { match self { - Test::A(ref __0) => - match __other { - Test::B(ref __other_0) => ::core::option::Option::Some(::core::cmp::Ordering::Less), - _ => #unreachable, - }, - Test::B(ref __0) => - match __other { - Test::A(ref __other_0) => ::core::option::Option::Some(::core::cmp::Ordering::Greater), - _ => #unreachable, - }, + Test::A(ref __0) => ::core::option::Option::Some(::core::cmp::Ordering::Less), + Test::B(ref __0) => ::core::option::Option::Some(::core::cmp::Ordering::Greater), } }; @@ -228,16 +220,8 @@ fn unit() -> Result<()> { #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { match self { - Test::A(ref __0) => - match __other { - Test::B => ::core::option::Option::Some(::core::cmp::Ordering::Less), - _ => ::core::unreachable!("comparing variants yielded unexpected results"), - }, - Test::B => - match __other { - Test::A(ref __other_0) => ::core::option::Option::Some(::core::cmp::Ordering::Greater), - _ => ::core::unreachable!("comparing variants yielded unexpected results"), - }, + Test::A(ref __0) => ::core::option::Option::Some(::core::cmp::Ordering::Less), + Test::B => ::core::option::Option::Some(::core::cmp::Ordering::Greater), } }; @@ -313,16 +297,8 @@ fn struct_unit() -> Result<()> { #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { match self { - Test::A(ref __0) => - match __other { - Test::B { } => ::core::option::Option::Some(::core::cmp::Ordering::Less), - _ => ::core::unreachable!("comparing variants yielded unexpected results"), - }, - Test::B { } => - match __other { - Test::A(ref __other_0) => ::core::option::Option::Some(::core::cmp::Ordering::Greater), - _ => ::core::unreachable!("comparing variants yielded unexpected results"), - }, + Test::A(ref __0) => ::core::option::Option::Some(::core::cmp::Ordering::Less), + Test::B { } => ::core::option::Option::Some(::core::cmp::Ordering::Greater), } }; @@ -398,16 +374,8 @@ fn tuple_unit() -> Result<()> { #[cfg(all(not(feature = "nightly"), feature = "safe"))] let partial_ord = quote! { match self { - Test::A(ref __0) => - match __other { - Test::B() => ::core::option::Option::Some(::core::cmp::Ordering::Less), - _ => ::core::unreachable!("comparing variants yielded unexpected results"), - }, - Test::B() => - match __other { - Test::A(ref __other_0) => ::core::option::Option::Some(::core::cmp::Ordering::Greater), - _ => ::core::unreachable!("comparing variants yielded unexpected results"), - }, + Test::A(ref __0) => ::core::option::Option::Some(::core::cmp::Ordering::Less), + Test::B() => ::core::option::Option::Some(::core::cmp::Ordering::Greater), } }; diff --git a/src/test/skip.rs b/src/test/skip.rs index b9151356..ffaee1ca 100644 --- a/src/test/skip.rs +++ b/src/test/skip.rs @@ -122,16 +122,8 @@ fn variants_empty() -> Result<()> { #[cfg(all(not(feature = "nightly"), feature = "safe"))] let ord = quote! { match self { - Test::A(ref __0) => - match __other { - Test::B (ref __other_0) => ::core::cmp::Ordering::Less, - _ => ::core::unreachable!("comparing variants yielded unexpected results"), - }, - Test::B(ref __0) => - match __other { - Test::A (ref __other_0) => ::core::cmp::Ordering::Greater, - _ => ::core::unreachable!("comparing variants yielded unexpected results"), - }, + Test::A(ref __0) => ::core::cmp::Ordering::Less, + Test::B(ref __0) => ::core::cmp::Ordering::Greater, } }; @@ -190,16 +182,8 @@ fn variants_partly_empty() -> Result<()> { #[cfg(all(not(feature = "nightly"), feature = "safe"))] let ord = quote! { match self { - Test::A(ref __0) => - match __other { - Test::B (ref __other_0, ref __other_1) => ::core::cmp::Ordering::Less, - _ => ::core::unreachable!("comparing variants yielded unexpected results"), - }, - Test::B(ref __0, ref __1) => - match __other { - Test::A (ref __other_0) => ::core::cmp::Ordering::Greater, - _ => ::core::unreachable!("comparing variants yielded unexpected results"), - }, + Test::A(ref __0) => ::core::cmp::Ordering::Less, + Test::B(ref __0, ref __1) => ::core::cmp::Ordering::Greater, } }; diff --git a/src/trait_/common_ord.rs b/src/trait_/common_ord.rs index 106f107d..913b3a66 100644 --- a/src/trait_/common_ord.rs +++ b/src/trait_/common_ord.rs @@ -90,6 +90,8 @@ pub fn build_ord_signature(item: &Item, trait_: &DeriveTrait, body: &TokenStream // Safe implementation when not on nightly. #[cfg(all(not(feature = "nightly"), feature = "safe"))] { + use syn::PatOr; + let mut less = quote! { ::core::cmp::Ordering::Less }; let mut greater = quote! { ::core::cmp::Ordering::Greater }; @@ -105,35 +107,45 @@ pub fn build_ord_signature(item: &Item, trait_: &DeriveTrait, body: &TokenStream // other. The index for these variants is used to determine which // `Ordering` to return. for (index, variant) in variants.iter().enumerate() { - let mut arms = Vec::with_capacity(variants.len() - 1); - - for (index_other, variant_other) in variants.iter().enumerate() { - // Make sure we aren't comparing the same variant with itself. - if index != index_other { - use std::cmp::Ordering::*; - - let ordering = match index.cmp(&index_other) { - Less => &less, - Equal => &equal, - Greater => &greater, - }; - - let pattern = &variant_other.other_pattern(); - - arms.push(quote! { - #pattern => #ordering, - }); - } - } - let pattern = &variant.self_pattern(); - different.push(quote! { - #pattern => match __other { - #(#arms)* - _ => ::core::unreachable!("comparing variants yielded unexpected results"), - }, - }); + // The first variant is always `Less` then everything. + if index == 0 { + different.push(quote! { + #pattern => #less, + }) + } + // The last variant is always `Greater` then everything. + else if index == variants.len() - 1 { + different.push(quote! { + #pattern => #greater, + }) + } + // Any variant between the first and last. + else { + // Collect all variants that are `Less`. + let cases = variants + .iter() + .enumerate() + .filter(|(index_other, _)| *index_other < index) + .map(|(_, variant_other)| variant_other.other_pattern_skip().clone()) + .collect(); + + // Build one match arm pattern with all variants that are `Greater`. + let pattern_less = PatOr { + attrs: Vec::new(), + leading_vert: None, + cases, + }; + + // All other variants are `Less`. + different.push(quote! { + #pattern => match __other { + #pattern_less => #greater, + _ => #less, + }, + }); + } } quote! {