Skip to content

Commit 63427af

Browse files
Merge #9825
9825: Generate default impl when converting #[derive(Default)] to manual impl r=Veykril a=yoshuawuyts Similar to #9814, but for `#[derive(Default)]`. Thanks! ## Follow-up steps I've added the tests inside `handlers/replace_derive_with_manual_impl.rs` again, but I'm planning a follow-up PR to extract these to `utils/` so we can share them between assists - and maybe even add another assist just for the purpose of testing these impls (e.g. `generate_default_trait_body`). The step after _that_ is likely to fill out the remaining traits, so we can make it so whenever RA auto-completes a trait which also can be derived, we provide a default function body. Co-authored-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2 parents b2b2425 + 13749e7 commit 63427af

File tree

1 file changed

+113
-4
lines changed

1 file changed

+113
-4
lines changed

crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs

Lines changed: 113 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ fn impl_def_from_trait(
169169

170170
// Generate a default `impl` function body for the derived trait.
171171
if let ast::AssocItem::Fn(ref func) = first_assoc_item {
172-
let _ = gen_default_impl(func, trait_path, adt, annotated_name);
172+
let _ = gen_trait_body_impl(func, trait_path, adt, annotated_name);
173173
};
174174

175175
Some((impl_def, first_assoc_item))
@@ -180,23 +180,24 @@ fn impl_def_from_trait(
180180
/// Returns `Option` so that we can use `?` rather than `if let Some`. Returning
181181
/// `None` means that generating a custom trait body failed, and the body will remain
182182
/// as `todo!` instead.
183-
fn gen_default_impl(
183+
fn gen_trait_body_impl(
184184
func: &ast::Fn,
185185
trait_path: &ast::Path,
186186
adt: &ast::Adt,
187187
annotated_name: &ast::Name,
188188
) -> Option<()> {
189189
match trait_path.segment()?.name_ref()?.text().as_str() {
190190
"Debug" => gen_debug_impl(adt, func, annotated_name),
191-
_ => Some(()),
191+
"Default" => gen_default_impl(adt, func),
192+
_ => None,
192193
}
193194
}
194195

195196
/// Generate a `Debug` impl based on the fields and members of the target type.
196197
fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) -> Option<()> {
197198
match adt {
198199
// `Debug` cannot be derived for unions, so no default impl can be provided.
199-
ast::Adt::Union(_) => Some(()),
200+
ast::Adt::Union(_) => None,
200201

201202
// => match self { Self::Variant => write!(f, "Variant") }
202203
ast::Adt::Enum(enum_) => {
@@ -276,6 +277,50 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) ->
276277
}
277278
}
278279

280+
/// Generate a `Debug` impl based on the fields and members of the target type.
281+
fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
282+
fn gen_default_call() -> ast::Expr {
283+
let trait_name = make::ext::ident_path("Default");
284+
let method_name = make::ext::ident_path("default");
285+
let fn_name = make::expr_path(make::path_concat(trait_name, method_name));
286+
make::expr_call(fn_name, make::arg_list(None))
287+
}
288+
match adt {
289+
// `Debug` cannot be derived for unions, so no default impl can be provided.
290+
ast::Adt::Union(_) => None,
291+
// Deriving `Debug` for enums is not stable yet.
292+
ast::Adt::Enum(_) => None,
293+
ast::Adt::Struct(strukt) => {
294+
let expr = match strukt.field_list() {
295+
Some(ast::FieldList::RecordFieldList(field_list)) => {
296+
let mut fields = vec![];
297+
for field in field_list.fields() {
298+
let method_call = gen_default_call();
299+
let name_ref = make::name_ref(&field.name()?.to_string());
300+
let field = make::record_expr_field(name_ref, Some(method_call));
301+
fields.push(field);
302+
}
303+
let struct_name = make::ext::ident_path("Self");
304+
let fields = make::record_expr_field_list(fields);
305+
make::record_expr(struct_name, fields).into()
306+
}
307+
Some(ast::FieldList::TupleFieldList(field_list)) => {
308+
let struct_name = make::expr_path(make::ext::ident_path("Self"));
309+
let fields = field_list.fields().map(|_| gen_default_call());
310+
make::expr_call(struct_name, make::arg_list(fields))
311+
}
312+
None => {
313+
let struct_name = make::ext::ident_path("Self");
314+
let fields = make::record_expr_field_list(None);
315+
make::record_expr(struct_name, fields).into()
316+
}
317+
};
318+
let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
319+
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
320+
Some(())
321+
}
322+
}
323+
}
279324
fn update_attribute(
280325
builder: &mut AssistBuilder,
281326
input: &ast::TokenTree,
@@ -405,6 +450,70 @@ impl core::fmt::Debug for Foo {
405450
}
406451
}
407452
}
453+
"#,
454+
)
455+
}
456+
#[test]
457+
fn add_custom_impl_default_record_struct() {
458+
check_assist(
459+
replace_derive_with_manual_impl,
460+
r#"
461+
//- minicore: default
462+
#[derive(Defau$0lt)]
463+
struct Foo {
464+
foo: usize,
465+
}
466+
"#,
467+
r#"
468+
struct Foo {
469+
foo: usize,
470+
}
471+
472+
impl Default for Foo {
473+
$0fn default() -> Self {
474+
Self { foo: Default::default() }
475+
}
476+
}
477+
"#,
478+
)
479+
}
480+
#[test]
481+
fn add_custom_impl_default_tuple_struct() {
482+
check_assist(
483+
replace_derive_with_manual_impl,
484+
r#"
485+
//- minicore: default
486+
#[derive(Defau$0lt)]
487+
struct Foo(usize);
488+
"#,
489+
r#"
490+
struct Foo(usize);
491+
492+
impl Default for Foo {
493+
$0fn default() -> Self {
494+
Self(Default::default())
495+
}
496+
}
497+
"#,
498+
)
499+
}
500+
#[test]
501+
fn add_custom_impl_default_empty_struct() {
502+
check_assist(
503+
replace_derive_with_manual_impl,
504+
r#"
505+
//- minicore: default
506+
#[derive(Defau$0lt)]
507+
struct Foo;
508+
"#,
509+
r#"
510+
struct Foo;
511+
512+
impl Default for Foo {
513+
$0fn default() -> Self {
514+
Self { }
515+
}
516+
}
408517
"#,
409518
)
410519
}

0 commit comments

Comments
 (0)