Skip to content

Commit e1dcec0

Browse files
Merge #9846
9846: feat: Generate default trait fn impl when generating `Clone` r=Veykril a=yoshuawuyts Implements a default trait function body when generating the `Clone` trait for a type. Thanks! r? `@\veykril` Co-authored-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
2 parents f6ef043 + 2f86697 commit e1dcec0

File tree

2 files changed

+281
-4
lines changed

2 files changed

+281
-4
lines changed

crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,169 @@ impl core::hash::Hash for Foo {
443443
core::mem::discriminant(self).hash(state);
444444
}
445445
}
446+
"#,
447+
)
448+
}
449+
450+
#[test]
451+
fn add_custom_impl_clone_record_struct() {
452+
check_assist(
453+
replace_derive_with_manual_impl,
454+
r#"
455+
//- minicore: clone
456+
#[derive(Clo$0ne)]
457+
struct Foo {
458+
bin: usize,
459+
bar: usize,
460+
}
461+
"#,
462+
r#"
463+
struct Foo {
464+
bin: usize,
465+
bar: usize,
466+
}
467+
468+
impl Clone for Foo {
469+
$0fn clone(&self) -> Self {
470+
Self { bin: self.bin.clone(), bar: self.bar.clone() }
471+
}
472+
}
473+
"#,
474+
)
475+
}
476+
477+
#[test]
478+
fn add_custom_impl_clone_tuple_struct() {
479+
check_assist(
480+
replace_derive_with_manual_impl,
481+
r#"
482+
//- minicore: clone
483+
#[derive(Clo$0ne)]
484+
struct Foo(usize, usize);
485+
"#,
486+
r#"
487+
struct Foo(usize, usize);
488+
489+
impl Clone for Foo {
490+
$0fn clone(&self) -> Self {
491+
Self(self.0.clone(), self.1.clone())
492+
}
493+
}
494+
"#,
495+
)
496+
}
497+
498+
#[test]
499+
fn add_custom_impl_clone_empty_struct() {
500+
check_assist(
501+
replace_derive_with_manual_impl,
502+
r#"
503+
//- minicore: clone
504+
#[derive(Clo$0ne)]
505+
struct Foo;
506+
"#,
507+
r#"
508+
struct Foo;
509+
510+
impl Clone for Foo {
511+
$0fn clone(&self) -> Self {
512+
Self { }
513+
}
514+
}
515+
"#,
516+
)
517+
}
518+
519+
#[test]
520+
fn add_custom_impl_clone_enum() {
521+
check_assist(
522+
replace_derive_with_manual_impl,
523+
r#"
524+
//- minicore: clone
525+
#[derive(Clo$0ne)]
526+
enum Foo {
527+
Bar,
528+
Baz,
529+
}
530+
"#,
531+
r#"
532+
enum Foo {
533+
Bar,
534+
Baz,
535+
}
536+
537+
impl Clone for Foo {
538+
$0fn clone(&self) -> Self {
539+
match self {
540+
Self::Bar => Self::Bar,
541+
Self::Baz => Self::Baz,
542+
}
543+
}
544+
}
545+
"#,
546+
)
547+
}
548+
549+
#[test]
550+
fn add_custom_impl_clone_tuple_enum() {
551+
check_assist(
552+
replace_derive_with_manual_impl,
553+
r#"
554+
//- minicore: clone
555+
#[derive(Clo$0ne)]
556+
enum Foo {
557+
Bar(String),
558+
Baz,
559+
}
560+
"#,
561+
r#"
562+
enum Foo {
563+
Bar(String),
564+
Baz,
565+
}
566+
567+
impl Clone for Foo {
568+
$0fn clone(&self) -> Self {
569+
match self {
570+
Self::Bar(arg0) => Self::Bar(arg0.clone()),
571+
Self::Baz => Self::Baz,
572+
}
573+
}
574+
}
575+
"#,
576+
)
577+
}
578+
579+
#[test]
580+
fn add_custom_impl_clone_record_enum() {
581+
check_assist(
582+
replace_derive_with_manual_impl,
583+
r#"
584+
//- minicore: clone
585+
#[derive(Clo$0ne)]
586+
enum Foo {
587+
Bar {
588+
bin: String,
589+
},
590+
Baz,
591+
}
592+
"#,
593+
r#"
594+
enum Foo {
595+
Bar {
596+
bin: String,
597+
},
598+
Baz,
599+
}
600+
601+
impl Clone for Foo {
602+
$0fn clone(&self) -> Self {
603+
match self {
604+
Self::Bar { bin } => Self::Bar { bin: bin.clone() },
605+
Self::Baz => Self::Baz,
606+
}
607+
}
608+
}
446609
"#,
447610
)
448611
}

crates/ide_assists/src/utils/gen_trait_fn_body.rs

Lines changed: 118 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,127 @@ pub(crate) fn gen_trait_fn_body(
1616
adt: &ast::Adt,
1717
) -> Option<()> {
1818
match trait_path.segment()?.name_ref()?.text().as_str() {
19+
"Clone" => gen_clone_impl(adt, func),
1920
"Debug" => gen_debug_impl(adt, func),
2021
"Default" => gen_default_impl(adt, func),
2122
"Hash" => gen_hash_impl(adt, func),
2223
_ => None,
2324
}
2425
}
2526

27+
/// Generate a `Clone` impl based on the fields and members of the target type.
28+
fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
29+
fn gen_clone_call(target: ast::Expr) -> ast::Expr {
30+
let method = make::name_ref("clone");
31+
make::expr_method_call(target, method, make::arg_list(None))
32+
}
33+
let expr = match adt {
34+
// `Clone` cannot be derived for unions, so no default impl can be provided.
35+
ast::Adt::Union(_) => return None,
36+
ast::Adt::Enum(enum_) => {
37+
let list = enum_.variant_list()?;
38+
let mut arms = vec![];
39+
for variant in list.variants() {
40+
let name = variant.name()?;
41+
let left = make::ext::ident_path("Self");
42+
let right = make::ext::ident_path(&format!("{}", name));
43+
let variant_name = make::path_concat(left, right);
44+
45+
match variant.field_list() {
46+
// => match self { Self::Name { x } => Self::Name { x: x.clone() } }
47+
Some(ast::FieldList::RecordFieldList(list)) => {
48+
let mut pats = vec![];
49+
let mut fields = vec![];
50+
for field in list.fields() {
51+
let field_name = field.name()?;
52+
let pat = make::ident_pat(false, false, field_name.clone());
53+
pats.push(pat.into());
54+
55+
let path = make::ext::ident_path(&field_name.to_string());
56+
let method_call = gen_clone_call(make::expr_path(path));
57+
let name_ref = make::name_ref(&field_name.to_string());
58+
let field = make::record_expr_field(name_ref, Some(method_call));
59+
fields.push(field);
60+
}
61+
let pat = make::record_pat(variant_name.clone(), pats.into_iter());
62+
let fields = make::record_expr_field_list(fields);
63+
let record_expr = make::record_expr(variant_name, fields).into();
64+
arms.push(make::match_arm(Some(pat.into()), None, record_expr));
65+
}
66+
67+
// => match self { Self::Name(arg1) => Self::Name(arg1.clone()) }
68+
Some(ast::FieldList::TupleFieldList(list)) => {
69+
let mut pats = vec![];
70+
let mut fields = vec![];
71+
for (i, _) in list.fields().enumerate() {
72+
let field_name = format!("arg{}", i);
73+
let pat = make::ident_pat(false, false, make::name(&field_name));
74+
pats.push(pat.into());
75+
76+
let f_path = make::expr_path(make::ext::ident_path(&field_name));
77+
fields.push(gen_clone_call(f_path));
78+
}
79+
let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter());
80+
let struct_name = make::expr_path(variant_name);
81+
let tuple_expr = make::expr_call(struct_name, make::arg_list(fields));
82+
arms.push(make::match_arm(Some(pat.into()), None, tuple_expr));
83+
}
84+
85+
// => match self { Self::Name => Self::Name }
86+
None => {
87+
let pattern = make::path_pat(variant_name.clone());
88+
let variant_expr = make::expr_path(variant_name);
89+
arms.push(make::match_arm(Some(pattern.into()), None, variant_expr));
90+
}
91+
}
92+
}
93+
94+
let match_target = make::expr_path(make::ext::ident_path("self"));
95+
let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
96+
make::expr_match(match_target, list)
97+
}
98+
ast::Adt::Struct(strukt) => {
99+
match strukt.field_list() {
100+
// => Self { name: self.name.clone() }
101+
Some(ast::FieldList::RecordFieldList(field_list)) => {
102+
let mut fields = vec![];
103+
for field in field_list.fields() {
104+
let base = make::expr_path(make::ext::ident_path("self"));
105+
let target = make::expr_field(base, &field.name()?.to_string());
106+
let method_call = gen_clone_call(target);
107+
let name_ref = make::name_ref(&field.name()?.to_string());
108+
let field = make::record_expr_field(name_ref, Some(method_call));
109+
fields.push(field);
110+
}
111+
let struct_name = make::ext::ident_path("Self");
112+
let fields = make::record_expr_field_list(fields);
113+
make::record_expr(struct_name, fields).into()
114+
}
115+
// => Self(self.0.clone(), self.1.clone())
116+
Some(ast::FieldList::TupleFieldList(field_list)) => {
117+
let mut fields = vec![];
118+
for (i, _) in field_list.fields().enumerate() {
119+
let f_path = make::expr_path(make::ext::ident_path("self"));
120+
let target = make::expr_field(f_path, &format!("{}", i)).into();
121+
fields.push(gen_clone_call(target));
122+
}
123+
let struct_name = make::expr_path(make::ext::ident_path("Self"));
124+
make::expr_call(struct_name, make::arg_list(fields))
125+
}
126+
// => Self { }
127+
None => {
128+
let struct_name = make::ext::ident_path("Self");
129+
let fields = make::record_expr_field_list(None);
130+
make::record_expr(struct_name, fields).into()
131+
}
132+
}
133+
}
134+
};
135+
let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
136+
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
137+
Some(())
138+
}
139+
26140
/// Generate a `Debug` impl based on the fields and members of the target type.
27141
fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
28142
let annotated_name = adt.name()?;
@@ -88,10 +202,10 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
88202
Some(ast::FieldList::TupleFieldList(field_list)) => {
89203
let method = make::name_ref("debug_tuple");
90204
let mut expr = make::expr_method_call(target, method, args);
91-
for (idx, _) in field_list.fields().enumerate() {
205+
for (i, _) in field_list.fields().enumerate() {
92206
let f_path = make::expr_path(make::ext::ident_path("self"));
93207
let f_path = make::expr_ref(f_path, false);
94-
let f_path = make::expr_field(f_path, &format!("{}", idx)).into();
208+
let f_path = make::expr_field(f_path, &format!("{}", i)).into();
95209
let method = make::name_ref("field");
96210
expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path)));
97211
}
@@ -182,7 +296,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
182296
make::block_expr(Some(stmt), None).indent(ast::edit::IndentLevel(1))
183297
}
184298
ast::Adt::Struct(strukt) => match strukt.field_list() {
185-
// => self.<field>.hash(state);*
299+
// => self.<field>.hash(state);
186300
Some(ast::FieldList::RecordFieldList(field_list)) => {
187301
let mut stmts = vec![];
188302
for field in field_list.fields() {
@@ -193,7 +307,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
193307
make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1))
194308
}
195309

196-
// => self.<field_index>.hash(state);*
310+
// => self.<field_index>.hash(state);
197311
Some(ast::FieldList::TupleFieldList(field_list)) => {
198312
let mut stmts = vec![];
199313
for (i, _) in field_list.fields().enumerate() {

0 commit comments

Comments
 (0)