Skip to content

Commit 423860c

Browse files
committed
Add default body when implementing Default by hand
1 parent 5664a2b commit 423860c

File tree

2 files changed

+99
-2
lines changed

2 files changed

+99
-2
lines changed

crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs

Lines changed: 91 additions & 2 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,14 +180,15 @@ 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+
"Default" => gen_default_impl(adt, func),
191192
_ => Some(()),
192193
}
193194
}
@@ -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+
match adt {
283+
// `Debug` cannot be derived for unions, so no default impl can be provided.
284+
ast::Adt::Union(_) => Some(()),
285+
// Deriving `Debug` for enums is not stable yet.
286+
ast::Adt::Enum(_) => Some(()),
287+
ast::Adt::Struct(strukt) => {
288+
let expr = match strukt.field_list() {
289+
Some(ast::FieldList::RecordFieldList(field_list)) => {
290+
let mut fields = vec![];
291+
for field in field_list.fields() {
292+
let trait_name = make::ext::ident_path("Default");
293+
let method_name = make::ext::ident_path("default");
294+
let fn_name = make::expr_path(make::path_concat(trait_name, method_name));
295+
let method_call = make::expr_call(fn_name, make::arg_list(None));
296+
let name_ref = make::name_ref(&field.name()?.to_string());
297+
let field = make::record_expr_field(name_ref, Some(method_call));
298+
fields.push(field);
299+
}
300+
let struct_name = make::ext::ident_path("Self");
301+
let fields = make::record_expr_field_list(fields);
302+
make::record_expr(struct_name, fields).into()
303+
}
304+
Some(ast::FieldList::TupleFieldList(field_list)) => {
305+
let mut fields = vec![];
306+
for _ in field_list.fields() {
307+
let trait_name = make::ext::ident_path("Default");
308+
let method_name = make::ext::ident_path("default");
309+
let fn_name = make::expr_path(make::path_concat(trait_name, method_name));
310+
let method_call = make::expr_call(fn_name, make::arg_list(None));
311+
fields.push(method_call);
312+
}
313+
let struct_name = make::expr_path(make::ext::ident_path("Self"));
314+
make::expr_call(struct_name, make::arg_list(fields))
315+
}
316+
None => todo!(),
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,50 @@ 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+
}
408497
"#,
409498
)
410499
}

crates/test_utils/src/minicore.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,14 @@ pub mod fmt {
346346
}
347347
// endregion:fmt
348348

349+
// region:default
350+
pub mod default {
351+
pub trait Default {
352+
fn default() -> Self;
353+
}
354+
}
355+
// endregion:default
356+
349357
// region:slice
350358
pub mod slice {
351359
#[lang = "slice"]

0 commit comments

Comments
 (0)