Skip to content

Commit 7ddc26a

Browse files
committed
add clone generation for structs and bare enums
1 parent 7d7a50d commit 7ddc26a

File tree

2 files changed

+97
-9
lines changed

2 files changed

+97
-9
lines changed

crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -467,10 +467,7 @@ struct Foo {
467467
468468
impl Clone for Foo {
469469
$0fn clone(&self) -> Self {
470-
Self {
471-
bin: self.bin.clone(),
472-
bar: self.bar.clone(),
473-
}
470+
Self { bin: self.bin.clone(), bar: self.bar.clone() }
474471
}
475472
}
476473
"#,
@@ -498,6 +495,27 @@ impl Clone for Foo {
498495
)
499496
}
500497

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+
501519
#[test]
502520
fn add_custom_impl_clone_enum() {
503521
check_assist(
@@ -536,7 +554,7 @@ impl Clone for Foo {
536554
//- minicore: clone
537555
#[derive(Clo$0ne)]
538556
enum Foo {
539-
Bar,
557+
Bar(String),
540558
Baz,
541559
}
542560
"#,

crates/ide_assists/src/utils/gen_trait_fn_body.rs

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,83 @@ 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+
let pattern = make::path_pat(variant_name.clone());
46+
let variant_expr = make::expr_path(variant_name);
47+
arms.push(make::match_arm(Some(pattern.into()), None, variant_expr));
48+
}
49+
50+
let match_target = make::expr_path(make::ext::ident_path("self"));
51+
let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
52+
make::expr_match(match_target, list)
53+
}
54+
ast::Adt::Struct(strukt) => {
55+
match strukt.field_list() {
56+
// => Self { name: self.name.clone() }
57+
Some(ast::FieldList::RecordFieldList(field_list)) => {
58+
let mut fields = vec![];
59+
for field in field_list.fields() {
60+
let base = make::expr_path(make::ext::ident_path("self"));
61+
let target = make::expr_field(base, &field.name()?.to_string());
62+
let method_call = gen_clone_call(target);
63+
let name_ref = make::name_ref(&field.name()?.to_string());
64+
let field = make::record_expr_field(name_ref, Some(method_call));
65+
fields.push(field);
66+
}
67+
let struct_name = make::ext::ident_path("Self");
68+
let fields = make::record_expr_field_list(fields);
69+
make::record_expr(struct_name, fields).into()
70+
}
71+
// => Self(self.0.clone(), self.1.clone())
72+
Some(ast::FieldList::TupleFieldList(field_list)) => {
73+
let mut fields = vec![];
74+
for (i, _) in field_list.fields().enumerate() {
75+
let f_path = make::expr_path(make::ext::ident_path("self"));
76+
let target = make::expr_field(f_path, &format!("{}", i)).into();
77+
fields.push(gen_clone_call(target));
78+
}
79+
let struct_name = make::expr_path(make::ext::ident_path("Self"));
80+
make::expr_call(struct_name, make::arg_list(fields))
81+
}
82+
// => Self { }
83+
None => {
84+
let struct_name = make::ext::ident_path("Self");
85+
let fields = make::record_expr_field_list(None);
86+
make::record_expr(struct_name, fields).into()
87+
}
88+
}
89+
}
90+
};
91+
let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
92+
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
93+
Some(())
94+
}
95+
2696
/// Generate a `Debug` impl based on the fields and members of the target type.
2797
fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
2898
let annotated_name = adt.name()?;
@@ -88,10 +158,10 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
88158
Some(ast::FieldList::TupleFieldList(field_list)) => {
89159
let method = make::name_ref("debug_tuple");
90160
let mut expr = make::expr_method_call(target, method, args);
91-
for (idx, _) in field_list.fields().enumerate() {
161+
for (i, _) in field_list.fields().enumerate() {
92162
let f_path = make::expr_path(make::ext::ident_path("self"));
93163
let f_path = make::expr_ref(f_path, false);
94-
let f_path = make::expr_field(f_path, &format!("{}", idx)).into();
164+
let f_path = make::expr_field(f_path, &format!("{}", i)).into();
95165
let method = make::name_ref("field");
96166
expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path)));
97167
}
@@ -182,7 +252,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
182252
make::block_expr(Some(stmt), None).indent(ast::edit::IndentLevel(1))
183253
}
184254
ast::Adt::Struct(strukt) => match strukt.field_list() {
185-
// => self.<field>.hash(state);*
255+
// => self.<field>.hash(state);
186256
Some(ast::FieldList::RecordFieldList(field_list)) => {
187257
let mut stmts = vec![];
188258
for field in field_list.fields() {
@@ -193,7 +263,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
193263
make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1))
194264
}
195265

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

0 commit comments

Comments
 (0)