]> git.lizzy.rs Git - rust.git/blobdiff - crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
Simplify generated PartialOrd code
[rust.git] / crates / ide_assists / src / handlers / replace_derive_with_manual_impl.rs
index c76c517dccad969a5061daf4ed3970b1be6d5829..b04bd6ba09845165cbb439acc3006849b3da3fe0 100644 (file)
@@ -2,18 +2,16 @@
 use ide_db::helpers::{import_assets::NameToImport, mod_path_to_ast};
 use ide_db::items_locator;
 use itertools::Itertools;
-use syntax::ast::edit::AstNodeEdit;
-use syntax::ted;
 use syntax::{
-    ast::{self, make, AstNode, NameOwner},
+    ast::{self, make, AstNode, HasName},
     SyntaxKind::{IDENT, WHITESPACE},
 };
 
 use crate::{
     assist_context::{AssistBuilder, AssistContext, Assists},
     utils::{
-        add_trait_assoc_items_to_impl, filter_assoc_items, generate_trait_impl_text,
-        render_snippet, Cursor, DefaultMethods,
+        add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body,
+        generate_trait_impl_text, render_snippet, Cursor, DefaultMethods,
     },
     AssistId, AssistKind,
 };
@@ -168,91 +166,11 @@ fn impl_def_from_trait(
         add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
 
     // Generate a default `impl` function body for the derived trait.
-    if let ast::AssocItem::Fn(func) = &first_assoc_item {
-        match trait_path.segment().unwrap().name_ref().unwrap().text().as_str() {
-            "Debug" => gen_debug_impl(adt, func, annotated_name),
-            _ => {}
-        };
-    }
-    Some((impl_def, first_assoc_item))
-}
-
-/// Generate a `Debug` impl based on the fields and members of the target type.
-fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) {
-    match adt {
-        ast::Adt::Union(_) => {} // `Debug` cannot be derived for unions, so no default impl can be provided.
-        ast::Adt::Enum(enum_) => {
-            // => match self { Self::Variant => write!(f, "Variant") }
-            if let Some(list) = enum_.variant_list() {
-                let mut arms = vec![];
-                for variant in list.variants() {
-                    let name = variant.name().unwrap();
-
-                    let left = make::ext::ident_path("Self");
-                    let right = make::ext::ident_path(&format!("{}", name));
-                    let variant_name = make::path_pat(make::path_concat(left, right));
-
-                    let target = make::expr_path(make::ext::ident_path("f").into());
-                    let fmt_string = make::expr_literal(&(format!("\"{}\"", name))).into();
-                    let args = make::arg_list(vec![target, fmt_string]);
-                    let macro_name = make::expr_path(make::ext::ident_path("write"));
-                    let macro_call = make::expr_macro_call(macro_name, args);
-
-                    arms.push(make::match_arm(Some(variant_name.into()), None, macro_call.into()));
-                }
-
-                let match_target = make::expr_path(make::ext::ident_path("self"));
-                let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
-                let match_expr = make::expr_match(match_target, list);
-
-                let body = make::block_expr(None, Some(match_expr));
-                let body = body.indent(ast::edit::IndentLevel(1));
-                ted::replace(func.body().unwrap().syntax(), body.clone_for_update().syntax());
-            }
-        }
-        ast::Adt::Struct(strukt) => {
-            let name = format!("\"{}\"", annotated_name);
-            let args = make::arg_list(Some(make::expr_literal(&name).into()));
-            let target = make::expr_path(make::ext::ident_path("f"));
-
-            let expr = match strukt.field_list() {
-                None => {
-                    // => f.debug_struct("Name").finish()
-                    make::expr_method_call(target, "debug_struct", args)
-                }
-                Some(ast::FieldList::RecordFieldList(field_list)) => {
-                    // => f.debug_struct("Name").field("foo", &self.foo).finish()
-                    let mut expr = make::expr_method_call(target, "debug_struct", args);
-                    for field in field_list.fields() {
-                        if let Some(name) = field.name() {
-                            let f_name = make::expr_literal(&(format!("\"{}\"", name))).into();
-                            let f_path = make::expr_path(make::ext::ident_path("self"));
-                            let f_path = make::expr_ref(f_path, false);
-                            let f_path = make::expr_field(f_path, &format!("{}", name)).into();
-                            let args = make::arg_list(vec![f_name, f_path]);
-                            expr = make::expr_method_call(expr, "field", args);
-                        }
-                    }
-                    expr
-                }
-                Some(ast::FieldList::TupleFieldList(field_list)) => {
-                    // => f.debug_tuple("Name").field(self.0).finish()
-                    let mut expr = make::expr_method_call(target, "debug_tuple", args);
-                    for (idx, _) in field_list.fields().enumerate() {
-                        let f_path = make::expr_path(make::ext::ident_path("self"));
-                        let f_path = make::expr_ref(f_path, false);
-                        let f_path = make::expr_field(f_path, &format!("{}", idx)).into();
-                        expr = make::expr_method_call(expr, "field", make::arg_list(Some(f_path)));
-                    }
-                    expr
-                }
-            };
+    if let ast::AssocItem::Fn(ref func) = first_assoc_item {
+        let _ = gen_trait_fn_body(func, trait_path, adt);
+    };
 
-            let expr = make::expr_method_call(expr, "finish", make::arg_list(None));
-            let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
-            ted::replace(func.body().unwrap().syntax(), body.clone_for_update().syntax());
-        }
-    }
+    Some((impl_def, first_assoc_item))
 }
 
 fn update_attribute(
@@ -300,36 +218,19 @@ fn add_custom_impl_debug_record_struct() {
         check_assist(
             replace_derive_with_manual_impl,
             r#"
-mod fmt {
-    pub struct Error;
-    pub type Result = Result<(), Error>;
-    pub struct Formatter<'a>;
-    pub trait Debug {
-        fn fmt(&self, f: &mut Formatter<'_>) -> Result;
-    }
-}
-
+//- minicore: fmt
 #[derive(Debu$0g)]
 struct Foo {
     bar: String,
 }
 "#,
             r#"
-mod fmt {
-    pub struct Error;
-    pub type Result = Result<(), Error>;
-    pub struct Formatter<'a>;
-    pub trait Debug {
-        fn fmt(&self, f: &mut Formatter<'_>) -> Result;
-    }
-}
-
 struct Foo {
     bar: String,
 }
 
-impl fmt::Debug for Foo {
-    $0fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+impl core::fmt::Debug for Foo {
+    $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
         f.debug_struct("Foo").field("bar", &self.bar).finish()
     }
 }
@@ -341,115 +242,791 @@ fn add_custom_impl_debug_tuple_struct() {
         check_assist(
             replace_derive_with_manual_impl,
             r#"
-mod fmt {
-    pub struct Error;
-    pub type Result = Result<(), Error>;
-    pub struct Formatter<'a>;
-    pub trait Debug {
-        fn fmt(&self, f: &mut Formatter<'_>) -> Result;
+//- minicore: fmt
+#[derive(Debu$0g)]
+struct Foo(String, usize);
+"#,
+            r#"struct Foo(String, usize);
+
+impl core::fmt::Debug for Foo {
+    $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        f.debug_tuple("Foo").field(&self.0).field(&self.1).finish()
     }
 }
+"#,
+        )
+    }
+    #[test]
+    fn add_custom_impl_debug_empty_struct() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: fmt
+#[derive(Debu$0g)]
+struct Foo;
+"#,
+            r#"
+struct Foo;
 
+impl core::fmt::Debug for Foo {
+    $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        f.debug_struct("Foo").finish()
+    }
+}
+"#,
+        )
+    }
+    #[test]
+    fn add_custom_impl_debug_enum() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: fmt
 #[derive(Debu$0g)]
-struct Foo(String, usize);
+enum Foo {
+    Bar,
+    Baz,
+}
 "#,
             r#"
-mod fmt {
-    pub struct Error;
-    pub type Result = Result<(), Error>;
-    pub struct Formatter<'a>;
-    pub trait Debug {
-        fn fmt(&self, f: &mut Formatter<'_>) -> Result;
+enum Foo {
+    Bar,
+    Baz,
+}
+
+impl core::fmt::Debug for Foo {
+    $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        match self {
+            Self::Bar => write!(f, "Bar"),
+            Self::Baz => write!(f, "Baz"),
+        }
     }
 }
+"#,
+        )
+    }
 
-struct Foo(String, usize);
+    #[test]
+    fn add_custom_impl_debug_tuple_enum() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: fmt
+#[derive(Debu$0g)]
+enum Foo {
+    Bar(usize, usize),
+    Baz,
+}
+"#,
+            r#"
+enum Foo {
+    Bar(usize, usize),
+    Baz,
+}
 
-impl fmt::Debug for Foo {
-    $0fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_tuple("Foo").field(&self.0).field(&self.1).finish()
+impl core::fmt::Debug for Foo {
+    $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        match self {
+            Self::Bar(arg0, arg1) => f.debug_tuple("Bar").field(arg0).field(arg1).finish(),
+            Self::Baz => write!(f, "Baz"),
+        }
     }
 }
 "#,
         )
     }
     #[test]
-    fn add_custom_impl_debug_empty_struct() {
+    fn add_custom_impl_debug_record_enum() {
         check_assist(
             replace_derive_with_manual_impl,
             r#"
-mod fmt {
-    pub struct Error;
-    pub type Result = Result<(), Error>;
-    pub struct Formatter<'a>;
-    pub trait Debug {
-        fn fmt(&self, f: &mut Formatter<'_>) -> Result;
+//- minicore: fmt
+#[derive(Debu$0g)]
+enum Foo {
+    Bar {
+        baz: usize,
+        qux: usize,
+    },
+    Baz,
+}
+"#,
+            r#"
+enum Foo {
+    Bar {
+        baz: usize,
+        qux: usize,
+    },
+    Baz,
+}
+
+impl core::fmt::Debug for Foo {
+    $0fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        match self {
+            Self::Bar { baz, qux } => f.debug_struct("Bar").field("baz", baz).field("qux", qux).finish(),
+            Self::Baz => write!(f, "Baz"),
+        }
     }
 }
+"#,
+        )
+    }
+    #[test]
+    fn add_custom_impl_default_record_struct() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: default
+#[derive(Defau$0lt)]
+struct Foo {
+    foo: usize,
+}
+"#,
+            r#"
+struct Foo {
+    foo: usize,
+}
 
-#[derive(Debu$0g)]
+impl Default for Foo {
+    $0fn default() -> Self {
+        Self { foo: Default::default() }
+    }
+}
+"#,
+        )
+    }
+    #[test]
+    fn add_custom_impl_default_tuple_struct() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: default
+#[derive(Defau$0lt)]
+struct Foo(usize);
+"#,
+            r#"
+struct Foo(usize);
+
+impl Default for Foo {
+    $0fn default() -> Self {
+        Self(Default::default())
+    }
+}
+"#,
+        )
+    }
+    #[test]
+    fn add_custom_impl_default_empty_struct() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: default
+#[derive(Defau$0lt)]
 struct Foo;
 "#,
             r#"
-mod fmt {
-    pub struct Error;
-    pub type Result = Result<(), Error>;
-    pub struct Formatter<'a>;
-    pub trait Debug {
-        fn fmt(&self, f: &mut Formatter<'_>) -> Result;
+struct Foo;
+
+impl Default for Foo {
+    $0fn default() -> Self {
+        Self {  }
     }
 }
+"#,
+        )
+    }
 
-struct Foo;
+    #[test]
+    fn add_custom_impl_hash_record_struct() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: hash
+#[derive(Has$0h)]
+struct Foo {
+    bin: usize,
+    bar: usize,
+}
+"#,
+            r#"
+struct Foo {
+    bin: usize,
+    bar: usize,
+}
 
-impl fmt::Debug for Foo {
-    $0fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("Foo").finish()
+impl core::hash::Hash for Foo {
+    $0fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+        self.bin.hash(state);
+        self.bar.hash(state);
     }
 }
 "#,
         )
     }
+
     #[test]
-    fn add_custom_impl_debug_enum() {
+    fn add_custom_impl_hash_tuple_struct() {
         check_assist(
             replace_derive_with_manual_impl,
             r#"
-mod fmt {
-    pub struct Error;
-    pub type Result = Result<(), Error>;
-    pub struct Formatter<'a>;
-    pub trait Debug {
-        fn fmt(&self, f: &mut Formatter<'_>) -> Result;
+//- minicore: hash
+#[derive(Has$0h)]
+struct Foo(usize, usize);
+"#,
+            r#"
+struct Foo(usize, usize);
+
+impl core::hash::Hash for Foo {
+    $0fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+        self.0.hash(state);
+        self.1.hash(state);
     }
 }
+"#,
+        )
+    }
 
-#[derive(Debu$0g)]
+    #[test]
+    fn add_custom_impl_hash_enum() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: hash
+#[derive(Has$0h)]
 enum Foo {
     Bar,
     Baz,
 }
 "#,
             r#"
-mod fmt {
-    pub struct Error;
-    pub type Result = Result<(), Error>;
-    pub struct Formatter<'a>;
-    pub trait Debug {
-        fn fmt(&self, f: &mut Formatter<'_>) -> Result;
+enum Foo {
+    Bar,
+    Baz,
+}
+
+impl core::hash::Hash for Foo {
+    $0fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+        core::mem::discriminant(self).hash(state);
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_clone_record_struct() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: clone
+#[derive(Clo$0ne)]
+struct Foo {
+    bin: usize,
+    bar: usize,
+}
+"#,
+            r#"
+struct Foo {
+    bin: usize,
+    bar: usize,
+}
+
+impl Clone for Foo {
+    $0fn clone(&self) -> Self {
+        Self { bin: self.bin.clone(), bar: self.bar.clone() }
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_clone_tuple_struct() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: clone
+#[derive(Clo$0ne)]
+struct Foo(usize, usize);
+"#,
+            r#"
+struct Foo(usize, usize);
+
+impl Clone for Foo {
+    $0fn clone(&self) -> Self {
+        Self(self.0.clone(), self.1.clone())
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_clone_empty_struct() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: clone
+#[derive(Clo$0ne)]
+struct Foo;
+"#,
+            r#"
+struct Foo;
+
+impl Clone for Foo {
+    $0fn clone(&self) -> Self {
+        Self {  }
     }
 }
+"#,
+        )
+    }
 
+    #[test]
+    fn add_custom_impl_clone_enum() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: clone
+#[derive(Clo$0ne)]
+enum Foo {
+    Bar,
+    Baz,
+}
+"#,
+            r#"
 enum Foo {
     Bar,
     Baz,
 }
 
-impl fmt::Debug for Foo {
-    $0fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+impl Clone for Foo {
+    $0fn clone(&self) -> Self {
         match self {
-            Self::Bar => write!(f, "Bar"),
-            Self::Baz => write!(f, "Baz"),
+            Self::Bar => Self::Bar,
+            Self::Baz => Self::Baz,
+        }
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_clone_tuple_enum() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: clone
+#[derive(Clo$0ne)]
+enum Foo {
+    Bar(String),
+    Baz,
+}
+"#,
+            r#"
+enum Foo {
+    Bar(String),
+    Baz,
+}
+
+impl Clone for Foo {
+    $0fn clone(&self) -> Self {
+        match self {
+            Self::Bar(arg0) => Self::Bar(arg0.clone()),
+            Self::Baz => Self::Baz,
+        }
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_clone_record_enum() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: clone
+#[derive(Clo$0ne)]
+enum Foo {
+    Bar {
+        bin: String,
+    },
+    Baz,
+}
+"#,
+            r#"
+enum Foo {
+    Bar {
+        bin: String,
+    },
+    Baz,
+}
+
+impl Clone for Foo {
+    $0fn clone(&self) -> Self {
+        match self {
+            Self::Bar { bin } => Self::Bar { bin: bin.clone() },
+            Self::Baz => Self::Baz,
+        }
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_partial_ord_record_struct() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: ord
+#[derive(Partial$0Ord)]
+struct Foo {
+    bin: usize,
+}
+"#,
+            r#"
+struct Foo {
+    bin: usize,
+}
+
+impl PartialOrd for Foo {
+    $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+        self.bin.partial_cmp(other.bin)
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_partial_ord_record_struct_multi_field() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: ord
+#[derive(Partial$0Ord)]
+struct Foo {
+    bin: usize,
+    bar: usize,
+    baz: usize,
+}
+"#,
+            r#"
+struct Foo {
+    bin: usize,
+    bar: usize,
+    baz: usize,
+}
+
+impl PartialOrd for Foo {
+    $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+        (self.bin, self.bar, self.baz).partial_cmp((other.bin, other.bar, other.baz))
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_partial_ord_tuple_struct() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: ord
+#[derive(Partial$0Ord)]
+struct Foo(usize, usize, usize);
+"#,
+            r#"
+struct Foo(usize, usize, usize);
+
+impl PartialOrd for Foo {
+    $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+        (self.0, self.1, self.2).partial_cmp((other.0, other.1, other.2))
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_partial_ord_enum() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: ord
+#[derive(Partial$0Ord)]
+enum Foo {
+    Bin,
+    Bar,
+    Baz,
+}
+"#,
+            r#"
+enum Foo {
+    Bin,
+    Bar,
+    Baz,
+}
+
+impl PartialOrd for Foo {
+    $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+        core::mem::discriminant(self).partial_cmp(core::mem::discriminant(other))
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_partial_ord_record_enum() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: ord
+#[derive(Partial$0Ord)]
+enum Foo {
+    Bar {
+        bin: String,
+    },
+    Baz {
+        qux: String,
+        fez: String,
+    },
+    Qux {},
+    Bin,
+}
+"#,
+            r#"
+enum Foo {
+    Bar {
+        bin: String,
+    },
+    Baz {
+        qux: String,
+        fez: String,
+    },
+    Qux {},
+    Bin,
+}
+
+impl PartialOrd for Foo {
+    $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+        match (self, other) {
+            (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin.partial_cmp(r_bin),
+            (Self::Baz { qux: l_qux, fez: l_fez }, Self::Baz { qux: r_qux, fez: r_fez }) => {
+                (l_qux, l_fez).partial_cmp((r_qux, r_fez))
+            }
+            _ => core::mem::discriminant(self).partial_cmp(core::mem::discriminant(other)),
+        }
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_partial_ord_tuple_enum() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: ord
+#[derive(Partial$0Ord)]
+enum Foo {
+    Bar(String),
+    Baz(String, String),
+    Qux(),
+    Bin,
+}
+"#,
+            r#"
+enum Foo {
+    Bar(String),
+    Baz(String, String),
+    Qux(),
+    Bin,
+}
+
+impl PartialOrd for Foo {
+    $0fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+        match (self, other) {
+            (Self::Bar(l0), Self::Bar(r0)) => l0.partial_cmp(r0),
+            (Self::Baz(l0, l1), Self::Baz(r0, r1)) => {
+                (l0, l1).partial_cmp((r0, r1))
+            }
+            _ => core::mem::discriminant(self).partial_cmp(core::mem::discriminant(other)),
+        }
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_partial_eq_record_struct() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: eq
+#[derive(Partial$0Eq)]
+struct Foo {
+    bin: usize,
+    bar: usize,
+}
+"#,
+            r#"
+struct Foo {
+    bin: usize,
+    bar: usize,
+}
+
+impl PartialEq for Foo {
+    $0fn eq(&self, other: &Self) -> bool {
+        self.bin == other.bin && self.bar == other.bar
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_partial_eq_tuple_struct() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: eq
+#[derive(Partial$0Eq)]
+struct Foo(usize, usize);
+"#,
+            r#"
+struct Foo(usize, usize);
+
+impl PartialEq for Foo {
+    $0fn eq(&self, other: &Self) -> bool {
+        self.0 == other.0 && self.1 == other.1
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_partial_eq_empty_struct() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: eq
+#[derive(Partial$0Eq)]
+struct Foo;
+"#,
+            r#"
+struct Foo;
+
+impl PartialEq for Foo {
+    $0fn eq(&self, other: &Self) -> bool {
+        true
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_partial_eq_enum() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: eq
+#[derive(Partial$0Eq)]
+enum Foo {
+    Bar,
+    Baz,
+}
+"#,
+            r#"
+enum Foo {
+    Bar,
+    Baz,
+}
+
+impl PartialEq for Foo {
+    $0fn eq(&self, other: &Self) -> bool {
+        core::mem::discriminant(self) == core::mem::discriminant(other)
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_partial_eq_tuple_enum() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: eq
+#[derive(Partial$0Eq)]
+enum Foo {
+    Bar(String),
+    Baz,
+}
+"#,
+            r#"
+enum Foo {
+    Bar(String),
+    Baz,
+}
+
+impl PartialEq for Foo {
+    $0fn eq(&self, other: &Self) -> bool {
+        match (self, other) {
+            (Self::Bar(l0), Self::Bar(r0)) => l0 == r0,
+            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
+        }
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn add_custom_impl_partial_eq_record_enum() {
+        check_assist(
+            replace_derive_with_manual_impl,
+            r#"
+//- minicore: eq
+#[derive(Partial$0Eq)]
+enum Foo {
+    Bar {
+        bin: String,
+    },
+    Baz {
+        qux: String,
+        fez: String,
+    },
+    Qux {},
+    Bin,
+}
+"#,
+            r#"
+enum Foo {
+    Bar {
+        bin: String,
+    },
+    Baz {
+        qux: String,
+        fez: String,
+    },
+    Qux {},
+    Bin,
+}
+
+impl PartialEq for Foo {
+    $0fn eq(&self, other: &Self) -> bool {
+        match (self, other) {
+            (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin,
+            (Self::Baz { qux: l_qux, fez: l_fez }, Self::Baz { qux: r_qux, fez: r_fez }) => l_qux == r_qux && l_fez == r_fez,
+            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
         }
     }
 }