Skip to content

Commit 4d2e25a

Browse files
committed
move trait body gen code to utils
1 parent 8e4fb4f commit 4d2e25a

File tree

3 files changed

+154
-149
lines changed

3 files changed

+154
-149
lines changed

crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs

Lines changed: 2 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ use hir::ModuleDef;
22
use ide_db::helpers::{import_assets::NameToImport, mod_path_to_ast};
33
use ide_db::items_locator;
44
use itertools::Itertools;
5-
use syntax::ast::edit::AstNodeEdit;
6-
use syntax::ted;
75
use syntax::{
86
ast::{self, make, AstNode, NameOwner},
97
SyntaxKind::{IDENT, WHITESPACE},
108
};
119

10+
use crate::utils::gen_trait_body;
1211
use crate::{
1312
assist_context::{AssistBuilder, AssistContext, Assists},
1413
utils::{
@@ -169,158 +168,12 @@ fn impl_def_from_trait(
169168

170169
// Generate a default `impl` function body for the derived trait.
171170
if let ast::AssocItem::Fn(ref func) = first_assoc_item {
172-
let _ = gen_trait_body_impl(func, trait_path, adt, annotated_name);
171+
let _ = gen_trait_body(func, trait_path, adt, annotated_name);
173172
};
174173

175174
Some((impl_def, first_assoc_item))
176175
}
177176

178-
/// Generate custom trait bodies where possible.
179-
///
180-
/// Returns `Option` so that we can use `?` rather than `if let Some`. Returning
181-
/// `None` means that generating a custom trait body failed, and the body will remain
182-
/// as `todo!` instead.
183-
fn gen_trait_body_impl(
184-
func: &ast::Fn,
185-
trait_path: &ast::Path,
186-
adt: &ast::Adt,
187-
annotated_name: &ast::Name,
188-
) -> Option<()> {
189-
match trait_path.segment()?.name_ref()?.text().as_str() {
190-
"Debug" => gen_debug_impl(adt, func, annotated_name),
191-
"Default" => gen_default_impl(adt, func),
192-
_ => None,
193-
}
194-
}
195-
196-
/// Generate a `Debug` impl based on the fields and members of the target type.
197-
fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) -> Option<()> {
198-
match adt {
199-
// `Debug` cannot be derived for unions, so no default impl can be provided.
200-
ast::Adt::Union(_) => None,
201-
202-
// => match self { Self::Variant => write!(f, "Variant") }
203-
ast::Adt::Enum(enum_) => {
204-
let list = enum_.variant_list()?;
205-
let mut arms = vec![];
206-
for variant in list.variants() {
207-
let name = variant.name()?;
208-
let left = make::ext::ident_path("Self");
209-
let right = make::ext::ident_path(&format!("{}", name));
210-
let variant_name = make::path_pat(make::path_concat(left, right));
211-
212-
let target = make::expr_path(make::ext::ident_path("f").into());
213-
let fmt_string = make::expr_literal(&(format!("\"{}\"", name))).into();
214-
let args = make::arg_list(vec![target, fmt_string]);
215-
let macro_name = make::expr_path(make::ext::ident_path("write"));
216-
let macro_call = make::expr_macro_call(macro_name, args);
217-
218-
arms.push(make::match_arm(Some(variant_name.into()), None, macro_call.into()));
219-
}
220-
221-
let match_target = make::expr_path(make::ext::ident_path("self"));
222-
let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
223-
let match_expr = make::expr_match(match_target, list);
224-
225-
let body = make::block_expr(None, Some(match_expr));
226-
let body = body.indent(ast::edit::IndentLevel(1));
227-
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
228-
Some(())
229-
}
230-
231-
ast::Adt::Struct(strukt) => {
232-
let name = format!("\"{}\"", annotated_name);
233-
let args = make::arg_list(Some(make::expr_literal(&name).into()));
234-
let target = make::expr_path(make::ext::ident_path("f"));
235-
236-
let expr = match strukt.field_list() {
237-
// => f.debug_struct("Name").finish()
238-
None => make::expr_method_call(target, make::name_ref("debug_struct"), args),
239-
240-
// => f.debug_struct("Name").field("foo", &self.foo).finish()
241-
Some(ast::FieldList::RecordFieldList(field_list)) => {
242-
let method = make::name_ref("debug_struct");
243-
let mut expr = make::expr_method_call(target, method, args);
244-
for field in field_list.fields() {
245-
let name = field.name()?;
246-
let f_name = make::expr_literal(&(format!("\"{}\"", name))).into();
247-
let f_path = make::expr_path(make::ext::ident_path("self"));
248-
let f_path = make::expr_ref(f_path, false);
249-
let f_path = make::expr_field(f_path, &format!("{}", name)).into();
250-
let args = make::arg_list(vec![f_name, f_path]);
251-
expr = make::expr_method_call(expr, make::name_ref("field"), args);
252-
}
253-
expr
254-
}
255-
256-
// => f.debug_tuple("Name").field(self.0).finish()
257-
Some(ast::FieldList::TupleFieldList(field_list)) => {
258-
let method = make::name_ref("debug_tuple");
259-
let mut expr = make::expr_method_call(target, method, args);
260-
for (idx, _) in field_list.fields().enumerate() {
261-
let f_path = make::expr_path(make::ext::ident_path("self"));
262-
let f_path = make::expr_ref(f_path, false);
263-
let f_path = make::expr_field(f_path, &format!("{}", idx)).into();
264-
let method = make::name_ref("field");
265-
expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path)));
266-
}
267-
expr
268-
}
269-
};
270-
271-
let method = make::name_ref("finish");
272-
let expr = make::expr_method_call(expr, method, make::arg_list(None));
273-
let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
274-
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
275-
Some(())
276-
}
277-
}
278-
}
279-
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-
}
324177
fn update_attribute(
325178
builder: &mut AssistBuilder,
326179
input: &ast::TokenTree,

crates/ide_assists/src/utils.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Assorted functions shared by several assists.
22
33
pub(crate) mod suggest_name;
4+
mod gen_trait_body;
45

56
use std::ops;
67

@@ -25,6 +26,8 @@ use syntax::{
2526

2627
use crate::assist_context::{AssistBuilder, AssistContext};
2728

29+
pub(crate) use gen_trait_body::gen_trait_body;
30+
2831
pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr {
2932
extract_trivial_expression(&block)
3033
.filter(|expr| !expr.syntax().text().contains_char('\n'))
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
use syntax::ast::{self, edit::AstNodeEdit, make, AstNode, NameOwner};
2+
use syntax::ted;
3+
4+
/// Generate custom trait bodies where possible.
5+
///
6+
/// Returns `Option` so that we can use `?` rather than `if let Some`. Returning
7+
/// `None` means that generating a custom trait body failed, and the body will remain
8+
/// as `todo!` instead.
9+
pub(crate) fn gen_trait_body(
10+
func: &ast::Fn,
11+
trait_path: &ast::Path,
12+
adt: &ast::Adt,
13+
annotated_name: &ast::Name,
14+
) -> Option<()> {
15+
match trait_path.segment()?.name_ref()?.text().as_str() {
16+
"Debug" => gen_debug_impl(adt, func, annotated_name),
17+
"Default" => gen_default_impl(adt, func),
18+
_ => None,
19+
}
20+
}
21+
22+
/// Generate a `Debug` impl based on the fields and members of the target type.
23+
fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn, annotated_name: &ast::Name) -> Option<()> {
24+
match adt {
25+
// `Debug` cannot be derived for unions, so no default impl can be provided.
26+
ast::Adt::Union(_) => None,
27+
28+
// => match self { Self::Variant => write!(f, "Variant") }
29+
ast::Adt::Enum(enum_) => {
30+
let list = enum_.variant_list()?;
31+
let mut arms = vec![];
32+
for variant in list.variants() {
33+
let name = variant.name()?;
34+
let left = make::ext::ident_path("Self");
35+
let right = make::ext::ident_path(&format!("{}", name));
36+
let variant_name = make::path_pat(make::path_concat(left, right));
37+
38+
let target = make::expr_path(make::ext::ident_path("f").into());
39+
let fmt_string = make::expr_literal(&(format!("\"{}\"", name))).into();
40+
let args = make::arg_list(vec![target, fmt_string]);
41+
let macro_name = make::expr_path(make::ext::ident_path("write"));
42+
let macro_call = make::expr_macro_call(macro_name, args);
43+
44+
arms.push(make::match_arm(Some(variant_name.into()), None, macro_call.into()));
45+
}
46+
47+
let match_target = make::expr_path(make::ext::ident_path("self"));
48+
let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
49+
let match_expr = make::expr_match(match_target, list);
50+
51+
let body = make::block_expr(None, Some(match_expr));
52+
let body = body.indent(ast::edit::IndentLevel(1));
53+
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
54+
Some(())
55+
}
56+
57+
ast::Adt::Struct(strukt) => {
58+
let name = format!("\"{}\"", annotated_name);
59+
let args = make::arg_list(Some(make::expr_literal(&name).into()));
60+
let target = make::expr_path(make::ext::ident_path("f"));
61+
62+
let expr = match strukt.field_list() {
63+
// => f.debug_struct("Name").finish()
64+
None => make::expr_method_call(target, make::name_ref("debug_struct"), args),
65+
66+
// => f.debug_struct("Name").field("foo", &self.foo).finish()
67+
Some(ast::FieldList::RecordFieldList(field_list)) => {
68+
let method = make::name_ref("debug_struct");
69+
let mut expr = make::expr_method_call(target, method, args);
70+
for field in field_list.fields() {
71+
let name = field.name()?;
72+
let f_name = make::expr_literal(&(format!("\"{}\"", name))).into();
73+
let f_path = make::expr_path(make::ext::ident_path("self"));
74+
let f_path = make::expr_ref(f_path, false);
75+
let f_path = make::expr_field(f_path, &format!("{}", name)).into();
76+
let args = make::arg_list(vec![f_name, f_path]);
77+
expr = make::expr_method_call(expr, make::name_ref("field"), args);
78+
}
79+
expr
80+
}
81+
82+
// => f.debug_tuple("Name").field(self.0).finish()
83+
Some(ast::FieldList::TupleFieldList(field_list)) => {
84+
let method = make::name_ref("debug_tuple");
85+
let mut expr = make::expr_method_call(target, method, args);
86+
for (idx, _) in field_list.fields().enumerate() {
87+
let f_path = make::expr_path(make::ext::ident_path("self"));
88+
let f_path = make::expr_ref(f_path, false);
89+
let f_path = make::expr_field(f_path, &format!("{}", idx)).into();
90+
let method = make::name_ref("field");
91+
expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path)));
92+
}
93+
expr
94+
}
95+
};
96+
97+
let method = make::name_ref("finish");
98+
let expr = make::expr_method_call(expr, method, make::arg_list(None));
99+
let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
100+
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
101+
Some(())
102+
}
103+
}
104+
}
105+
106+
/// Generate a `Debug` impl based on the fields and members of the target type.
107+
fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
108+
fn gen_default_call() -> ast::Expr {
109+
let trait_name = make::ext::ident_path("Default");
110+
let method_name = make::ext::ident_path("default");
111+
let fn_name = make::expr_path(make::path_concat(trait_name, method_name));
112+
make::expr_call(fn_name, make::arg_list(None))
113+
}
114+
match adt {
115+
// `Debug` cannot be derived for unions, so no default impl can be provided.
116+
ast::Adt::Union(_) => None,
117+
// Deriving `Debug` for enums is not stable yet.
118+
ast::Adt::Enum(_) => None,
119+
ast::Adt::Struct(strukt) => {
120+
let expr = match strukt.field_list() {
121+
Some(ast::FieldList::RecordFieldList(field_list)) => {
122+
let mut fields = vec![];
123+
for field in field_list.fields() {
124+
let method_call = gen_default_call();
125+
let name_ref = make::name_ref(&field.name()?.to_string());
126+
let field = make::record_expr_field(name_ref, Some(method_call));
127+
fields.push(field);
128+
}
129+
let struct_name = make::ext::ident_path("Self");
130+
let fields = make::record_expr_field_list(fields);
131+
make::record_expr(struct_name, fields).into()
132+
}
133+
Some(ast::FieldList::TupleFieldList(field_list)) => {
134+
let struct_name = make::expr_path(make::ext::ident_path("Self"));
135+
let fields = field_list.fields().map(|_| gen_default_call());
136+
make::expr_call(struct_name, make::arg_list(fields))
137+
}
138+
None => {
139+
let struct_name = make::ext::ident_path("Self");
140+
let fields = make::record_expr_field_list(None);
141+
make::record_expr(struct_name, fields).into()
142+
}
143+
};
144+
let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
145+
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
146+
Some(())
147+
}
148+
}
149+
}

0 commit comments

Comments
 (0)