diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs index d29a312eba5..d6319ea5c13 100644 --- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs @@ -302,6 +302,71 @@ impl core::fmt::Debug for Foo { } } } +"#, + ) + } + + #[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 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_record_enum() { + check_assist( + replace_derive_with_manual_impl, + r#" +//- 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"), + } + } +} "#, ) } diff --git a/crates/ide_assists/src/utils/gen_trait_fn_body.rs b/crates/ide_assists/src/utils/gen_trait_fn_body.rs index 5a8914b3316..b9c7da71b5c 100644 --- a/crates/ide_assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide_assists/src/utils/gen_trait_fn_body.rs @@ -149,16 +149,90 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut arms = vec![]; for variant in list.variants() { let name = variant.name()?; - let variant_name = - make::path_pat(make::ext::path_from_idents(["Self", &format!("{}", name)])?); - + let variant_name = make::ext::path_from_idents(["Self", &format!("{}", name)])?; 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())); + match variant.field_list() { + Some(ast::FieldList::RecordFieldList(list)) => { + // => f.debug_struct(name) + let target = make::expr_path(make::ext::ident_path("f")); + let method = make::name_ref("debug_struct"); + let struct_name = format!("\"{}\"", name); + let args = make::arg_list(Some(make::expr_literal(&struct_name).into())); + let mut expr = make::expr_method_call(target, method, args); + + let mut pats = vec![]; + for field in list.fields() { + let field_name = field.name()?; + + // create a field pattern for use in `MyStruct { fields.. }` + let pat = make::ident_pat(false, false, field_name.clone()); + pats.push(pat.into()); + + // => .field("field_name", field) + let method_name = make::name_ref("field"); + let name = make::expr_literal(&(format!("\"{}\"", field_name))).into(); + let path = &format!("{}", field_name); + let path = make::expr_path(make::ext::ident_path(path)); + let args = make::arg_list(vec![name, path]); + expr = make::expr_method_call(expr, method_name, args); + } + + // => .finish() + let method = make::name_ref("finish"); + let expr = make::expr_method_call(expr, method, make::arg_list(None)); + + // => MyStruct { fields.. } => f.debug_struct("MyStruct")...finish(), + let pat = make::record_pat(variant_name.clone(), pats.into_iter()); + arms.push(make::match_arm(Some(pat.into()), None, expr)); + } + Some(ast::FieldList::TupleFieldList(list)) => { + // => f.debug_tuple(name) + let target = make::expr_path(make::ext::ident_path("f")); + let method = make::name_ref("debug_tuple"); + let struct_name = format!("\"{}\"", name); + let args = make::arg_list(Some(make::expr_literal(&struct_name).into())); + let mut expr = make::expr_method_call(target, method, args); + + let mut pats = vec![]; + for (i, _) in list.fields().enumerate() { + let name = format!("arg{}", i); + + // create a field pattern for use in `MyStruct(fields..)` + let field_name = make::name(&name); + let pat = make::ident_pat(false, false, field_name.clone()); + pats.push(pat.into()); + + // => .field(field) + let method_name = make::name_ref("field"); + let field_path = &format!("{}", name); + let field_path = make::expr_path(make::ext::ident_path(field_path)); + let args = make::arg_list(vec![field_path]); + expr = make::expr_method_call(expr, method_name, args); + } + + // => .finish() + let method = make::name_ref("finish"); + let expr = make::expr_method_call(expr, method, make::arg_list(None)); + + // => MyStruct (fields..) => f.debug_tuple("MyStruct")...finish(), + let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter()); + arms.push(make::match_arm(Some(pat.into()), None, expr)); + } + None => { + let fmt_string = make::expr_literal(&(format!("\"{}\"", name))).into(); + let args = make::arg_list([target, fmt_string]); + let macro_name = make::expr_path(make::ext::ident_path("write")); + let macro_call = make::expr_macro_call(macro_name, args); + + let variant_name = make::path_pat(variant_name); + arms.push(make::match_arm( + Some(variant_name.into()), + None, + macro_call.into(), + )); + } + } } let match_target = make::expr_path(make::ext::ident_path("self")); @@ -190,7 +264,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { 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]); + let args = make::arg_list([f_name, f_path]); expr = make::expr_method_call(expr, make::name_ref("field"), args); } expr