Repository: JelteF/derive_more
Pull request #483 (2025-06-04 17:17:06)

Support `skip` attribute in `Eq`/`PartialEq` derives (#163)
Part of #163  
Requires #479

## Synopsis

Support of `skip` attribute on fields and variants:
```rust
#[derive(Eq, PartialEq)]
enum E {
    Foo(bool, #[eq(skip)] NoEq),
    #[partial_eq(skip)]
    Bar(NoEq, NoEq),
}
```

## Solution

Allow `utils::attr::Skip` on fields and variants.

### Ergonomics

`#[eq(skip)]`/`#[partial_eq(skip)]` attributes are visible and mean the same for both `Eq` and `PartialEq` macros, allowing to use only one and do not repeat yourself.

Once put on a variant (or a whole struct), ignores all the fields.

## Checklist

- [x] Documentation is updated
- [x] Tests are added/updated
- [x] ~~[CHANGELOG entry](/CHANGELOG.md) is added~~ (not required)



Patch:
diff --git a/impl/doc/eq.md b/impl/doc/eq.md
index 3087414..111ea03 100644
--- a/impl/doc/eq.md
+++ b/impl/doc/eq.md
@@ -1,6 +1,6 @@
 # Using `#[derive(Eq)]` and `#[derive(PartialEq)]`
 
-Deriving `Eq`/`PartialEq` works by checking whether two values are equal according 
+Deriving `Eq`/`PartialEq` works by checking whether two values are equal according
 to their type structure.
 
 
@@ -66,7 +66,6 @@ where
     PhantomData<B>: PartialEq, // `B: PartialEq` is generated by `std` instead
     C::Assoc: PartialEq,       // `C: PartialEq` is generated by `std` instead
 {
-    #[inline]
     fn eq(&self, other: &Self) -> bool {
         match (self, other) {
             (Self { a: self_0, b: self_1, c: self_2 }, Self { a: other_0, b: other_1, c: other_2 }) => {
@@ -74,8 +73,6 @@ where
             }
         }
     }
-
-    #[inline]
     fn ne(&self, other: &Self) -> bool {
         match (self, other) {
             (Self { a: self_0, b: self_1, c: self_2 }, Self { a: other_0, b: other_1, c: other_2 }) => {
@@ -152,25 +149,22 @@ where
     PhantomData<B>: PartialEq, // `B: PartialEq` is generated by `std` instead
     C::Assoc: PartialEq,       // `C: PartialEq` is generated by `std` instead
 {
-    #[inline]
     fn eq(&self, other: &Self) -> bool {
         std::mem::discriminant(self) == std::mem::discriminant(other) &&
             match (self, other) {
                 (Self::A(self_0), Self::A(other_0)) => { self_0 == other_0 }
                 (Self::B { b: self_0 }, Self::B { b: other_0 }) => { self_0 == other_0 }
                 (Self::C(self_0), Self::C(other_0)) => { self_0 == other_0 }
-                _ => unsafe { std::hint::unreachable_unchecked() },
+                _ => unsafe { std::hint::unreachable_unchecked() }
             }
     }
-
-    #[inline]
     fn ne(&self, other: &Self) -> bool {
         std::mem::discriminant(self) != std::mem::discriminant(other) ||
             match (self, other) {
                 (Self::A(self_0), Self::A(other_0)) => { self_0 != other_0 }
                 (Self::B { b: self_0 }, Self::B { b: other_0 }) => { self_0 != other_0 }
                 (Self::C(self_0), Self::C(other_0)) => { self_0 != other_0 }
-                _ => unsafe { std::hint::unreachable_unchecked() },
+                _ => unsafe { std::hint::unreachable_unchecked() }
             }
     }
 }
@@ -183,3 +177,112 @@ where
     C::Assoc: Eq,       // `C: Eq` is generated by `std` instead
 {}
 ```
+
+
+### Ignoring
+
+Both `#[eq(skip)]` and `#[partial_eq(skip)]` attributes could be used to ignore
+fields, a whole struct or enum variants in the expansion.
+
+```rust
+# use derive_more::{Eq, PartialEq};
+#
+#[derive(Debug)]
+struct NoEq; // doesn't implement `Eq`/`PartialEq`
+
+#[derive(Debug, Eq, PartialEq)]
+struct Foo {
+    num: i32,
+    // This attribute is seen by both `Eq` and `PartialEq` macros,
+    // so the repetition is unnecessary.
+    #[eq(skip)] // or #[eq(ignore)]
+    ignored: f32,
+}
+
+#[derive(Debug, Eq, PartialEq)]
+// This attribute is also seen by both `Eq` and `PartialEq` macros.
+// Makes all fields of this struct being ignored.
+#[partial_eq(skip)] // or #[partial_eq(ignore)]
+struct Bar(f32, NoEq);
+
+#[derive(Debug, Eq, PartialEq)]
+enum Enum {
+    Foo(i32, #[eq(skip)] NoEq),
+    #[eq(skip)]
+    Bar(NoEq),
+    Baz,
+}
+
+assert_eq!(Foo { num: 0, ignored: 1.0 }, Foo { num: 0, ignored: 1.0 });
+assert_eq!(Foo { num: 0, ignored: 1.0 }, Foo { num: 0, ignored: 2.0 });
+assert_ne!(Foo { num: 0, ignored: 1.0 }, Foo { num: 1, ignored: 1.0 });
+
+assert_eq!(Bar(0.0, NoEq), Bar(0.0, NoEq));
+assert_eq!(Bar(0.0, NoEq), Bar(1.0, NoEq));
+
+assert_eq!(Enum::Foo(0, NoEq), Enum::Foo(0, NoEq));
+assert_ne!(Enum::Foo(0, NoEq), Enum::Foo(1, NoEq));
+
+assert_eq!(Enum::Bar(NoEq), Enum::Bar(NoEq));
+
+assert_eq!(Enum::Baz, Enum::Baz);
+
+// NOTE: Different variants are still not equal despite being ignored!
+//       Ignoring a variant only ignores all its fields during comparison.
+assert_ne!(Enum::Foo(0, NoEq), Enum::Bar(NoEq));
+assert_ne!(Enum::Foo(0, NoEq), Enum::Baz);
+assert_ne!(Enum::Bar(NoEq), Enum::Baz);
+```
+This generates code equivalent to:
+```rust
+# struct NoEq;
+#
+# struct Foo { num: i32, ignored: f32 }
+#
+impl PartialEq for Foo {
+    fn eq(&self, __other: &Self) -> bool {
+        match (self, __other) {
+            (Self { num: __self_0, .. }, Self { num: __other_0, .. }) => { __self_0 == __other_0 }
+        }
+    }
+    fn ne(&self, __other: &Self) -> bool {
+        match (self, __other) {
+            (Self { num: __self_0, .. }, Self { num: __other_0, .. }) => { __self_0 != __other_0 }
+        }
+    }
+}
+impl Eq for Foo
+where
+    i32: Eq,
+{}
+
+# struct Bar(i32, NoEq);
+#
+impl PartialEq for Bar {
+    fn eq(&self, __other: &Self) -> bool { true }
+}
+impl Eq for Bar {}
+
+# enum Enum {
+#     Foo(i32, NoEq),
+#     Bar(NoEq),
+#     Baz,
+# }
+#
+impl PartialEq for Enum {
+    fn eq(&self, __other: &Self) -> bool {
+        std::mem::discriminant(self) == std::mem::discriminant(__other) &&
+            match (self, __other) {
+                (Self::Foo(__self_0, _, ), Self::Foo(__other_0, _, )) => { __self_0 == __other_0 }
+                _ => true,
+            }
+    }
+    fn ne(&self, __other: &Self) -> bool {
+        std::mem::discriminant(self) != std::mem::discriminant(__other) ||
+            match (self, __other) {
+                (Self::Foo(__self_0, _, ), Self::Foo(__other_0, _, )) => { __self_0 != __other_0 }
+                _ => false,
+        }
+    }
+}
+```
diff --git a/impl/src/cmp/eq.rs b/impl/src/cmp/eq.rs
index 87c033e..b861c1f 100644
--- a/impl/src/cmp/eq.rs
+++ b/impl/src/cmp/eq.rs
@@ -1,30 +1,65 @@
 //! Implementation of an [`Eq`] derive macro.
 
 use proc_macro2::TokenStream;
-use quote::{quote, ToTokens};
+use quote::{format_ident, quote, ToTokens};
 use syn::{parse_quote, spanned::Spanned as _};
 
 use super::TypeExt as _;
-use crate::utils::HashSet;
+use crate::utils::{
+    attr::{self, ParseMultiple as _},
+    HashSet,
+};
 
 /// Expands an [`Eq`] derive macro.
 pub fn expand(input: &syn::DeriveInput, _: &'static str) -> syn::Result<TokenStream> {
-    let fields_types = match &input.data {
+    let attr_name = format_ident!("eq");
+    let secondary_attr_name = format_ident!("partial_eq");
+
+    let mut fields_types = HashSet::default();
+    match &input.data {
         syn::Data::Struct(data) => {
-            data.fields.iter().map(|f| &f.ty).collect::<HashSet<_>>()
+            let mut is_skipped = false;
+            for attr_name in [&attr_name, &secondary_attr_name] {
+                if attr::Skip::parse_attrs(&input.attrs, attr_name)?.is_some() {
+                    is_skipped = true;
+                    break;
+                }
+            }
+            if !is_skipped {
+                'fields: for field in &data.fields {
+                    for attr_name in [&attr_name, &secondary_attr_name] {
+                        if attr::Skip::parse_attrs(&field.attrs, attr_name)?.is_some() {
+                            continue 'fields;
+                        }
+                    }
+                    _ = fields_types.insert(&field.ty);
+                }
+            }
+        }
+        syn::Data::Enum(data) => {
+            'variants: for variant in &data.variants {
+                for attr_name in [&attr_name, &secondary_attr_name] {
+                    if attr::Skip::parse_attrs(&variant.attrs, attr_name)?.is_some() {
+                        continue 'variants;
+                    }
+                }
+                'fields: for field in &variant.fields {
+                    for attr_name in [&attr_name, &secondary_attr_name] {
+                        if attr::Skip::parse_attrs(&field.attrs, attr_name)?.is_some() {
+                            continue 'fields;
+                        }
+                    }
+                    _ = fields_types.insert(&field.ty);
+                }
+            }
         }
-        syn::Data::Enum(data) => data
-            .variants
-            .iter()
-            .flat_map(|variant| variant.fields.iter().map(|f| &f.ty))
-            .collect(),
         syn::Data::Union(data) => {
             return Err(syn::Error::new(
                 data.union_token.span(),
                 "`Eq` cannot be derived for unions",
             ))
         }
-    };
+    }
 
     Ok(StructuralExpansion {
         self_ty: (&input.ident, &input.generics),
diff --git a/impl/src/cmp/partial_eq.rs b/impl/src/cmp/partial_eq.rs
index 64141cf..9c3d026 100644
--- a/impl/src/cmp/partial_eq.rs
+++ b/impl/src/cmp/partial_eq.rs
@@ -6,16 +6,84 @@ use syn::{
     parse_quote,
     punctuated::{self, Punctuated},
     spanned::Spanned as _,
+    token,
 };
 
 use super::TypeExt as _;
-use crate::utils::GenericsSearch;
+use crate::utils::{
+    attr::{self, ParseMultiple as _},
+    GenericsSearch, HashSet,
+};
 
 /// Expands a [`PartialEq`] derive macro.
 pub fn expand(input: &syn::DeriveInput, _: &'static str) -> syn::Result<TokenStream> {
-    Ok(StructuralExpansion::try_from(input)?.into_token_stream())
+    let attr_name = format_ident!("partial_eq");
+    let secondary_attr_name = format_ident!("eq");
+
+    let mut has_skipped_variants = false;
+    let mut variants = vec![];
+
+    match &input.data {
+        syn::Data::Struct(data) => {
+            for attr_name in [&attr_name, &secondary_attr_name] {
+                if attr::Skip::parse_attrs(&input.attrs, attr_name)?.is_some() {
+                    has_skipped_variants = true;
+                    break;
+                }
+            }
+            if !has_skipped_variants {
+                let mut skipped_fields = HashSet::default();
+                'fields: for (n, field) in data.fields.iter().enumerate() {
+                    for attr_name in [&attr_name, &secondary_attr_name] {
+                        if attr::Skip::parse_attrs(&field.attrs, attr_name)?.is_some() {
+                            _ = skipped_fields.insert(n);
+                            continue 'fields;
+                        }
+                    }
+                }
+                variants.push((None, &data.fields, skipped_fields));
+            }
+        }
+        syn::Data::Enum(data) => {
+            'variants: for variant in &data.variants {
+                for attr_name in [&attr_name, &secondary_attr_name] {
+                    if attr::Skip::parse_attrs(&variant.attrs, attr_name)?.is_some() {
+                        has_skipped_variants = true;
+                        continue 'variants;
+                    }
+                }
+                let mut skipped_fields = HashSet::default();
+                'fields: for (n, field) in variant.fields.iter().enumerate() {
+                    for attr_name in [&attr_name, &secondary_attr_name] {
+                        if attr::Skip::parse_attrs(&field.attrs, attr_name)?.is_some() {
+                            _ = skipped_fields.insert(n);
+                            continue 'fields;
+                        }
+                    }
+                }
+                variants.push((Some(&variant.ident), &variant.fields, skipped_fields));
+            }
+        }
+        syn::Data::Union(data) => {
+            return Err(syn::Error::new(
+                data.union_token.span(),
+                "`PartialEq` cannot be derived for unions",
+            ))
+        }
+    }
+
+    Ok(StructuralExpansion {
+        self_ty: (&input.ident, &input.generics),
+        variants,
+        has_skipped_variants,
+        is_enum: matches!(input.data, syn::Data::Enum(_)),
+    }
+    .into_token_stream())
 }
 
+/// Indices of [`syn::Field`]s marked with an [`attr::Skip`].
+type SkippedFields = HashSet<usize>;
+
 /// Expansion of a macro for generating a structural [`PartialEq`] implementation of an enum or a
 /// struct.
 struct StructuralExpansion<'i> {
@@ -25,35 +93,13 @@ struct StructuralExpansion<'i> {
     self_ty: (&'i syn::Ident, &'i syn::Generics),
 
     /// [`syn::Fields`] of the enum/struct to be compared in this [`StructuralExpansion`].
-    variants: Vec<(Option<&'i syn::Ident>, &'i syn::Fields)>,
-}
+    variants: Vec<(Option<&'i syn::Ident>, &'i syn::Fields, SkippedFields)>,
 
-impl<'i> TryFrom<&'i syn::DeriveInput> for StructuralExpansion<'i> {
-    type Error = syn::Error;
+    /// Indicator whether some original enum variants where skipped with an [`attr::Skip`].
+    has_skipped_variants: bool,
 
-    fn try_from(input: &'i syn::DeriveInput) -> syn::Result<Self> {
-        let variants = match &input.data {
-            syn::Data::Struct(data) => {
-                vec![(None, &data.fields)]
-            }
-            syn::Data::Enum(data) => data
-                .variants
-                .iter()
-                .map(|variant| (Some(&variant.ident), &variant.fields))
-                .collect(),
-            syn::Data::Union(data) => {
-                return Err(syn::Error::new(
-                    data.union_token.span(),
-                    "`PartialEq` cannot be derived structurally for unions",
-                ))
-            }
-        };
-
-        Ok(Self {
-            self_ty: (&input.ident, &input.generics),
-            variants,
-        })
-    }
+    /// Indicator whether this expansion is for an enum.
+    is_enum: bool,
 }
 
 impl StructuralExpansion<'_> {
@@ -61,14 +107,23 @@ impl StructuralExpansion<'_> {
     /// [`StructuralExpansion`], if it's required.
     fn body(&self, eq: bool) -> Option<TokenStream> {
         // Special case: empty enum (also, no need for `ne()` method in this case).
-        if self.variants.is_empty() {
+        if self.is_enum && self.variants.is_empty() && !self.has_skipped_variants {
             return eq.then(|| quote! { match *self {} });
         }
 
         let no_fields_result = quote! { #eq };
 
-        // Special case: no fields to compare (also, no need for `ne()` method in this case).
-        if self.variants.len() == 1 && self.variants[0].1.is_empty() {
+        // Special case: fully skipped struct (also, no need for `ne()` method in this case).
+        if !self.is_enum && self.variants.is_empty() && self.has_skipped_variants {
+            return eq.then_some(no_fields_result);
+        }
+        // Special case: no fields to compare in struct/single-variant enum (also, no need for
+        // `ne()` method in this case).
+        if !(self.is_enum && self.has_skipped_variants)
+            && self.variants.len() == 1
+            && (self.variants[0].1.is_empty()
+                || self.variants[0].1.len() == self.variants[0].2.len())
+        {
             return eq.then_some(no_fields_result);
         }
 
@@ -78,33 +133,32 @@ impl StructuralExpansion<'_> {
             (quote! { != }, quote! { || })
         };
 
-        let discriminants_cmp = (self.variants.len() > 1).then(|| {
-            quote! {
-                derive_more::core::mem::discriminant(self) #cmp
-                    derive_more::core::mem::discriminant(__other)
-            }
-        });
+        let discriminants_cmp = (self.variants.len() > 1 || self.has_skipped_variants)
+            .then(|| {
+                quote! {
+                    derive_more::core::mem::discriminant(self) #cmp
+                        derive_more::core::mem::discriminant(__other)
+                }
+            });
 
         let match_arms = self
             .variants
             .iter()
-            .filter_map(|(variant, fields)| {
-                if fields.is_empty() {
+            .filter_map(|(variant, all_fields, skipped_fields)| {
+                if all_fields.is_empty() || skipped_fields.len() == all_fields.len() {
                     return None;
                 }
 
                 let variant = variant.map(|variant| quote! { :: #variant });
-                let self_pattern = fields.arm_pattern("__self_");
-                let other_pattern = fields.arm_pattern("__other_");
+                let self_pattern = all_fields.arm_pattern("__self_", skipped_fields);
+                let other_pattern = all_fields.arm_pattern("__other_", skipped_fields);
 
-                let mut val_eqs = (0..fields.len())
+                let mut val_eqs = (0..all_fields.len())
+                    .filter(|num| !skipped_fields.contains(num))
                     .map(|num| {
                         let self_val = format_ident!("__self_{num}");
                         let other_val = format_ident!("__other_{num}");
-                        punctuated::Pair::Punctuated(
-                            quote! { #self_val #cmp #other_val },
-                            &chain,
-                        )
+                        punctuated::Pair::Punctuated(quote! { #self_val #cmp #other_val }, &chain)
                     })
                     .collect::<Punctuated<TokenStream, _>>();
                 _ = val_eqs.pop_punct();
@@ -115,9 +169,11 @@ impl StructuralExpansion<'_> {
             })
             .collect::<Vec<_>>();
         let match_expr = (!match_arms.is_empty()).then(|| {
-            let no_fields_arm = (match_arms.len() != self.variants.len()).then(|| {
-                quote! { _ => #no_fields_result }
-            });
+            let no_fields_arm = (match_arms.len() != self.variants.len()
+                || self.has_skipped_variants)
+                .then(|| {
+                    quote! { _ => #no_fields_result }
+                });
             let unreachable_arm = (self.variants.len() > 1
                 && no_fields_arm.is_none())
             .then(|| {
@@ -162,8 +218,12 @@ impl ToTokens for StructuralExpansion<'_> {
         {
             let self_ty: syn::Type = parse_quote! { Self };
             let implementor_ty: syn::Type = parse_quote! { #ty #ty_generics };
-            for variant in &self.variants {
-                for field_ty in variant.1.iter().map(|field| &field.ty) {
+            for (_, all_fields, skipped_fields) in &self.variants {
+                for field_ty in
+                    all_fields.iter().enumerate().filter_map(|(n, field)| {
+                        (!skipped_fields.contains(&n)).then_some(&field.ty)
+                    })
+                {
                     if generics_search.any_in(field_ty)
                         && !field_ty.contains_type_structurally(&self_ty)
                         && !field_ty.contains_type_structurally(&implementor_ty)
@@ -209,24 +269,40 @@ trait FieldsExt {
     /// Generates a pattern for matching these [`syn::Fields`] in an arm of a `match` expression.
     ///
     /// All the [`syn::Fields`] will be assigned as `{prefix}{num}` bindings for use.
-    fn arm_pattern(&self, prefix: &str) -> TokenStream;
+    fn arm_pattern(&self, prefix: &str, skipped_indices: &SkippedFields)
+        -> TokenStream;
 }
 
 impl FieldsExt for syn::Fields {
-    fn arm_pattern(&self, prefix: &str) -> TokenStream {
+    fn arm_pattern(
+        &self,
+        prefix: &str,
+        skipped_indices: &SkippedFields,
+    ) -> TokenStream {
         match self {
             Self::Named(fields) => {
-                let fields = fields.named.iter().enumerate().map(|(num, field)| {
-                    let name = &field.ident;
-                    let binding = format_ident!("{prefix}{num}");
-                    quote! { #name: #binding }
-                });
-                quote! {{ #( #fields , )* }}
+                let wildcard =
+                    (!skipped_indices.is_empty()).then(token::DotDot::default);
+                let fields = fields
+                    .named
+                    .iter()
+                    .enumerate()
+                    .filter(|(num, _)| !skipped_indices.contains(num))
+                    .map(|(num, field)| {
+                        let name = &field.ident;
+                        let binding = format_ident!("{prefix}{num}");
+                        quote! { #name: #binding }
+                    });
+                quote! {{ #( #fields , )* #wildcard }}
             }
             Self::Unnamed(fields) => {
                 let fields = (0..fields.unnamed.len()).map(|num| {
-                    let binding = format_ident!("{prefix}{num}");
-                    quote! { #binding }
+                    if skipped_indices.contains(&num) {
+                        quote! { _ }
+                    } else {
+                        let binding = format_ident!("{prefix}{num}");
+                        quote! { #binding }
+                    }
                 });
                 quote! {( #( #fields , )* )}
             }
diff --git a/impl/src/lib.rs b/impl/src/lib.rs
index e70ed8b..017ed5d 100644
--- a/impl/src/lib.rs
+++ b/impl/src/lib.rs
@@ -186,8 +186,14 @@ create_derive!(
 );
 create_derive!("display", fmt::display, Pointer, pointer_derive, pointer);
 
-create_derive!("eq", cmp::eq, Eq, eq_derive);
-create_derive!("eq", cmp::partial_eq, PartialEq, partial_eq_derive);
+create_derive!("eq", cmp::eq, Eq, eq_derive, eq);
+create_derive!(
+    "eq",
+    cmp::partial_eq,
+    PartialEq,
+    partial_eq_derive,
+    partial_eq,
+);
 
 create_derive!("error", error, Error, error_derive, error);
 
diff --git a/impl/src/utils.rs b/impl/src/utils.rs
index dd72f5d..0bf2db5 100644
--- a/impl/src/utils.rs
+++ b/impl/src/utils.rs
@@ -16,6 +16,7 @@ use syn::{
     feature = "as_ref",
     feature = "debug",
     feature = "display",
+    feature = "eq",
     feature = "from",
     feature = "from_str",
     feature = "into",
@@ -30,6 +31,7 @@ pub(crate) use self::generics_search::GenericsSearch;
     feature = "as_ref",
     feature = "debug",
     feature = "display",
+    feature = "eq",
     feature = "from",
     feature = "from_str",
     feature = "into",
@@ -1301,6 +1303,7 @@ pub fn is_type_parameter_used_in_type(
     feature = "as_ref",
     feature = "debug",
     feature = "display",
+    feature = "eq",
     feature = "from",
     feature = "from_str",
     feature = "into",
@@ -1373,6 +1376,7 @@ mod either {
     feature = "as_ref",
     feature = "debug",
     feature = "display",
+    feature = "eq",
     feature = "from",
     feature = "from_str",
     feature = "into",
@@ -1469,6 +1473,7 @@ mod spanning {
     feature = "as_ref",
     feature = "debug",
     feature = "display",
+    feature = "eq",
     feature = "from",
     feature = "from_str",
     feature = "into",
@@ -1496,6 +1501,7 @@ pub(crate) mod attr {
     #[cfg(any(
         feature = "as_ref",
         feature = "debug",
+        feature = "eq",
         feature = "from",
         feature = "into",
     ))]
@@ -1828,6 +1834,7 @@ pub(crate) mod attr {
         feature = "as_ref",
         feature = "debug",
         feature = "display",
+        feature = "eq",
         feature = "from",
         feature = "into",
     ))]


Test patch:
diff --git a/tests/compile_fail/eq/unknow_field_attribute.rs b/tests/compile_fail/eq/unknow_field_attribute.rs
new file mode 100644
index 0000000..9b0d577
--- /dev/null
+++ b/tests/compile_fail/eq/unknow_field_attribute.rs
@@ -0,0 +1,21 @@
+#[derive(derive_more::Eq)]
+struct Foo(#[eq(unknown)] i32);
+
+impl PartialEq for Foo {
+    fn eq(&self, _: &Self) -> bool {
+        unimplemented!()
+    }
+}
+
+#[derive(derive_more::Eq)]
+enum Enum {
+    Bar { #[eq(unknown)] i: i32 },
+}
+
+impl PartialEq for Enum {
+    fn eq(&self, _: &Self) -> bool {
+        unimplemented!()
+    }
+}
+
+fn main() {}
diff --git a/tests/compile_fail/eq/unknow_field_attribute.stderr b/tests/compile_fail/eq/unknow_field_attribute.stderr
new file mode 100644
index 0000000..537626d
--- /dev/null
+++ b/tests/compile_fail/eq/unknow_field_attribute.stderr
@@ -0,0 +1,11 @@
+error: only `skip`/`ignore` allowed here
+ --> tests/compile_fail/eq/unknow_field_attribute.rs:2:17
+  |
+2 | struct Foo(#[eq(unknown)] i32);
+  |                 ^^^^^^^
+
+error: only `skip`/`ignore` allowed here
+  --> tests/compile_fail/eq/unknow_field_attribute.rs:12:16
+   |
+12 |     Bar { #[eq(unknown)] i: i32 },
+   |                ^^^^^^^
diff --git a/tests/compile_fail/eq/unknow_struct_attribute.rs b/tests/compile_fail/eq/unknow_struct_attribute.rs
new file mode 100644
index 0000000..ccb6679
--- /dev/null
+++ b/tests/compile_fail/eq/unknow_struct_attribute.rs
@@ -0,0 +1,11 @@
+#[derive(derive_more::Eq)]
+#[eq(unknown)]
+struct Foo(i32);
+
+impl PartialEq for Foo {
+    fn eq(&self, _: &Self) -> bool {
+        unimplemented!()
+    }
+}
+
+fn main() {}
diff --git a/tests/compile_fail/eq/unknow_struct_attribute.stderr b/tests/compile_fail/eq/unknow_struct_attribute.stderr
new file mode 100644
index 0000000..6a684c3
--- /dev/null
+++ b/tests/compile_fail/eq/unknow_struct_attribute.stderr
@@ -0,0 +1,5 @@
+error: only `skip`/`ignore` allowed here
+ --> tests/compile_fail/eq/unknow_struct_attribute.rs:2:6
+  |
+2 | #[eq(unknown)]
+  |      ^^^^^^^
diff --git a/tests/compile_fail/eq/unknow_variant_attribute.rs b/tests/compile_fail/eq/unknow_variant_attribute.rs
new file mode 100644
index 0000000..e579d78
--- /dev/null
+++ b/tests/compile_fail/eq/unknow_variant_attribute.rs
@@ -0,0 +1,13 @@
+#[derive(derive_more::Eq)]
+enum Enum {
+    #[eq(unknown)]
+    Bar { i: i32 },
+}
+
+impl PartialEq for Enum {
+    fn eq(&self, _: &Self) -> bool {
+        unimplemented!()
+    }
+}
+
+fn main() {}
diff --git a/tests/compile_fail/eq/unknow_variant_attribute.stderr b/tests/compile_fail/eq/unknow_variant_attribute.stderr
new file mode 100644
index 0000000..f225e5b
--- /dev/null
+++ b/tests/compile_fail/eq/unknow_variant_attribute.stderr
@@ -0,0 +1,5 @@
+error: only `skip`/`ignore` allowed here
+ --> tests/compile_fail/eq/unknow_variant_attribute.rs:3:10
+  |
+3 |     #[eq(unknown)]
+  |          ^^^^^^^
diff --git a/tests/compile_fail/partial_eq/union.stderr b/tests/compile_fail/partial_eq/union.stderr
index c908f8e..b61696f 100644
--- a/tests/compile_fail/partial_eq/union.stderr
+++ b/tests/compile_fail/partial_eq/union.stderr
@@ -1,4 +1,4 @@
-error: `PartialEq` cannot be derived structurally for unions
+error: `PartialEq` cannot be derived for unions
  --> tests/compile_fail/partial_eq/union.rs:2:1
   |
 2 | union IntOrFloat {
diff --git a/tests/compile_fail/partial_eq/unknow_field_attribute.rs b/tests/compile_fail/partial_eq/unknow_field_attribute.rs
new file mode 100644
index 0000000..529401b
--- /dev/null
+++ b/tests/compile_fail/partial_eq/unknow_field_attribute.rs
@@ -0,0 +1,9 @@
+#[derive(derive_more::PartialEq)]
+struct Foo(#[partial_eq(unknown)] i32);
+
+#[derive(derive_more::PartialEq)]
+enum Enum {
+    Bar { #[partial_eq(unknown)] i: i32 },
+}
+
+fn main() {}
diff --git a/tests/compile_fail/partial_eq/unknow_field_attribute.stderr b/tests/compile_fail/partial_eq/unknow_field_attribute.stderr
new file mode 100644
index 0000000..8a7ec22
--- /dev/null
+++ b/tests/compile_fail/partial_eq/unknow_field_attribute.stderr
@@ -0,0 +1,11 @@
+error: only `skip`/`ignore` allowed here
+ --> tests/compile_fail/partial_eq/unknow_field_attribute.rs:2:25
+  |
+2 | struct Foo(#[partial_eq(unknown)] i32);
+  |                         ^^^^^^^
+
+error: only `skip`/`ignore` allowed here
+ --> tests/compile_fail/partial_eq/unknow_field_attribute.rs:6:24
+  |
+6 |     Bar { #[partial_eq(unknown)] i: i32 },
+  |                        ^^^^^^^
diff --git a/tests/compile_fail/partial_eq/unknow_struct_attribute.rs b/tests/compile_fail/partial_eq/unknow_struct_attribute.rs
new file mode 100644
index 0000000..11ec06b
--- /dev/null
+++ b/tests/compile_fail/partial_eq/unknow_struct_attribute.rs
@@ -0,0 +1,5 @@
+#[derive(derive_more::PartialEq)]
+#[partial_eq(unknown)]
+struct Foo(i32);
+
+fn main() {}
diff --git a/tests/compile_fail/partial_eq/unknow_struct_attribute.stderr b/tests/compile_fail/partial_eq/unknow_struct_attribute.stderr
new file mode 100644
index 0000000..7f6ab83
--- /dev/null
+++ b/tests/compile_fail/partial_eq/unknow_struct_attribute.stderr
@@ -0,0 +1,5 @@
+error: only `skip`/`ignore` allowed here
+ --> tests/compile_fail/partial_eq/unknow_struct_attribute.rs:2:14
+  |
+2 | #[partial_eq(unknown)]
+  |              ^^^^^^^
diff --git a/tests/compile_fail/partial_eq/unknow_variant_attribute.rs b/tests/compile_fail/partial_eq/unknow_variant_attribute.rs
new file mode 100644
index 0000000..6e95724
--- /dev/null
+++ b/tests/compile_fail/partial_eq/unknow_variant_attribute.rs
@@ -0,0 +1,7 @@
+#[derive(derive_more::PartialEq)]
+enum Enum {
+    #[partial_eq(unknown)]
+    Bar { i: i32 },
+}
+
+fn main() {}
diff --git a/tests/compile_fail/partial_eq/unknow_variant_attribute.stderr b/tests/compile_fail/partial_eq/unknow_variant_attribute.stderr
new file mode 100644
index 0000000..ef810bc
--- /dev/null
+++ b/tests/compile_fail/partial_eq/unknow_variant_attribute.stderr
@@ -0,0 +1,5 @@
+error: only `skip`/`ignore` allowed here
+ --> tests/compile_fail/partial_eq/unknow_variant_attribute.rs:3:18
+  |
+3 |     #[partial_eq(unknown)]
+  |                  ^^^^^^^
diff --git a/tests/eq.rs b/tests/eq.rs
index 2683427..89ad88d 100644
--- a/tests/eq.rs
+++ b/tests/eq.rs
@@ -72,6 +72,96 @@ mod structs {
             let _: AssertParamIsEq<Bar>;
         }
 
+        mod skip {
+            use derive_more::{Eq, PartialEq, __private::AssertParamIsEq};
+
+            struct NoEq;
+
+            #[test]
+            fn fields() {
+                #[derive(Eq, PartialEq)]
+                struct Foo(#[eq(skip)] NoEq, bool, #[eq(skip)] i32);
+
+                #[derive(Eq, PartialEq)]
+                struct Bar {
+                    #[partial_eq(skip)]
+                    a: NoEq,
+                    i: i32,
+                    #[partial_eq(skip)]
+                    b: bool,
+                }
+
+                let _: AssertParamIsEq<Foo>;
+                let _: AssertParamIsEq<Bar>;
+            }
+
+            #[test]
+            fn all_fields() {
+                #[derive(Eq, PartialEq)]
+                struct Foo(#[eq(skip)] NoEq, #[eq(skip)] i32);
+
+                #[derive(Eq, PartialEq)]
+                struct Bar {
+                    #[partial_eq(skip)]
+                    a: NoEq,
+                    #[partial_eq(skip)]
+                    b: bool,
+                }
+
+                let _: AssertParamIsEq<Foo>;
+                let _: AssertParamIsEq<Bar>;
+            }
+
+            #[test]
+            fn empty_struct() {
+                #[derive(Eq, PartialEq)]
+                #[eq(skip)]
+                struct Foo;
+
+                #[derive(Eq, PartialEq)]
+                #[partial_eq(skip)]
+                struct Bar {}
+
+                let _: AssertParamIsEq<Foo>;
+                let _: AssertParamIsEq<Bar>;
+            }
+
+            #[test]
+            fn multifield_struct() {
+                #[derive(Eq, PartialEq)]
+                #[eq(skip)]
+                struct Foo(NoEq, bool);
+
+                #[derive(Eq, PartialEq)]
+                #[partial_eq(skip)]
+                struct Bar {
+                    a: NoEq,
+                    b: bool,
+                }
+
+                let _: AssertParamIsEq<Foo>;
+                let _: AssertParamIsEq<Bar>;
+            }
+
+            #[test]
+            fn mixed() {
+                #[derive(Eq, PartialEq)]
+                #[eq(skip)]
+                struct Foo(NoEq, #[eq(skip)] bool);
+
+                #[derive(Eq, PartialEq)]
+                #[partial_eq(skip)]
+                struct Bar {
+                    a: NoEq,
+                    #[partial_eq(skip)]
+                    b: bool,
+                }
+
+                let _: AssertParamIsEq<Foo>;
+                let _: AssertParamIsEq<Bar>;
+            }
+        }
+
         mod generic {
             #[cfg(not(feature = "std"))]
             use ::alloc::{boxed::Box, vec::Vec};
@@ -153,6 +243,82 @@ mod structs {
 
                 let _: AssertParamIsEq<Foo<f32, ()>>;
             }
+
+            mod skip {
+                use derive_more::{Eq, PartialEq, __private::AssertParamIsEq};
+
+                struct NoEq;
+
+                #[test]
+                fn fields() {
+                    #[derive(Eq, PartialEq)]
+                    struct Foo<A, B, C>(#[eq(skip)] A, B, #[eq(skip)] C);
+
+                    #[derive(Eq, PartialEq)]
+                    struct Bar<A, B, C> {
+                        #[partial_eq(skip)]
+                        a: A,
+                        i: B,
+                        #[partial_eq(skip)]
+                        b: C,
+                    }
+
+                    let _: AssertParamIsEq<Foo<NoEq, bool, i32>>;
+                    let _: AssertParamIsEq<Bar<NoEq, i32, bool>>;
+                }
+
+                #[test]
+                fn all_fields() {
+                    #[derive(Eq, PartialEq)]
+                    struct Foo<A, B>(#[eq(skip)] A, #[eq(skip)] B);
+
+                    #[derive(Eq, PartialEq)]
+                    struct Bar<A, B> {
+                        #[partial_eq(skip)]
+                        a: A,
+                        #[partial_eq(skip)]
+                        b: B,
+                    }
+
+                    let _: AssertParamIsEq<Foo<NoEq, i32>>;
+                    let _: AssertParamIsEq<Bar<NoEq, bool>>;
+                }
+
+                #[test]
+                fn multifield_struct() {
+                    #[derive(Eq, PartialEq)]
+                    #[eq(skip)]
+                    struct Foo<A, B>(A, B);
+
+                    #[derive(Eq, PartialEq)]
+                    #[partial_eq(skip)]
+                    struct Bar<A, B> {
+                        a: A,
+                        b: B,
+                    }
+
+                    let _: AssertParamIsEq<Foo<NoEq, NoEq>>;
+                    let _: AssertParamIsEq<Bar<NoEq, NoEq>>;
+                }
+
+                #[test]
+                fn mixed() {
+                    #[derive(Eq, PartialEq)]
+                    #[eq(skip)]
+                    struct Foo<A, B>(A, #[eq(skip)] B);
+
+                    #[derive(Eq, PartialEq)]
+                    #[partial_eq(skip)]
+                    struct Bar<A, B> {
+                        a: A,
+                        #[partial_eq(skip)]
+                        b: B,
+                    }
+
+                    let _: AssertParamIsEq<Foo<NoEq, NoEq>>;
+                    let _: AssertParamIsEq<Bar<NoEq, NoEq>>;
+                }
+            }
         }
     }
 }
@@ -267,6 +433,120 @@ mod enums {
             let _: AssertParamIsEq<E>;
         }
 
+        mod skip {
+            use derive_more::{Eq, PartialEq, __private::AssertParamIsEq};
+
+            struct NoEq;
+
+            #[test]
+            fn fields() {
+                #[derive(Eq, PartialEq)]
+                enum E {
+                    Foo(#[eq(skip)] NoEq, bool, #[eq(skip)] i32),
+                    Bar {
+                        #[partial_eq(skip)]
+                        a: NoEq,
+                        i: i32,
+                        #[partial_eq(skip)]
+                        b: bool,
+                    },
+                }
+
+                let _: AssertParamIsEq<E>;
+            }
+
+            #[test]
+            fn all_fields() {
+                #[derive(Eq, PartialEq)]
+                enum E {
+                    Foo(#[partial_eq(skip)] NoEq, #[partial_eq(skip)] i32),
+                    Bar {
+                        #[partial_eq(skip)]
+                        a: NoEq,
+                        #[partial_eq(skip)]
+                        b: bool,
+                    },
+                }
+
+                let _: AssertParamIsEq<E>;
+            }
+
+            #[test]
+            fn variants() {
+                #[derive(Eq, PartialEq)]
+                enum E {
+                    Foo(bool, i32),
+                    #[eq(skip)]
+                    Bar {
+                        a: NoEq,
+                        b: bool,
+                    },
+                }
+
+                let _: AssertParamIsEq<E>;
+            }
+
+            #[test]
+            fn all_variants() {
+                #[derive(Eq, PartialEq)]
+                enum E {
+                    #[eq(skip)]
+                    Foo(bool, i32),
+                    #[partial_eq(skip)]
+                    Bar { a: NoEq, b: bool },
+                    #[eq(skip)]
+                    Baz,
+                }
+
+                let _: AssertParamIsEq<E>;
+            }
+
+            #[test]
+            fn single_variant() {
+                #[derive(Eq, PartialEq)]
+                enum E {
+                    #[eq(skip)]
+                    Bar { a: NoEq, b: bool },
+                }
+
+                let _: AssertParamIsEq<E>;
+            }
+
+            #[test]
+            fn all_but_single_empty_variant() {
+                #[derive(Eq, PartialEq)]
+                enum E {
+                    #[eq(skip)]
+                    Foo(bool, i32),
+                    #[partial_eq(skip)]
+                    Bar {
+                        a: NoEq,
+                        b: bool,
+                    },
+                    Baz,
+                }
+
+                let _: AssertParamIsEq<E>;
+            }
+
+            #[test]
+            fn mixed() {
+                #[derive(Eq, PartialEq)]
+                enum E {
+                    #[eq(skip)]
+                    Foo(bool, i32),
+                    Bar {
+                        #[partial_eq(skip)]
+                        a: NoEq,
+                        b: bool,
+                    },
+                    Baz,
+                }
+
+                let _: AssertParamIsEq<E>;
+            }
+        }
+
         mod generic {
             #[cfg(not(feature = "std"))]
             use ::alloc::{boxed::Box, vec::Vec};
@@ -372,6 +652,120 @@ mod enums {
 
                 let _: AssertParamIsEq<E<i64, f64>>;
             }
+
+            mod skip {
+                use derive_more::{Eq, PartialEq, __private::AssertParamIsEq};
+
+                struct NoEq;
+
+                #[test]
+                fn fields() {
+                    #[derive(Eq, PartialEq)]
+                    enum E<A, B, C> {
+                        Foo(#[partial_eq(skip)] A, B, #[partial_eq(skip)] C),
+                        Bar {
+                            #[partial_eq(skip)]
+                            a: A,
+                            i: C,
+                            #[partial_eq(skip)]
+                            b: B,
+                        },
+                    }
+
+                    let _: AssertParamIsEq<E<NoEq, bool, i32>>;
+                }
+
+                #[test]
+                fn all_fields() {
+                    #[derive(Eq, PartialEq)]
+                    enum E<A, B, C> {
+                        Foo(#[partial_eq(skip)] A, #[partial_eq(skip)] B),
+                        Bar {
+                            #[partial_eq(skip)]
+                            a: A,
+                            #[partial_eq(skip)]
+                            b: C,
+                        },
+                    }
+
+                    let _: AssertParamIsEq<E<NoEq, bool, i32>>;
+                }
+
+                #[test]
+                fn variants() {
+                    #[derive(Eq, PartialEq)]
+                    enum E<A, B, C> {
+                        Foo(A, B),
+                        #[eq(skip)]
+                        Bar {
+                            a: C,
+                            b: A,
+                        },
+                    }
+
+                    let _: AssertParamIsEq<E<i32, bool, NoEq>>;
+                }
+
+                #[test]
+                fn all_variants() {
+                    #[derive(Eq, PartialEq)]
+                    enum E<A, B, C> {
+                        #[eq(skip)]
+                        Foo(A, B),
+                        #[partial_eq(skip)]
+                        Bar { a: C, b: A },
+                        #[eq(skip)]
+                        Baz,
+                    }
+
+                    let _: AssertParamIsEq<E<NoEq, NoEq, f32>>;
+                }
+
+                #[test]
+                fn single_variant() {
+                    #[derive(Eq, PartialEq)]
+                    enum E<A, B> {
+                        #[eq(skip)]
+                        Bar { a: A, b: B },
+                    }
+
+                    let _: AssertParamIsEq<E<NoEq, NoEq>>;
+                }
+
+                #[test]
+                fn all_but_single_empty_variant() {
+                    #[derive(Eq, PartialEq)]
+                    enum E<A, B, C> {
+                        #[eq(skip)]
+                        Foo(A, B),
+                        #[partial_eq(skip)]
+                        Bar {
+                            a: C,
+                            b: A,
+                        },
+                        Baz,
+                    }
+
+                    let _: AssertParamIsEq<E<NoEq, NoEq, f32>>;
+                }
+
+                #[test]
+                fn mixed() {
+                    #[derive(Eq, PartialEq)]
+                    enum E<A, B, C> {
+                        #[eq(skip)]
+                        Foo(A, B),
+                        Bar {
+                            #[partial_eq(skip)]
+                            a: C,
+                            b: A,
+                        },
+                        Baz,
+                    }
+
+                    let _: AssertParamIsEq<E<i32, NoEq, f32>>;
+                }
+            }
         }
     }
 }
diff --git a/tests/partial_eq.rs b/tests/partial_eq.rs
index a49dd5b..ff94053 100644
--- a/tests/partial_eq.rs
+++ b/tests/partial_eq.rs
@@ -109,6 +109,145 @@ mod structs {
             );
         }
 
+        mod skip {
+            use derive_more::PartialEq;
+
+            #[derive(Debug)]
+            struct NoEq;
+
+            #[test]
+            fn fields() {
+                #[derive(Debug, PartialEq)]
+                struct Foo(#[partial_eq(skip)] NoEq, bool, #[partial_eq(skip)] i32);
+
+                #[derive(Debug, PartialEq)]
+                struct Bar {
+                    #[partial_eq(skip)]
+                    a: NoEq,
+                    i: i32,
+                    #[partial_eq(skip)]
+                    b: bool,
+                }
+
+                assert_eq!(Foo(NoEq, true, 0), Foo(NoEq, true, 0));
+                assert_eq!(Foo(NoEq, true, 0), Foo(NoEq, true, 1));
+                assert_ne!(Foo(NoEq, true, 0), Foo(NoEq, false, 0));
+
+                assert_eq!(
+                    Bar {
+                        a: NoEq,
+                        i: 0,
+                        b: true,
+                    },
+                    Bar {
+                        a: NoEq,
+                        i: 0,
+                        b: true,
+                    },
+                );
+                assert_eq!(
+                    Bar {
+                        a: NoEq,
+                        i: 0,
+                        b: true,
+                    },
+                    Bar {
+                        a: NoEq,
+                        i: 0,
+                        b: false,
+                    },
+                );
+                assert_ne!(
+                    Bar {
+                        a: NoEq,
+                        i: 0,
+                        b: true,
+                    },
+                    Bar {
+                        a: NoEq,
+                        i: 1,
+                        b: true,
+                    },
+                );
+            }
+
+            #[test]
+            fn all_fields() {
+                #[derive(Debug, PartialEq)]
+                struct Foo(#[partial_eq(skip)] NoEq, #[partial_eq(skip)] i32);
+
+                #[derive(Debug, PartialEq)]
+                struct Bar {
+                    #[partial_eq(skip)]
+                    a: NoEq,
+                    #[partial_eq(skip)]
+                    b: bool,
+                }
+
+                assert_eq!(Foo(NoEq, 0), Foo(NoEq, 0));
+                assert_eq!(Foo(NoEq, 0), Foo(NoEq, 1));
+
+                assert_eq!(Bar { a: NoEq, b: true }, Bar { a: NoEq, b: true });
+                assert_eq!(Bar { a: NoEq, b: true }, Bar { a: NoEq, b: false });
+            }
+
+            #[test]
+            fn empty_struct() {
+                #[derive(Debug, PartialEq)]
+                #[partial_eq(skip)]
+                struct Foo;
+
+                #[derive(Debug, PartialEq)]
+                struct Bar {}
+
+                assert_eq!(Foo, Foo);
+
+                assert_eq!(Bar {}, Bar {});
+            }
+
+            #[test]
+            fn multifield_struct() {
+                #[derive(Debug, PartialEq)]
+                #[partial_eq(skip)]
+                struct Foo(NoEq, bool);
+
+                #[derive(Debug, PartialEq)]
+                #[partial_eq(skip)]
+                struct Bar {
+                    a: NoEq,
+                    b: bool,
+                }
+
+                assert_eq!(Foo(NoEq, true), Foo(NoEq, true));
+                assert_eq!(Foo(NoEq, true), Foo(NoEq, false));
+
+                assert_eq!(Bar { a: NoEq, b: true }, Bar { a: NoEq, b: true });
+                assert_eq!(Bar { a: NoEq, b: true }, Bar { a: NoEq, b: false });
+            }
+
+            #[test]
+            fn mixed() {
+                #[derive(Debug, derive_more::PartialEq)]
+                #[partial_eq(skip)]
+                struct Foo(NoEq, #[partial_eq(skip)] bool);
+
+                #[derive(Debug, PartialEq)]
+                #[partial_eq(skip)]
+                struct Bar {
+                    a: NoEq,
+
+                    #[partial_eq(skip)]
+                    b: bool,
+                }
+
+                assert_eq!(Foo(NoEq, true), Foo(NoEq, true));
+                assert_eq!(Foo(NoEq, true), Foo(NoEq, false));
+
+                assert_eq!(Bar { a: NoEq, b: true }, Bar { a: NoEq, b: true });
+                assert_eq!(Bar { a: NoEq, b: true }, Bar { a: NoEq, b: false });
+            }
+        }
+
         mod generic {
             #[cfg(not(feature = "std"))]
             use ::alloc::{boxed::Box, vec, vec::Vec};
@@ -282,6 +421,134 @@ mod structs {
                     ),
                 );
             }
+
+            mod skip {
+                use derive_more::PartialEq;
+
+                #[derive(Debug)]
+                struct NoEq;
+
+                #[test]
+                fn fields() {
+                    #[derive(Debug, PartialEq)]
+                    struct Foo<A, B, C>(
+                        #[partial_eq(skip)] A,
+                        B,
+                        #[partial_eq(skip)] C,
+                    );
+
+                    #[derive(Debug, PartialEq)]
+                    struct Bar<A, B, C> {
+                        #[partial_eq(skip)]
+                        a: A,
+                        i: B,
+                        #[partial_eq(skip)]
+                        b: C,
+                    }
+
+                    assert_eq!(Foo(NoEq, true, 0), Foo(NoEq, true, 0));
+                    assert_eq!(Foo(NoEq, true, 0), Foo(NoEq, true, 1));
+                    assert_ne!(Foo(NoEq, true, 0), Foo(NoEq, false, 0));
+
+                    assert_eq!(
+                        Bar {
+                            a: NoEq,
+                            i: 0,
+                            b: true,
+                        },
+                        Bar {
+                            a: NoEq,
+                            i: 0,
+                            b: true,
+                        },
+                    );
+                    assert_eq!(
+                        Bar {
+                            a: NoEq,
+                            i: 0,
+                            b: true,
+                        },
+                        Bar {
+                            a: NoEq,
+                            i: 0,
+                            b: false,
+                        },
+                    );
+                    assert_ne!(
+                        Bar {
+                            a: NoEq,
+                            i: 0,
+                            b: true,
+                        },
+                        Bar {
+                            a: NoEq,
+                            i: 1,
+                            b: true,
+                        },
+                    );
+                }
+
+                #[test]
+                fn all_fields() {
+                    #[derive(Debug, PartialEq)]
+                    struct Foo<A, B>(#[partial_eq(skip)] A, #[partial_eq(skip)] B);
+
+                    #[derive(Debug, PartialEq)]
+                    struct Bar<A, B> {
+                        #[partial_eq(skip)]
+                        a: A,
+                        #[partial_eq(skip)]
+                        b: B,
+                    }
+
+                    assert_eq!(Foo(NoEq, 0), Foo(NoEq, 0));
+                    assert_eq!(Foo(NoEq, 0), Foo(NoEq, 1));
+
+                    assert_eq!(Bar { a: NoEq, b: true }, Bar { a: NoEq, b: true });
+                    assert_eq!(Bar { a: NoEq, b: true }, Bar { a: NoEq, b: false });
+                }
+
+                #[test]
+                fn multifield_struct() {
+                    #[derive(Debug, PartialEq)]
+                    #[partial_eq(skip)]
+                    struct Foo<A, B>(A, B);
+
+                    #[derive(Debug, PartialEq)]
+                    #[partial_eq(skip)]
+                    struct Bar<A, B> {
+                        a: A,
+                        b: B,
+                    }
+
+                    assert_eq!(Foo(1, NoEq), Foo(1, NoEq));
+                    assert_eq!(Foo(1, NoEq), Foo(0, NoEq));
+
+                    assert_eq!(Bar { a: NoEq, b: true }, Bar { a: NoEq, b: true });
+                    assert_eq!(Bar { a: NoEq, b: true }, Bar { a: NoEq, b: false });
+                }
+
+                #[test]
+                fn mixed() {
+                    #[derive(Debug, PartialEq)]
+                    #[partial_eq(skip)]
+                    struct Foo<A, B>(A, #[partial_eq(skip)] B);
+
+                    #[derive(Debug, PartialEq)]
+                    #[partial_eq(skip)]
+                    struct Bar<A, B> {
+                        a: A,
+                        #[partial_eq(skip)]
+                        b: B,
+                    }
+
+                    assert_eq!(Foo(NoEq, true), Foo(NoEq, true));
+                    assert_eq!(Foo(NoEq, true), Foo(NoEq, false));
+
+                    assert_eq!(Bar { a: NoEq, b: true }, Bar { a: NoEq, b: true });
+                    assert_eq!(Bar { a: NoEq, b: true }, Bar { a: NoEq, b: false });
+                }
+            }
         }
     }
 }
@@ -447,6 +714,219 @@ mod enums {
             assert_ne!(E::Bar { b: vec![] }, E::Baz);
         }
 
+        mod skip {
+            use derive_more::PartialEq;
+
+            #[derive(Debug)]
+            struct NoEq;
+
+            #[test]
+            fn fields() {
+                #[derive(Debug, PartialEq)]
+                enum E {
+                    Foo(#[partial_eq(skip)] NoEq, bool, #[partial_eq(skip)] i32),
+                    Bar {
+                        #[partial_eq(skip)]
+                        a: NoEq,
+                        i: i32,
+                        #[partial_eq(skip)]
+                        b: bool,
+                    },
+                }
+
+                assert_eq!(E::Foo(NoEq, true, 0), E::Foo(NoEq, true, 0));
+                assert_eq!(E::Foo(NoEq, true, 0), E::Foo(NoEq, true, 1));
+                assert_ne!(E::Foo(NoEq, true, 0), E::Foo(NoEq, false, 0));
+
+                assert_eq!(
+                    E::Bar {
+                        a: NoEq,
+                        i: 0,
+                        b: true,
+                    },
+                    E::Bar {
+                        a: NoEq,
+                        i: 0,
+                        b: true,
+                    },
+                );
+                assert_eq!(
+                    E::Bar {
+                        a: NoEq,
+                        i: 0,
+                        b: true,
+                    },
+                    E::Bar {
+                        a: NoEq,
+                        i: 0,
+                        b: false,
+                    },
+                );
+                assert_ne!(
+                    E::Bar {
+                        a: NoEq,
+                        i: 0,
+                        b: true,
+                    },
+                    E::Bar {
+                        a: NoEq,
+                        i: 1,
+                        b: true,
+                    },
+                );
+
+                assert_ne!(
+                    E::Foo(NoEq, true, 0),
+                    E::Bar {
+                        a: NoEq,
+                        i: 1,
+                        b: true,
+                    },
+                );
+            }
+
+            #[test]
+            fn all_fields() {
+                #[derive(Debug, PartialEq)]
+                enum E {
+                    Foo(#[partial_eq(skip)] NoEq, #[partial_eq(skip)] i32),
+                    Bar {
+                        #[partial_eq(skip)]
+                        a: NoEq,
+                        #[partial_eq(skip)]
+                        b: bool,
+                    },
+                }
+
+                assert_eq!(E::Foo(NoEq, 0), E::Foo(NoEq, 0));
+                assert_eq!(E::Foo(NoEq, 0), E::Foo(NoEq, 1));
+
+                assert_eq!(E::Bar { a: NoEq, b: true }, E::Bar { a: NoEq, b: true });
+                assert_eq!(E::Bar { a: NoEq, b: true }, E::Bar { a: NoEq, b: false });
+
+                assert_ne!(E::Foo(NoEq, 0), E::Bar { a: NoEq, b: true });
+            }
+
+            #[test]
+            fn variants() {
+                #[derive(Debug, PartialEq)]
+                enum E {
+                    Foo(bool, i32),
+                    #[partial_eq(skip)]
+                    Bar {
+                        a: NoEq,
+                        b: bool,
+                    },
+                }
+
+                assert_eq!(E::Foo(true, 0), E::Foo(true, 0));
+                assert_ne!(E::Foo(true, 0), E::Foo(true, 1));
+                assert_ne!(E::Foo(true, 0), E::Foo(false, 0));
+
+                assert_eq!(E::Bar { a: NoEq, b: true }, E::Bar { a: NoEq, b: true });
+                assert_eq!(E::Bar { a: NoEq, b: true }, E::Bar { a: NoEq, b: false });
+
+                assert_ne!(E::Foo(true, 0), E::Bar { a: NoEq, b: true });
+            }
+
+            #[test]
+            fn all_variants() {
+                #[derive(Debug, PartialEq)]
+                enum E {
+                    #[partial_eq(skip)]
+                    Foo(bool, i32),
+                    #[partial_eq(skip)]
+                    Bar { a: NoEq, b: bool },
+                    #[partial_eq(skip)]
+                    Baz,
+                }
+
+                assert_eq!(E::Foo(true, 0), E::Foo(true, 0));
+                assert_eq!(E::Foo(true, 0), E::Foo(true, 1));
+                assert_eq!(E::Foo(true, 0), E::Foo(false, 0));
+                assert_eq!(E::Foo(true, 0), E::Foo(false, 1));
+
+                assert_eq!(E::Bar { a: NoEq, b: true }, E::Bar { a: NoEq, b: true });
+                assert_eq!(E::Bar { a: NoEq, b: true }, E::Bar { a: NoEq, b: false });
+
+                assert_eq!(E::Baz, E::Baz);
+
+                assert_ne!(E::Foo(true, 0), E::Bar { a: NoEq, b: true });
+                assert_ne!(E::Foo(true, 0), E::Baz);
+                assert_ne!(E::Bar { a: NoEq, b: true }, E::Baz);
+            }
+
+            #[test]
+            fn single_variant() {
+                #[derive(Debug, PartialEq)]
+                enum E {
+                    #[partial_eq(skip)]
+                    Bar { a: NoEq, b: bool },
+                }
+
+                assert_eq!(E::Bar { a: NoEq, b: true }, E::Bar { a: NoEq, b: true });
+                assert_eq!(E::Bar { a: NoEq, b: true }, E::Bar { a: NoEq, b: false });
+            }
+
+            #[test]
+            fn all_but_single_empty_variant() {
+                #[derive(Debug, PartialEq)]
+                enum E {
+                    #[partial_eq(skip)]
+                    Foo(bool, i32),
+                    #[partial_eq(skip)]
+                    Bar {
+                        a: NoEq,
+                        b: bool,
+                    },
+                    Baz,
+                }
+
+                assert_eq!(E::Foo(true, 0), E::Foo(true, 0));
+                assert_eq!(E::Foo(true, 0), E::Foo(true, 1));
+                assert_eq!(E::Foo(true, 0), E::Foo(false, 0));
+                assert_eq!(E::Foo(true, 0), E::Foo(false, 1));
+
+                assert_eq!(E::Bar { a: NoEq, b: true }, E::Bar { a: NoEq, b: true });
+                assert_eq!(E::Bar { a: NoEq, b: true }, E::Bar { a: NoEq, b: false });
+
+                assert_eq!(E::Baz, E::Baz);
+
+                assert_ne!(E::Foo(true, 0), E::Bar { a: NoEq, b: true });
+                assert_ne!(E::Foo(true, 0), E::Baz);
+                assert_ne!(E::Bar { a: NoEq, b: true }, E::Baz);
+            }
+
+            #[test]
+            fn mixed() {
+                #[derive(Debug, PartialEq)]
+                enum E {
+                    #[partial_eq(skip)]
+                    Foo(bool, i32),
+                    Bar {
+                        #[partial_eq(skip)]
+                        a: NoEq,
+                        b: bool,
+                    },
+                    Baz,
+                }
+
+                assert_eq!(E::Foo(true, 0), E::Foo(true, 0));
+                assert_eq!(E::Foo(true, 0), E::Foo(true, 1));
+                assert_eq!(E::Foo(true, 0), E::Foo(false, 0));
+                assert_eq!(E::Foo(true, 0), E::Foo(false, 1));
+
+                assert_eq!(E::Bar { a: NoEq, b: true }, E::Bar { a: NoEq, b: true });
+                assert_ne!(E::Bar { a: NoEq, b: true }, E::Bar { a: NoEq, b: false });
+
+                assert_eq!(E::Baz, E::Baz);
+
+                assert_ne!(E::Foo(true, 0), E::Bar { a: NoEq, b: true });
+                assert_ne!(E::Foo(true, 0), E::Baz);
+                assert_ne!(E::Bar { a: NoEq, b: true }, E::Baz);
+            }
+        }
+
         mod generic {
             #[cfg(not(feature = "std"))]
             use ::alloc::{boxed::Box, vec, vec::Vec};
@@ -685,6 +1165,255 @@ mod enums {
                 assert_ne!(E::<(), NoEq>::Foo(true, vec![]), E::Baz);
                 assert_ne!(E::<_, NoEq>::Bar { b: None, i: 3 }, E::Baz);
             }
+
+            mod skip {
+                use derive_more::PartialEq;
+
+                #[derive(Debug)]
+                struct NoEq;
+
+                #[test]
+                fn fields() {
+                    #[derive(Debug, PartialEq)]
+                    enum E<A, B, C> {
+                        Foo(#[partial_eq(skip)] A, B, #[partial_eq(skip)] C),
+                        Bar {
+                            #[partial_eq(skip)]
+                            a: A,
+                            i: C,
+                            #[partial_eq(skip)]
+                            b: B,
+                        },
+                    }
+
+                    assert_eq!(E::Foo(NoEq, true, 0), E::Foo(NoEq, true, 0));
+                    assert_eq!(E::Foo(NoEq, true, 0), E::Foo(NoEq, true, 1));
+                    assert_ne!(E::Foo(NoEq, true, 0), E::Foo(NoEq, false, 0));
+
+                    assert_eq!(
+                        E::Bar {
+                            a: NoEq,
+                            i: 0,
+                            b: true,
+                        },
+                        E::Bar {
+                            a: NoEq,
+                            i: 0,
+                            b: true,
+                        },
+                    );
+                    assert_eq!(
+                        E::Bar {
+                            a: NoEq,
+                            i: 0,
+                            b: true,
+                        },
+                        E::Bar {
+                            a: NoEq,
+                            i: 0,
+                            b: false,
+                        },
+                    );
+                    assert_ne!(
+                        E::Bar {
+                            a: NoEq,
+                            i: 0,
+                            b: true,
+                        },
+                        E::Bar {
+                            a: NoEq,
+                            i: 1,
+                            b: true,
+                        },
+                    );
+
+                    assert_ne!(
+                        E::Foo(NoEq, true, 0),
+                        E::Bar {
+                            a: NoEq,
+                            i: 1,
+                            b: true,
+                        },
+                    );
+                }
+
+                #[test]
+                fn all_fields() {
+                    #[derive(Debug, PartialEq)]
+                    enum E<A, B, C> {
+                        Foo(#[partial_eq(skip)] A, #[partial_eq(skip)] B),
+                        Bar {
+                            #[partial_eq(skip)]
+                            a: A,
+                            #[partial_eq(skip)]
+                            b: C,
+                        },
+                    }
+
+                    assert_eq!(E::<_, _, ()>::Foo(NoEq, 0), E::Foo(NoEq, 0));
+                    assert_eq!(E::<_, _, ()>::Foo(NoEq, 0), E::Foo(NoEq, 1));
+
+                    assert_eq!(
+                        E::<_, (), _>::Bar { a: NoEq, b: true },
+                        E::Bar { a: NoEq, b: true },
+                    );
+                    assert_eq!(
+                        E::<_, (), _>::Bar { a: NoEq, b: true },
+                        E::Bar { a: NoEq, b: false },
+                    );
+
+                    assert_ne!(E::Foo(NoEq, 0), E::Bar { a: NoEq, b: true });
+                }
+
+                #[test]
+                fn variants() {
+                    #[derive(Debug, PartialEq)]
+                    enum E<A, B, C> {
+                        Foo(A, B),
+                        #[partial_eq(skip)]
+                        Bar {
+                            a: C,
+                            b: A,
+                        },
+                    }
+
+                    assert_eq!(E::<_, _, NoEq>::Foo(true, 0), E::Foo(true, 0));
+                    assert_ne!(E::<_, _, NoEq>::Foo(true, 0), E::Foo(true, 1));
+                    assert_ne!(E::<_, _, NoEq>::Foo(true, 0), E::Foo(false, 0));
+
+                    assert_eq!(
+                        E::<_, i32, _>::Bar { a: NoEq, b: true },
+                        E::Bar { a: NoEq, b: true },
+                    );
+                    assert_eq!(
+                        E::<_, i32, _>::Bar { a: NoEq, b: true },
+                        E::Bar { a: NoEq, b: false },
+                    );
+
+                    assert_ne!(E::Foo(true, 0), E::Bar { a: NoEq, b: true });
+                }
+
+                #[test]
+                fn all_variants() {
+                    #[derive(Debug, PartialEq)]
+                    enum E<A, B, C> {
+                        #[partial_eq(skip)]
+                        Foo(A, B),
+                        #[partial_eq(skip)]
+                        Bar { a: C, b: A },
+                        #[partial_eq(skip)]
+                        Baz,
+                    }
+
+                    assert_eq!(E::<_, _, NoEq>::Foo(true, 0), E::Foo(true, 0));
+                    assert_eq!(E::<_, _, NoEq>::Foo(true, 0), E::Foo(true, 1));
+                    assert_eq!(E::<_, _, NoEq>::Foo(true, 0), E::Foo(false, 0));
+                    assert_eq!(E::<_, _, NoEq>::Foo(true, 0), E::Foo(false, 1));
+
+                    assert_eq!(
+                        E::<_, i32, _>::Bar { a: NoEq, b: true },
+                        E::Bar { a: NoEq, b: true },
+                    );
+                    assert_eq!(
+                        E::<_, i32, _>::Bar { a: NoEq, b: true },
+                        E::Bar { a: NoEq, b: false },
+                    );
+
+                    assert_eq!(E::<bool, i32, NoEq>::Baz, E::Baz);
+
+                    assert_ne!(E::Foo(true, 0), E::Bar { a: NoEq, b: true });
+                    assert_ne!(E::<_, _, NoEq>::Foo(true, 0), E::Baz);
+                    assert_ne!(E::<_, i32, _>::Bar { a: NoEq, b: true }, E::Baz);
+                }
+
+                #[test]
+                fn single_variant() {
+                    #[derive(Debug, PartialEq)]
+                    enum E<A, B> {
+                        #[partial_eq(skip)]
+                        Bar { a: A, b: B },
+                    }
+
+                    assert_eq!(
+                        E::Bar { a: NoEq, b: true },
+                        E::Bar { a: NoEq, b: true },
+                    );
+                    assert_eq!(
+                        E::Bar { a: NoEq, b: true },
+                        E::Bar { a: NoEq, b: false },
+                    );
+                }
+
+                #[test]
+                fn all_but_single_empty_variant() {
+                    #[derive(Debug, PartialEq)]
+                    enum E<A, B, C> {
+                        #[partial_eq(skip)]
+                        Foo(A, B),
+                        #[partial_eq(skip)]
+                        Bar {
+                            a: C,
+                            b: A,
+                        },
+                        Baz,
+                    }
+
+                    assert_eq!(E::<_, _, NoEq>::Foo(true, 0), E::Foo(true, 0));
+                    assert_eq!(E::<_, _, NoEq>::Foo(true, 0), E::Foo(true, 1));
+                    assert_eq!(E::<_, _, NoEq>::Foo(true, 0), E::Foo(false, 0));
+                    assert_eq!(E::<_, _, NoEq>::Foo(true, 0), E::Foo(false, 1));
+
+                    assert_eq!(
+                        E::<_, i32, _>::Bar { a: NoEq, b: true },
+                        E::Bar { a: NoEq, b: true },
+                    );
+                    assert_eq!(
+                        E::<_, i32, _>::Bar { a: NoEq, b: true },
+                        E::Bar { a: NoEq, b: false },
+                    );
+
+                    assert_eq!(E::<bool, i32, NoEq>::Baz, E::Baz);
+
+                    assert_ne!(E::Foo(true, 0), E::Bar { a: NoEq, b: true });
+                    assert_ne!(E::<_, _, NoEq>::Foo(true, 0), E::Baz);
+                    assert_ne!(E::<_, i32, _>::Bar { a: NoEq, b: true }, E::Baz);
+                }
+
+                #[test]
+                fn mixed() {
+                    #[derive(Debug, PartialEq)]
+                    enum E<A, B, C> {
+                        #[partial_eq(skip)]
+                        Foo(A, B),
+                        Bar {
+                            #[partial_eq(skip)]
+                            a: C,
+                            b: A,
+                        },
+                        Baz,
+                    }
+
+                    assert_eq!(E::<_, _, NoEq>::Foo(true, 0), E::Foo(true, 0));
+                    assert_eq!(E::<_, _, NoEq>::Foo(true, 0), E::Foo(true, 1));
+                    assert_eq!(E::<_, _, NoEq>::Foo(true, 0), E::Foo(false, 0));
+                    assert_eq!(E::<_, _, NoEq>::Foo(true, 0), E::Foo(false, 1));
+
+                    assert_eq!(
+                        E::<_, i32, _>::Bar { a: NoEq, b: true },
+                        E::Bar { a: NoEq, b: true },
+                    );
+                    assert_ne!(
+                        E::<_, i32, _>::Bar { a: NoEq, b: true },
+                        E::Bar { a: NoEq, b: false },
+                    );
+
+                    assert_eq!(E::<bool, i32, NoEq>::Baz, E::Baz);
+
+                    assert_ne!(E::Foo(true, 0), E::Bar { a: NoEq, b: true });
+                    assert_ne!(E::<_, _, NoEq>::Foo(true, 0), E::Baz);
+                    assert_ne!(E::<_, i32, _>::Bar { a: NoEq, b: true }, E::Baz);
+                }
+            }
         }
     }
 }