From f2e547d5879eb26883e520319347795cbc84f0f9 Mon Sep 17 00:00:00 2001 From: Irina Shestak Date: Sun, 1 Aug 2021 20:46:23 +0200 Subject: [PATCH 01/15] impl Debug def from trait --- .../replace_derive_with_manual_impl.rs | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) 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 a69c1c00801..761ccff8516 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 @@ -2,6 +2,9 @@ use hir::ModuleDef; 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::ast::Pat; +use syntax::ted; use syntax::{ ast::{self, make, AstNode, NameOwner}, SyntaxKind::{IDENT, WHITESPACE}, @@ -127,6 +130,9 @@ fn add_assist( let mut cursor = Cursor::Before(first_assoc_item.syntax()); let placeholder; if let ast::AssocItem::Fn(ref func) = first_assoc_item { + // need to know what kind of derive this is: if it's Derive Debug, special case it. + // the name of the struct + // list of fields of the struct if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) { if m.syntax().text() == "todo!()" { @@ -163,6 +169,22 @@ fn impl_def_from_trait( make::impl_trait(trait_path.clone(), make::ext::ident_path(&annotated_name.text())); let (impl_def, first_assoc_item) = add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope); + if let ast::AssocItem::Fn(fn_) = &first_assoc_item { + if trait_path.segment().unwrap().name_ref().unwrap().text() == "Debug" { + let f_expr = make::expr_path(make::ext::ident_path("f")); + let args = make::arg_list(Some(make::expr_path(make::ext::ident_path( + annotated_name.text().as_str(), + )))); + let body = + make::block_expr(None, Some(make::expr_method_call(f_expr, "debug_struct", args))) + .indent(ast::edit::IndentLevel(1)); + + ted::replace( + fn_.body().unwrap().tail_expr().unwrap().syntax(), + body.clone_for_update().syntax(), + ); + } + } Some((impl_def, first_assoc_item)) } @@ -240,8 +262,8 @@ struct Foo { } impl fmt::Debug for Foo { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ${0:todo!()} + $0fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Foo").field("bar", &self.bar).finish() } } "#, From e2ab2e12a0e1ce0836cd1cdebf6338580f2aca14 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Aug 2021 13:09:50 +0200 Subject: [PATCH 02/15] wip --- .../replace_derive_with_manual_impl.rs | 47 +++++++++++++------ crates/ide_assists/src/tests/generated.rs | 4 +- 2 files changed, 34 insertions(+), 17 deletions(-) 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 761ccff8516..887fa34c781 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 @@ -35,8 +35,8 @@ use crate::{ // struct S; // // impl Debug for S { -// fn fmt(&self, f: &mut Formatter) -> Result<()> { -// ${0:todo!()} +// $0fn fmt(&self, f: &mut Formatter) -> Result<()> { +// f.debug_struct(S) // } // } // ``` @@ -114,7 +114,7 @@ fn add_assist( |builder| { let insert_pos = adt.syntax().text_range().end(); let impl_def_with_items = - impl_def_from_trait(&ctx.sema, &annotated_name, trait_, trait_path); + impl_def_from_trait(&ctx.sema, adt, &annotated_name, trait_, trait_path); update_attribute(builder, input, &trait_name, attr); let trait_path = format!("{}", trait_path); match (ctx.config.snippet_cap, impl_def_with_items) { @@ -155,6 +155,7 @@ fn add_assist( fn impl_def_from_trait( sema: &hir::Semantics, + adt: &ast::Adt, annotated_name: &ast::Name, trait_: Option, trait_path: &ast::Path, @@ -169,25 +170,41 @@ fn impl_def_from_trait( make::impl_trait(trait_path.clone(), make::ext::ident_path(&annotated_name.text())); let (impl_def, first_assoc_item) = add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope); + if let ast::AssocItem::Fn(fn_) = &first_assoc_item { if trait_path.segment().unwrap().name_ref().unwrap().text() == "Debug" { - let f_expr = make::expr_path(make::ext::ident_path("f")); - let args = make::arg_list(Some(make::expr_path(make::ext::ident_path( - annotated_name.text().as_str(), - )))); - let body = - make::block_expr(None, Some(make::expr_method_call(f_expr, "debug_struct", args))) - .indent(ast::edit::IndentLevel(1)); - - ted::replace( - fn_.body().unwrap().tail_expr().unwrap().syntax(), - body.clone_for_update().syntax(), - ); + gen_debug_impl(adt, fn_, annotated_name); } } Some((impl_def, first_assoc_item)) } +fn gen_debug_impl(adt: &ast::Adt, fn_: &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(_) => {} // TODO + ast::Adt::Struct(strukt) => { + match strukt.field_list() { + Some(ast::FieldList::RecordFieldList(field_list)) => { + 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 mut expr = make::expr_method_call(target, "debug_struct", args); + for field in field_list.fields() { + let args = make::arg_list(Some(make::expr_path(&name).into())); + expr = make::expr_method_call(expr, "field", args); + } + 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(fn_.body().unwrap().syntax(), body.clone_for_update().syntax()); + } + Some(ast::FieldList::TupleFieldList(field_list)) => {} + None => {} // `Debug` cannot be implemented for an incomplete struct. + } + } + } +} + fn update_attribute( builder: &mut AssistBuilder, input: &ast::TokenTree, diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index ebf312aa3f8..54dde4767c3 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs @@ -1363,8 +1363,8 @@ trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; } struct S; impl Debug for S { - fn fmt(&self, f: &mut Formatter) -> Result<()> { - ${0:todo!()} + $0fn fmt(&self, f: &mut Formatter) -> Result<()> { + f.debug_struct(S) } } "#####, From fd7236c7917c81f19b1e2f859b15e9d30bab7e3c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Aug 2021 13:50:49 +0200 Subject: [PATCH 03/15] debug for record field structs --- .../replace_derive_with_manual_impl.rs | 44 ++++++++++++------- crates/ide_assists/src/tests/generated.rs | 2 +- crates/syntax/src/ast/make.rs | 3 ++ 3 files changed, 32 insertions(+), 17 deletions(-) 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 887fa34c781..8b82cbf5e05 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 @@ -36,7 +36,7 @@ use crate::{ // // impl Debug for S { // $0fn fmt(&self, f: &mut Formatter) -> Result<()> { -// f.debug_struct(S) +// f.debug_struct("S").finish() // } // } // ``` @@ -183,25 +183,37 @@ fn gen_debug_impl(adt: &ast::Adt, fn_: &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(_) => {} // TODO - ast::Adt::Struct(strukt) => { - match strukt.field_list() { - Some(ast::FieldList::RecordFieldList(field_list)) => { - 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 mut expr = make::expr_method_call(target, "debug_struct", args); - for field in field_list.fields() { - let args = make::arg_list(Some(make::expr_path(&name).into())); + ast::Adt::Struct(strukt) => match strukt.field_list() { + Some(ast::FieldList::RecordFieldList(field_list)) => { + 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 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); } - 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(fn_.body().unwrap().syntax(), body.clone_for_update().syntax()); } - Some(ast::FieldList::TupleFieldList(field_list)) => {} - None => {} // `Debug` cannot be implemented for an incomplete struct. + 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(fn_.body().unwrap().syntax(), body.clone_for_update().syntax()); } - } + Some(ast::FieldList::TupleFieldList(field_list)) => {} + None => { + 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 = make::expr_method_call(target, "debug_struct", args); + 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(fn_.body().unwrap().syntax(), body.clone_for_update().syntax()); + } + }, } } diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 54dde4767c3..195f958208e 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs @@ -1364,7 +1364,7 @@ struct S; impl Debug for S { $0fn fmt(&self, f: &mut Formatter) -> Result<()> { - f.debug_struct(S) + f.debug_struct("S").finish() } } "#####, diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index e5fff983d0c..71254f08543 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -318,6 +318,9 @@ pub fn expr_closure(pats: impl IntoIterator, expr: ast::Expr) let params = pats.into_iter().join(", "); expr_from_text(&format!("|{}| {}", params, expr)) } +pub fn expr_field(receiver: ast::Expr, field: &str) -> ast::Expr { + expr_from_text(&format!("{}.{}", receiver, field)) +} pub fn expr_paren(expr: ast::Expr) -> ast::Expr { expr_from_text(&format!("({})", expr)) } From aa09141a8a838a09815aa84668053cc0999f8873 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Aug 2021 14:08:02 +0200 Subject: [PATCH 04/15] finish debug_struct impls --- .../replace_derive_with_manual_impl.rs | 94 ++++++++++++++++++- 1 file changed, 91 insertions(+), 3 deletions(-) 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 8b82cbf5e05..3ad1c46878c 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 @@ -3,7 +3,6 @@ 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::ast::Pat; use syntax::ted; use syntax::{ ast::{self, make, AstNode, NameOwner}, @@ -203,7 +202,22 @@ fn gen_debug_impl(adt: &ast::Adt, fn_: &ast::Fn, annotated_name: &ast::Name) { let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); ted::replace(fn_.body().unwrap().syntax(), body.clone_for_update().syntax()); } - Some(ast::FieldList::TupleFieldList(field_list)) => {} + Some(ast::FieldList::TupleFieldList(field_list)) => { + 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 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(); + let args = make::arg_list(Some(f_path)); + expr = make::expr_method_call(expr, "field", args); + } + 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(fn_.body().unwrap().syntax(), body.clone_for_update().syntax()); + } None => { let name = format!("\"{}\"", annotated_name); let args = make::arg_list(Some(make::expr_literal(&name).into())); @@ -258,7 +272,7 @@ mod tests { use super::*; #[test] - fn add_custom_impl_debug() { + fn add_custom_impl_debug_record_struct() { check_assist( replace_derive_with_manual_impl, r#" @@ -295,6 +309,80 @@ impl fmt::Debug for Foo { f.debug_struct("Foo").field("bar", &self.bar).finish() } } +"#, + ) + } + #[test] + 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; + } +} + +#[derive(Debu$0g)] +struct Foo(String, usize); +"#, + 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(String, usize); + +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() + } +} +"#, + ) + } + #[test] + fn add_custom_impl_debug_empty_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; + } +} + +#[derive(Debu$0g)] +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 fmt::Debug for Foo { + $0fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Foo").finish() + } +} "#, ) } From a1f2c7adcd926fdbe6b5ce10dcdc059acc147b3e Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Aug 2021 14:11:47 +0200 Subject: [PATCH 05/15] rename variables According to https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/dev/style.md#variable-naming --- .../handlers/replace_derive_with_manual_impl.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 3ad1c46878c..e0319adccc6 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 @@ -170,18 +170,18 @@ fn impl_def_from_trait( let (impl_def, first_assoc_item) = add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope); - if let ast::AssocItem::Fn(fn_) = &first_assoc_item { + if let ast::AssocItem::Fn(func) = &first_assoc_item { if trait_path.segment().unwrap().name_ref().unwrap().text() == "Debug" { - gen_debug_impl(adt, fn_, annotated_name); + gen_debug_impl(adt, func, annotated_name); } } Some((impl_def, first_assoc_item)) } -fn gen_debug_impl(adt: &ast::Adt, fn_: &ast::Fn, annotated_name: &ast::Name) { +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(_) => {} // TODO + ast::Adt::Enum(enum_) => {} // TODO ast::Adt::Struct(strukt) => match strukt.field_list() { Some(ast::FieldList::RecordFieldList(field_list)) => { let name = format!("\"{}\"", annotated_name); @@ -200,7 +200,7 @@ fn gen_debug_impl(adt: &ast::Adt, fn_: &ast::Fn, annotated_name: &ast::Name) { } 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(fn_.body().unwrap().syntax(), body.clone_for_update().syntax()); + ted::replace(func.body().unwrap().syntax(), body.clone_for_update().syntax()); } Some(ast::FieldList::TupleFieldList(field_list)) => { let name = format!("\"{}\"", annotated_name); @@ -216,7 +216,7 @@ fn gen_debug_impl(adt: &ast::Adt, fn_: &ast::Fn, annotated_name: &ast::Name) { } 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(fn_.body().unwrap().syntax(), body.clone_for_update().syntax()); + ted::replace(func.body().unwrap().syntax(), body.clone_for_update().syntax()); } None => { let name = format!("\"{}\"", annotated_name); @@ -225,7 +225,7 @@ fn gen_debug_impl(adt: &ast::Adt, fn_: &ast::Fn, annotated_name: &ast::Name) { let expr = make::expr_method_call(target, "debug_struct", args); 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(fn_.body().unwrap().syntax(), body.clone_for_update().syntax()); + ted::replace(func.body().unwrap().syntax(), body.clone_for_update().syntax()); } }, } From 4b7ae9fedc2154a31f04400babb1dd0a93dffd16 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Aug 2021 15:24:04 +0200 Subject: [PATCH 06/15] generate Debug for enums --- .../replace_derive_with_manual_impl.rs | 78 ++++++++++++++++++- crates/syntax/src/ast/make.rs | 3 + 2 files changed, 80 insertions(+), 1 deletion(-) 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 e0319adccc6..8c3a5d25d9b 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 @@ -181,7 +181,37 @@ fn impl_def_from_trait( 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_) => {} // TODO + ast::Adt::Enum(enum_) => { + if let Some(list) = enum_.variant_list() { + let mut arms = vec![]; + for variant in list.variants() { + let name = variant.name().unwrap(); + + // => Self:: + let first = make::ext::ident_path("Self"); + let second = make::ext::ident_path(&format!("{}", name)); + let pat = make::path_pat(make::path_concat(first, second)); + + // => write!(f, "") + 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 target = make::expr_path(make::ext::ident_path("write")); + let expr = make::expr_macro_call(target, args); + + // => Self:: => write!(f, ""), + arms.push(make::match_arm(Some(pat.into()), None, expr.into())); + } + + // => match self { ... } + let f_path = make::expr_path(make::ext::ident_path("self")); + let list = make::match_arm_list(arms); + let expr = make::expr_match(f_path, list); + + let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); + ted::replace(func.body().unwrap().syntax(), body.clone_for_update().syntax()); + } + } ast::Adt::Struct(strukt) => match strukt.field_list() { Some(ast::FieldList::RecordFieldList(field_list)) => { let name = format!("\"{}\"", annotated_name); @@ -383,6 +413,52 @@ impl fmt::Debug for Foo { f.debug_struct("Foo").finish() } } +"#, + ) + } + #[test] + fn add_custom_impl_debug_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; + } +} + +#[derive(Debu$0g)] +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 fmt::Debug for Foo { + $0fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Bar => write!(f, "Bar"), + Self::Baz => write!(f, "Baz"), + } + } +} "#, ) } diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 71254f08543..87faac0aa32 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -311,6 +311,9 @@ pub fn expr_method_call( ) -> ast::Expr { expr_from_text(&format!("{}.{}{}", receiver, method, arg_list)) } +pub fn expr_macro_call(f: ast::Expr, arg_list: ast::ArgList) -> ast::Expr { + expr_from_text(&format!("{}!{}", f, arg_list)) +} pub fn expr_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr { expr_from_text(&if exclusive { format!("&mut {}", expr) } else { format!("&{}", expr) }) } From cc3ff1b486edf123e2dad17f5225ce761c3b5711 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Aug 2021 15:32:11 +0200 Subject: [PATCH 07/15] Fix enum debug indent level --- .../src/handlers/replace_derive_with_manual_impl.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 8c3a5d25d9b..7d2e30fe209 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 @@ -205,7 +205,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) { // => match self { ... } let f_path = make::expr_path(make::ext::ident_path("self")); - let list = make::match_arm_list(arms); + let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); let expr = make::expr_match(f_path, list); let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); @@ -454,8 +454,8 @@ enum Foo { impl fmt::Debug for Foo { $0fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Bar => write!(f, "Bar"), - Self::Baz => write!(f, "Baz"), + Self::Bar => write!(f, "Bar"), + Self::Baz => write!(f, "Baz"), } } } From 720508a2df928f4973e6ef594652be22aebcdfcb Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Aug 2021 15:41:32 +0200 Subject: [PATCH 08/15] dedup struct debug impl code --- .../replace_derive_with_manual_impl.rs | 80 ++++++++----------- 1 file changed, 35 insertions(+), 45 deletions(-) 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 7d2e30fe209..7358444f170 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 @@ -178,6 +178,7 @@ fn impl_def_from_trait( 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. @@ -185,9 +186,8 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) { if let Some(list) = enum_.variant_list() { let mut arms = vec![]; for variant in list.variants() { - let name = variant.name().unwrap(); - // => Self:: + let name = variant.name().unwrap(); let first = make::ext::ident_path("Self"); let second = make::ext::ident_path(&format!("{}", name)); let pat = make::path_pat(make::path_concat(first, second)); @@ -199,7 +199,6 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) { let target = make::expr_path(make::ext::ident_path("write")); let expr = make::expr_macro_call(target, args); - // => Self:: => write!(f, ""), arms.push(make::match_arm(Some(pat.into()), None, expr.into())); } @@ -212,52 +211,43 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) { ted::replace(func.body().unwrap().syntax(), body.clone_for_update().syntax()); } } - ast::Adt::Struct(strukt) => match strukt.field_list() { - Some(ast::FieldList::RecordFieldList(field_list)) => { - 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 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(); + 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 => make::expr_method_call(target, "debug_struct", args), + Some(ast::FieldList::RecordFieldList(field_list)) => { + 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)) => { + 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!("{}", name)).into(); - let args = make::arg_list(vec![f_name, f_path]); - expr = make::expr_method_call(expr, "field", args); + 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 } - 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(ast::FieldList::TupleFieldList(field_list)) => { - 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 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(); - let args = make::arg_list(Some(f_path)); - expr = make::expr_method_call(expr, "field", args); - } - 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()); - } - None => { - 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 = make::expr_method_call(target, "debug_struct", args); - 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()); - } - }, + }; + + 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()); + } } } From a2e5fc659d59df45d37685876394cc261801baaa Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Aug 2021 15:56:26 +0200 Subject: [PATCH 09/15] Improve naming and add comments --- .../replace_derive_with_manual_impl.rs | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) 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 7358444f170..71d5331e280 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 @@ -129,9 +129,6 @@ fn add_assist( let mut cursor = Cursor::Before(first_assoc_item.syntax()); let placeholder; if let ast::AssocItem::Fn(ref func) = first_assoc_item { - // need to know what kind of derive this is: if it's Derive Debug, special case it. - // the name of the struct - // list of fields of the struct if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) { if m.syntax().text() == "todo!()" { @@ -170,10 +167,12 @@ fn impl_def_from_trait( let (impl_def, first_assoc_item) = 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 { - if trait_path.segment().unwrap().name_ref().unwrap().text() == "Debug" { - gen_debug_impl(adt, func, annotated_name); - } + match trait_path.segment().unwrap().name_ref().unwrap().text().as_str() { + "Debug" => gen_debug_impl(adt, func, annotated_name), + _ => {} // => If we don't know about the trait, the function body is left as `todo!`. + }; } Some((impl_def, first_assoc_item)) } @@ -183,31 +182,31 @@ 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() { - // => Self:: let name = variant.name().unwrap(); - let first = make::ext::ident_path("Self"); - let second = make::ext::ident_path(&format!("{}", name)); - let pat = make::path_pat(make::path_concat(first, second)); - // => write!(f, "") + 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 target = make::expr_path(make::ext::ident_path("write")); - let expr = make::expr_macro_call(target, args); + 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(pat.into()), None, expr.into())); + arms.push(make::match_arm(Some(variant_name.into()), None, macro_call.into())); } - // => match self { ... } - let f_path = make::expr_path(make::ext::ident_path("self")); + let match_target = make::expr_path(make::ext::ident_path("self")); let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); - let expr = make::expr_match(f_path, list); + let match_expr = make::expr_match(match_target, list); - let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); + 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()); } } @@ -217,8 +216,12 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) { let target = make::expr_path(make::ext::ident_path("f")); let expr = match strukt.field_list() { - None => make::expr_method_call(target, "debug_struct", args), + 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() { @@ -233,6 +236,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) { 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")); From e26ba723332499a9b1fb074b6a8a93bb2f56dbd7 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Aug 2021 16:24:14 +0200 Subject: [PATCH 10/15] Update replace_derive_with_manual_impl.rs --- .../ide_assists/src/handlers/replace_derive_with_manual_impl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 71d5331e280..c76c517dcca 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 @@ -171,7 +171,7 @@ fn impl_def_from_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), - _ => {} // => If we don't know about the trait, the function body is left as `todo!`. + _ => {} }; } Some((impl_def, first_assoc_item)) From dcbf385ffc4a47acf4a6fe54ea2e24025beaac9e Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Aug 2021 16:31:28 +0200 Subject: [PATCH 11/15] use make::name_ref --- .../handlers/replace_derive_with_manual_impl.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) 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 c76c517dcca..c842d3aa11e 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 @@ -218,11 +218,12 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) { let expr = match strukt.field_list() { None => { // => f.debug_struct("Name").finish() - make::expr_method_call(target, "debug_struct", args) + make::expr_method_call(target, make::name_ref("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); + let method = make::name_ref("debug_struct"); + let mut expr = make::expr_method_call(target, method, args); for field in field_list.fields() { if let Some(name) = field.name() { let f_name = make::expr_literal(&(format!("\"{}\"", name))).into(); @@ -230,25 +231,28 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) { 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 = make::expr_method_call(expr, make::name_ref("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); + let method = make::name_ref("debug_tuple"); + let mut expr = make::expr_method_call(target, method, 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))); + let method = make::name_ref("field"); + expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path))); } expr } }; - let expr = make::expr_method_call(expr, "finish", make::arg_list(None)); + let method = make::name_ref("finish"); + let expr = make::expr_method_call(expr, method, 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()); } From 5a3c3a029c9e83c9690704723bd256e9efcfc8f6 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Aug 2021 18:44:54 +0200 Subject: [PATCH 12/15] exclude files from tidy check --- crates/rust-analyzer/tests/slow-tests/tidy.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/rust-analyzer/tests/slow-tests/tidy.rs b/crates/rust-analyzer/tests/slow-tests/tidy.rs index eacf6b55b6c..4c3ff31533f 100644 --- a/crates/rust-analyzer/tests/slow-tests/tidy.rs +++ b/crates/rust-analyzer/tests/slow-tests/tidy.rs @@ -274,11 +274,13 @@ fn check_todo(path: &Path, text: &str) { "handlers/add_turbo_fish.rs", "handlers/generate_function.rs", "handlers/fill_match_arms.rs", + "handlers/replace_derive_with_manual_impl.rs", // To support generating `todo!()` in assists, we have `expr_todo()` in // `ast::make`. "ast/make.rs", // The documentation in string literals may contain anything for its own purposes "ide_db/src/helpers/generated_lints.rs", + "ide_assists/src/tests/generated.rs", ]; if need_todo.iter().any(|p| path.ends_with(p)) { return; From f64aacc0c1d3743f08a3be41562e4aab46f47871 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Aug 2021 18:58:42 +0200 Subject: [PATCH 13/15] Use minicore --- crates/ide/src/hover.rs | 4 +- .../replace_derive_with_manual_impl.rs | 95 +++---------------- crates/test_utils/src/minicore.rs | 12 +++ 3 files changed, 27 insertions(+), 84 deletions(-) diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 6789abfb0a7..a9e2b526394 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -2732,8 +2732,8 @@ fn foo() { file_id: FileId( 1, ), - full_range: 251..433, - focus_range: 290..296, + full_range: 252..434, + focus_range: 291..297, name: "Future", kind: Trait, description: "pub trait Future", 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 c842d3aa11e..009014396ca 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 @@ -304,36 +304,19 @@ mod tests { 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() } } @@ -345,32 +328,14 @@ impl fmt::Debug for Foo { 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#" -mod fmt { - pub struct Error; - pub type Result = Result<(), Error>; - pub struct Formatter<'a>; - pub trait Debug { - fn fmt(&self, f: &mut Formatter<'_>) -> Result; - } -} + r#"struct Foo(String, usize); -struct Foo(String, usize); - -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_tuple("Foo").field(&self.0).field(&self.1).finish() } } @@ -382,32 +347,15 @@ impl fmt::Debug for Foo { 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; "#, 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 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").finish() } } @@ -419,15 +367,7 @@ impl fmt::Debug for Foo { 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, @@ -435,22 +375,13 @@ enum 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; - } -} - enum Foo { Bar, Baz, } -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 { match self { Self::Bar => write!(f, "Bar"), Self::Baz => write!(f, "Baz"), diff --git a/crates/test_utils/src/minicore.rs b/crates/test_utils/src/minicore.rs index 2532738562b..eeadf2bee2e 100644 --- a/crates/test_utils/src/minicore.rs +++ b/crates/test_utils/src/minicore.rs @@ -31,6 +31,7 @@ //! eq: sized //! ord: eq, option //! derive: +//! fmt: pub mod marker { // region:sized @@ -334,6 +335,17 @@ pub mod cmp { } // endregion:eq +// region:fmt +pub mod fmt { + pub struct Error; + pub type Result = Result<(), Error>; + pub struct Formatter<'a>; + pub trait Debug { + fn fmt(&self, f: &mut Formatter<'_>) -> Result; + } +} +// endregion:fmt + // region:slice pub mod slice { #[lang = "slice"] From f424cd9c7e00509e6c5d077120f8bd20b19c5b51 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 9 Aug 2021 00:00:09 +0200 Subject: [PATCH 14/15] Update crates/test_utils/src/minicore.rs Co-authored-by: Lukas Wirth --- crates/test_utils/src/minicore.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/test_utils/src/minicore.rs b/crates/test_utils/src/minicore.rs index eeadf2bee2e..c37a0aa1efb 100644 --- a/crates/test_utils/src/minicore.rs +++ b/crates/test_utils/src/minicore.rs @@ -31,7 +31,7 @@ //! eq: sized //! ord: eq, option //! derive: -//! fmt: +//! fmt: result pub mod marker { // region:sized From 59cdb51ef3865582588f2735dc5479e9c205ddb6 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 9 Aug 2021 00:29:38 +0200 Subject: [PATCH 15/15] Remove unwraps --- .../replace_derive_with_manual_impl.rs | 111 ++++++++++-------- 1 file changed, 64 insertions(+), 47 deletions(-) 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 009014396ca..9da25b926c5 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 @@ -168,76 +168,92 @@ 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), - _ => {} - }; - } + if let ast::AssocItem::Fn(ref func) = first_assoc_item { + let _ = gen_default_impl(func, trait_path, adt, annotated_name); + }; + Some((impl_def, first_assoc_item)) } +/// Generate custom trait bodies where possible. +/// +/// Returns `Option` so that we can use `?` rather than `if let Some`. Returning +/// `None` means that generating a custom trait body failed, and the body will remain +/// as `todo!` instead. +fn gen_default_impl( + func: &ast::Fn, + trait_path: &ast::Path, + adt: &ast::Adt, + annotated_name: &ast::Name, +) -> Option<()> { + match trait_path.segment()?.name_ref()?.text().as_str() { + "Debug" => gen_debug_impl(adt, func, annotated_name), + _ => Some(()), + } +} + /// 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) { +fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) -> Option<()> { match adt { - ast::Adt::Union(_) => {} // `Debug` cannot be derived for unions, so no default impl can be provided. + // `Debug` cannot be derived for unions, so no default impl can be provided. + ast::Adt::Union(_) => Some(()), + + // => match self { Self::Variant => write!(f, "Variant") } 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 list = enum_.variant_list()?; + let mut arms = vec![]; + for variant in list.variants() { + let name = variant.name()?; + 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 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); - 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()); + 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()?.syntax(), body.clone_for_update().syntax()); + Some(()) } + 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, make::name_ref("debug_struct"), args) - } + // => f.debug_struct("Name").finish() + None => make::expr_method_call(target, make::name_ref("debug_struct"), args), + + // => f.debug_struct("Name").field("foo", &self.foo).finish() Some(ast::FieldList::RecordFieldList(field_list)) => { - // => f.debug_struct("Name").field("foo", &self.foo).finish() let method = make::name_ref("debug_struct"); let mut expr = make::expr_method_call(target, method, 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, make::name_ref("field"), args); - } + let 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, make::name_ref("field"), args); } expr } + + // => f.debug_tuple("Name").field(self.0).finish() Some(ast::FieldList::TupleFieldList(field_list)) => { - // => f.debug_tuple("Name").field(self.0).finish() let method = make::name_ref("debug_tuple"); let mut expr = make::expr_method_call(target, method, args); for (idx, _) in field_list.fields().enumerate() { @@ -254,7 +270,8 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) { let method = make::name_ref("finish"); let expr = make::expr_method_call(expr, method, 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()); + ted::replace(func.body()?.syntax(), body.clone_for_update().syntax()); + Some(()) } } }