]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/utils/gen_trait_fn_body.rs
c883e6fb11ba947b04f9e5dcb332a28f173520be
[rust.git] / crates / ide_assists / src / utils / gen_trait_fn_body.rs
1 //! This module contains functions to generate default trait impl function bodies where possible.
2
3 use syntax::{
4     ast::{self, edit::AstNodeEdit, make, AstNode, BinaryOp, CmpOp, HasName, LogicOp},
5     ted,
6 };
7
8 /// Generate custom trait bodies where possible.
9 ///
10 /// Returns `Option` so that we can use `?` rather than `if let Some`. Returning
11 /// `None` means that generating a custom trait body failed, and the body will remain
12 /// as `todo!` instead.
13 pub(crate) fn gen_trait_fn_body(
14     func: &ast::Fn,
15     trait_path: &ast::Path,
16     adt: &ast::Adt,
17 ) -> Option<()> {
18     match trait_path.segment()?.name_ref()?.text().as_str() {
19         "Clone" => gen_clone_impl(adt, func),
20         "Debug" => gen_debug_impl(adt, func),
21         "Default" => gen_default_impl(adt, func),
22         "Hash" => gen_hash_impl(adt, func),
23         "PartialEq" => gen_partial_eq(adt, func),
24         "PartialOrd" => gen_partial_ord(adt, func),
25         _ => None,
26     }
27 }
28
29 /// Generate a `Clone` impl based on the fields and members of the target type.
30 fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
31     fn gen_clone_call(target: ast::Expr) -> ast::Expr {
32         let method = make::name_ref("clone");
33         make::expr_method_call(target, method, make::arg_list(None))
34     }
35     let expr = match adt {
36         // `Clone` cannot be derived for unions, so no default impl can be provided.
37         ast::Adt::Union(_) => return None,
38         ast::Adt::Enum(enum_) => {
39             let list = enum_.variant_list()?;
40             let mut arms = vec![];
41             for variant in list.variants() {
42                 let name = variant.name()?;
43                 let variant_name = make::ext::path_from_idents(["Self", &format!("{}", name)])?;
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
140 /// Generate a `Debug` impl based on the fields and members of the target type.
141 fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
142     let annotated_name = adt.name()?;
143     match adt {
144         // `Debug` cannot be derived for unions, so no default impl can be provided.
145         ast::Adt::Union(_) => None,
146
147         // => match self { Self::Variant => write!(f, "Variant") }
148         ast::Adt::Enum(enum_) => {
149             let list = enum_.variant_list()?;
150             let mut arms = vec![];
151             for variant in list.variants() {
152                 let name = variant.name()?;
153                 let variant_name = make::ext::path_from_idents(["Self", &format!("{}", name)])?;
154                 let target = make::expr_path(make::ext::ident_path("f").into());
155
156                 match variant.field_list() {
157                     Some(ast::FieldList::RecordFieldList(list)) => {
158                         // => f.debug_struct(name)
159                         let target = make::expr_path(make::ext::ident_path("f"));
160                         let method = make::name_ref("debug_struct");
161                         let struct_name = format!("\"{}\"", name);
162                         let args = make::arg_list(Some(make::expr_literal(&struct_name).into()));
163                         let mut expr = make::expr_method_call(target, method, args);
164
165                         let mut pats = vec![];
166                         for field in list.fields() {
167                             let field_name = field.name()?;
168
169                             // create a field pattern for use in `MyStruct { fields.. }`
170                             let pat = make::ident_pat(false, false, field_name.clone());
171                             pats.push(pat.into());
172
173                             // => <expr>.field("field_name", field)
174                             let method_name = make::name_ref("field");
175                             let name = make::expr_literal(&(format!("\"{}\"", field_name))).into();
176                             let path = &format!("{}", field_name);
177                             let path = make::expr_path(make::ext::ident_path(path));
178                             let args = make::arg_list(vec![name, path]);
179                             expr = make::expr_method_call(expr, method_name, args);
180                         }
181
182                         // => <expr>.finish()
183                         let method = make::name_ref("finish");
184                         let expr = make::expr_method_call(expr, method, make::arg_list(None));
185
186                         // => MyStruct { fields.. } => f.debug_struct("MyStruct")...finish(),
187                         let pat = make::record_pat(variant_name.clone(), pats.into_iter());
188                         arms.push(make::match_arm(Some(pat.into()), None, expr));
189                     }
190                     Some(ast::FieldList::TupleFieldList(list)) => {
191                         // => f.debug_tuple(name)
192                         let target = make::expr_path(make::ext::ident_path("f"));
193                         let method = make::name_ref("debug_tuple");
194                         let struct_name = format!("\"{}\"", name);
195                         let args = make::arg_list(Some(make::expr_literal(&struct_name).into()));
196                         let mut expr = make::expr_method_call(target, method, args);
197
198                         let mut pats = vec![];
199                         for (i, _) in list.fields().enumerate() {
200                             let name = format!("arg{}", i);
201
202                             // create a field pattern for use in `MyStruct(fields..)`
203                             let field_name = make::name(&name);
204                             let pat = make::ident_pat(false, false, field_name.clone());
205                             pats.push(pat.into());
206
207                             // => <expr>.field(field)
208                             let method_name = make::name_ref("field");
209                             let field_path = &format!("{}", name);
210                             let field_path = make::expr_path(make::ext::ident_path(field_path));
211                             let args = make::arg_list(vec![field_path]);
212                             expr = make::expr_method_call(expr, method_name, args);
213                         }
214
215                         // => <expr>.finish()
216                         let method = make::name_ref("finish");
217                         let expr = make::expr_method_call(expr, method, make::arg_list(None));
218
219                         // => MyStruct (fields..) => f.debug_tuple("MyStruct")...finish(),
220                         let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter());
221                         arms.push(make::match_arm(Some(pat.into()), None, expr));
222                     }
223                     None => {
224                         let fmt_string = make::expr_literal(&(format!("\"{}\"", name))).into();
225                         let args = make::arg_list([target, fmt_string]);
226                         let macro_name = make::expr_path(make::ext::ident_path("write"));
227                         let macro_call = make::expr_macro_call(macro_name, args);
228
229                         let variant_name = make::path_pat(variant_name);
230                         arms.push(make::match_arm(
231                             Some(variant_name.into()),
232                             None,
233                             macro_call.into(),
234                         ));
235                     }
236                 }
237             }
238
239             let match_target = make::expr_path(make::ext::ident_path("self"));
240             let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
241             let match_expr = make::expr_match(match_target, list);
242
243             let body = make::block_expr(None, Some(match_expr));
244             let body = body.indent(ast::edit::IndentLevel(1));
245             ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
246             Some(())
247         }
248
249         ast::Adt::Struct(strukt) => {
250             let name = format!("\"{}\"", annotated_name);
251             let args = make::arg_list(Some(make::expr_literal(&name).into()));
252             let target = make::expr_path(make::ext::ident_path("f"));
253
254             let expr = match strukt.field_list() {
255                 // => f.debug_struct("Name").finish()
256                 None => make::expr_method_call(target, make::name_ref("debug_struct"), args),
257
258                 // => f.debug_struct("Name").field("foo", &self.foo).finish()
259                 Some(ast::FieldList::RecordFieldList(field_list)) => {
260                     let method = make::name_ref("debug_struct");
261                     let mut expr = make::expr_method_call(target, method, args);
262                     for field in field_list.fields() {
263                         let name = field.name()?;
264                         let f_name = make::expr_literal(&(format!("\"{}\"", name))).into();
265                         let f_path = make::expr_path(make::ext::ident_path("self"));
266                         let f_path = make::expr_ref(f_path, false);
267                         let f_path = make::expr_field(f_path, &format!("{}", name)).into();
268                         let args = make::arg_list([f_name, f_path]);
269                         expr = make::expr_method_call(expr, make::name_ref("field"), args);
270                     }
271                     expr
272                 }
273
274                 // => f.debug_tuple("Name").field(self.0).finish()
275                 Some(ast::FieldList::TupleFieldList(field_list)) => {
276                     let method = make::name_ref("debug_tuple");
277                     let mut expr = make::expr_method_call(target, method, args);
278                     for (i, _) in field_list.fields().enumerate() {
279                         let f_path = make::expr_path(make::ext::ident_path("self"));
280                         let f_path = make::expr_ref(f_path, false);
281                         let f_path = make::expr_field(f_path, &format!("{}", i)).into();
282                         let method = make::name_ref("field");
283                         expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path)));
284                     }
285                     expr
286                 }
287             };
288
289             let method = make::name_ref("finish");
290             let expr = make::expr_method_call(expr, method, make::arg_list(None));
291             let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
292             ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
293             Some(())
294         }
295     }
296 }
297
298 /// Generate a `Debug` impl based on the fields and members of the target type.
299 fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
300     fn gen_default_call() -> Option<ast::Expr> {
301         let fn_name = make::ext::path_from_idents(["Default", "default"])?;
302         Some(make::expr_call(make::expr_path(fn_name), make::arg_list(None)))
303     }
304     match adt {
305         // `Debug` cannot be derived for unions, so no default impl can be provided.
306         ast::Adt::Union(_) => None,
307         // Deriving `Debug` for enums is not stable yet.
308         ast::Adt::Enum(_) => None,
309         ast::Adt::Struct(strukt) => {
310             let expr = match strukt.field_list() {
311                 Some(ast::FieldList::RecordFieldList(field_list)) => {
312                     let mut fields = vec![];
313                     for field in field_list.fields() {
314                         let method_call = gen_default_call()?;
315                         let name_ref = make::name_ref(&field.name()?.to_string());
316                         let field = make::record_expr_field(name_ref, Some(method_call));
317                         fields.push(field);
318                     }
319                     let struct_name = make::ext::ident_path("Self");
320                     let fields = make::record_expr_field_list(fields);
321                     make::record_expr(struct_name, fields).into()
322                 }
323                 Some(ast::FieldList::TupleFieldList(field_list)) => {
324                     let struct_name = make::expr_path(make::ext::ident_path("Self"));
325                     let fields = field_list
326                         .fields()
327                         .map(|_| gen_default_call())
328                         .collect::<Option<Vec<ast::Expr>>>()?;
329                     make::expr_call(struct_name, make::arg_list(fields))
330                 }
331                 None => {
332                     let struct_name = make::ext::ident_path("Self");
333                     let fields = make::record_expr_field_list(None);
334                     make::record_expr(struct_name, fields).into()
335                 }
336             };
337             let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
338             ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
339             Some(())
340         }
341     }
342 }
343
344 /// Generate a `Hash` impl based on the fields and members of the target type.
345 fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
346     fn gen_hash_call(target: ast::Expr) -> ast::Stmt {
347         let method = make::name_ref("hash");
348         let arg = make::expr_path(make::ext::ident_path("state"));
349         let expr = make::expr_method_call(target, method, make::arg_list(Some(arg)));
350         make::expr_stmt(expr).into()
351     }
352
353     let body = match adt {
354         // `Hash` cannot be derived for unions, so no default impl can be provided.
355         ast::Adt::Union(_) => return None,
356
357         // => std::mem::discriminant(self).hash(state);
358         ast::Adt::Enum(_) => {
359             let fn_name = make_discriminant()?;
360
361             let arg = make::expr_path(make::ext::ident_path("self"));
362             let fn_call = make::expr_call(fn_name, make::arg_list(Some(arg)));
363             let stmt = gen_hash_call(fn_call);
364
365             make::block_expr(Some(stmt), None).indent(ast::edit::IndentLevel(1))
366         }
367         ast::Adt::Struct(strukt) => match strukt.field_list() {
368             // => self.<field>.hash(state);
369             Some(ast::FieldList::RecordFieldList(field_list)) => {
370                 let mut stmts = vec![];
371                 for field in field_list.fields() {
372                     let base = make::expr_path(make::ext::ident_path("self"));
373                     let target = make::expr_field(base, &field.name()?.to_string());
374                     stmts.push(gen_hash_call(target));
375                 }
376                 make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1))
377             }
378
379             // => self.<field_index>.hash(state);
380             Some(ast::FieldList::TupleFieldList(field_list)) => {
381                 let mut stmts = vec![];
382                 for (i, _) in field_list.fields().enumerate() {
383                     let base = make::expr_path(make::ext::ident_path("self"));
384                     let target = make::expr_field(base, &format!("{}", i));
385                     stmts.push(gen_hash_call(target));
386                 }
387                 make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1))
388             }
389
390             // No fields in the body means there's nothing to hash.
391             None => return None,
392         },
393     };
394
395     ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
396     Some(())
397 }
398
399 /// Generate a `PartialEq` impl based on the fields and members of the target type.
400 fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
401     fn gen_eq_chain(expr: Option<ast::Expr>, cmp: ast::Expr) -> Option<ast::Expr> {
402         match expr {
403             Some(expr) => Some(make::expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)),
404             None => Some(cmp),
405         }
406     }
407
408     fn gen_record_pat_field(field_name: &str, pat_name: &str) -> ast::RecordPatField {
409         let pat = make::ext::simple_ident_pat(make::name(&pat_name));
410         let name_ref = make::name_ref(field_name);
411         make::record_pat_field(name_ref, pat.into())
412     }
413
414     fn gen_record_pat(record_name: ast::Path, fields: Vec<ast::RecordPatField>) -> ast::RecordPat {
415         let list = make::record_pat_field_list(fields);
416         make::record_pat_with_fields(record_name, list)
417     }
418
419     fn gen_variant_path(variant: &ast::Variant) -> Option<ast::Path> {
420         make::ext::path_from_idents(["Self", &variant.name()?.to_string()])
421     }
422
423     fn gen_tuple_field(field_name: &String) -> ast::Pat {
424         ast::Pat::IdentPat(make::ident_pat(false, false, make::name(field_name)))
425     }
426
427     // FIXME: return `None` if the trait carries a generic type; we can only
428     // generate this code `Self` for the time being.
429
430     let body = match adt {
431         // `Hash` cannot be derived for unions, so no default impl can be provided.
432         ast::Adt::Union(_) => return None,
433
434         ast::Adt::Enum(enum_) => {
435             // => std::mem::discriminant(self) == std::mem::discriminant(other)
436             let lhs_name = make::expr_path(make::ext::ident_path("self"));
437             let lhs = make::expr_call(make_discriminant()?, make::arg_list(Some(lhs_name.clone())));
438             let rhs_name = make::expr_path(make::ext::ident_path("other"));
439             let rhs = make::expr_call(make_discriminant()?, make::arg_list(Some(rhs_name.clone())));
440             let eq_check =
441                 make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
442
443             let mut n_cases = 0;
444             let mut arms = vec![];
445             for variant in enum_.variant_list()?.variants() {
446                 n_cases += 1;
447                 match variant.field_list() {
448                     // => (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin,
449                     Some(ast::FieldList::RecordFieldList(list)) => {
450                         let mut expr = None;
451                         let mut l_fields = vec![];
452                         let mut r_fields = vec![];
453
454                         for field in list.fields() {
455                             let field_name = field.name()?.to_string();
456
457                             let l_name = &format!("l_{}", field_name);
458                             l_fields.push(gen_record_pat_field(&field_name, &l_name));
459
460                             let r_name = &format!("r_{}", field_name);
461                             r_fields.push(gen_record_pat_field(&field_name, &r_name));
462
463                             let lhs = make::expr_path(make::ext::ident_path(l_name));
464                             let rhs = make::expr_path(make::ext::ident_path(r_name));
465                             let cmp = make::expr_bin_op(
466                                 lhs,
467                                 BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
468                                 rhs,
469                             );
470                             expr = gen_eq_chain(expr, cmp);
471                         }
472
473                         let left = gen_record_pat(gen_variant_path(&variant)?, l_fields);
474                         let right = gen_record_pat(gen_variant_path(&variant)?, r_fields);
475                         let tuple = make::tuple_pat(vec![left.into(), right.into()]);
476
477                         if let Some(expr) = expr {
478                             arms.push(make::match_arm(Some(tuple.into()), None, expr));
479                         }
480                     }
481
482                     Some(ast::FieldList::TupleFieldList(list)) => {
483                         let mut expr = None;
484                         let mut l_fields = vec![];
485                         let mut r_fields = vec![];
486
487                         for (i, _) in list.fields().enumerate() {
488                             let field_name = format!("{}", i);
489
490                             let l_name = format!("l{}", field_name);
491                             l_fields.push(gen_tuple_field(&l_name));
492
493                             let r_name = format!("r{}", field_name);
494                             r_fields.push(gen_tuple_field(&r_name));
495
496                             let lhs = make::expr_path(make::ext::ident_path(&l_name));
497                             let rhs = make::expr_path(make::ext::ident_path(&r_name));
498                             let cmp = make::expr_bin_op(
499                                 lhs,
500                                 BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
501                                 rhs,
502                             );
503                             expr = gen_eq_chain(expr, cmp);
504                         }
505
506                         let left = make::tuple_struct_pat(gen_variant_path(&variant)?, l_fields);
507                         let right = make::tuple_struct_pat(gen_variant_path(&variant)?, r_fields);
508                         let tuple = make::tuple_pat(vec![left.into(), right.into()]);
509
510                         if let Some(expr) = expr {
511                             arms.push(make::match_arm(Some(tuple.into()), None, expr));
512                         }
513                     }
514                     None => continue,
515                 }
516             }
517
518             let expr = match arms.len() {
519                 0 => eq_check,
520                 _ => {
521                     if n_cases > arms.len() {
522                         let lhs = make::wildcard_pat().into();
523                         arms.push(make::match_arm(Some(lhs), None, eq_check));
524                     }
525
526                     let match_target = make::expr_tuple(vec![lhs_name, rhs_name]);
527                     let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
528                     make::expr_match(match_target, list)
529                 }
530             };
531
532             make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
533         }
534         ast::Adt::Struct(strukt) => match strukt.field_list() {
535             Some(ast::FieldList::RecordFieldList(field_list)) => {
536                 let mut expr = None;
537                 for field in field_list.fields() {
538                     let lhs = make::expr_path(make::ext::ident_path("self"));
539                     let lhs = make::expr_field(lhs, &field.name()?.to_string());
540                     let rhs = make::expr_path(make::ext::ident_path("other"));
541                     let rhs = make::expr_field(rhs, &field.name()?.to_string());
542                     let cmp =
543                         make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
544                     expr = gen_eq_chain(expr, cmp);
545                 }
546                 make::block_expr(None, expr).indent(ast::edit::IndentLevel(1))
547             }
548
549             Some(ast::FieldList::TupleFieldList(field_list)) => {
550                 let mut expr = None;
551                 for (i, _) in field_list.fields().enumerate() {
552                     let idx = format!("{}", i);
553                     let lhs = make::expr_path(make::ext::ident_path("self"));
554                     let lhs = make::expr_field(lhs, &idx);
555                     let rhs = make::expr_path(make::ext::ident_path("other"));
556                     let rhs = make::expr_field(rhs, &idx);
557                     let cmp =
558                         make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
559                     expr = gen_eq_chain(expr, cmp);
560                 }
561                 make::block_expr(None, expr).indent(ast::edit::IndentLevel(1))
562             }
563
564             // No fields in the body means there's nothing to hash.
565             None => {
566                 let expr = make::expr_literal("true").into();
567                 make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
568             }
569         },
570     };
571
572     ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
573     Some(())
574 }
575
576 fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
577     fn gen_partial_cmp_call(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
578         let method = make::name_ref("partial_cmp");
579         make::expr_method_call(lhs, method, make::arg_list(Some(rhs)))
580     }
581     fn gen_partial_cmp_call2(mut lhs: Vec<ast::Expr>, mut rhs: Vec<ast::Expr>) -> ast::Expr {
582         let (lhs, rhs) = match (lhs.len(), rhs.len()) {
583             (1, 1) => (lhs.pop().unwrap(), rhs.pop().unwrap()),
584             _ => (make::expr_tuple(lhs.into_iter()), make::expr_tuple(rhs.into_iter())),
585         };
586         let method = make::name_ref("partial_cmp");
587         make::expr_method_call(lhs, method, make::arg_list(Some(rhs)))
588     }
589
590     fn gen_record_pat_field(field_name: &str, pat_name: &str) -> ast::RecordPatField {
591         let pat = make::ext::simple_ident_pat(make::name(&pat_name));
592         let name_ref = make::name_ref(field_name);
593         make::record_pat_field(name_ref, pat.into())
594     }
595
596     fn gen_record_pat(record_name: ast::Path, fields: Vec<ast::RecordPatField>) -> ast::RecordPat {
597         let list = make::record_pat_field_list(fields);
598         make::record_pat_with_fields(record_name, list)
599     }
600
601     fn gen_variant_path(variant: &ast::Variant) -> Option<ast::Path> {
602         make::ext::path_from_idents(["Self", &variant.name()?.to_string()])
603     }
604
605     fn gen_tuple_field(field_name: &String) -> ast::Pat {
606         ast::Pat::IdentPat(make::ident_pat(false, false, make::name(field_name)))
607     }
608
609     // FIXME: return `None` if the trait carries a generic type; we can only
610     // generate this code `Self` for the time being.
611
612     let body = match adt {
613         // `Hash` cannot be derived for unions, so no default impl can be provided.
614         ast::Adt::Union(_) => return None,
615
616         ast::Adt::Enum(enum_) => {
617             // => std::mem::discriminant(self) == std::mem::discriminant(other)
618             let lhs_name = make::expr_path(make::ext::ident_path("self"));
619             let lhs = make::expr_call(make_discriminant()?, make::arg_list(Some(lhs_name.clone())));
620             let rhs_name = make::expr_path(make::ext::ident_path("other"));
621             let rhs = make::expr_call(make_discriminant()?, make::arg_list(Some(rhs_name.clone())));
622             let ord_check = gen_partial_cmp_call(lhs, rhs);
623
624             let mut case_count = 0;
625             let mut arms = vec![];
626             for variant in enum_.variant_list()?.variants() {
627                 case_count += 1;
628                 match variant.field_list() {
629                     // => (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin,
630                     Some(ast::FieldList::RecordFieldList(list)) => {
631                         let mut l_pat_fields = vec![];
632                         let mut r_pat_fields = vec![];
633                         let mut l_fields = vec![];
634                         let mut r_fields = vec![];
635
636                         for field in list.fields() {
637                             let field_name = field.name()?.to_string();
638
639                             let l_name = &format!("l_{}", field_name);
640                             l_pat_fields.push(gen_record_pat_field(&field_name, &l_name));
641
642                             let r_name = &format!("r_{}", field_name);
643                             r_pat_fields.push(gen_record_pat_field(&field_name, &r_name));
644
645                             let lhs = make::expr_path(make::ext::ident_path(l_name));
646                             let rhs = make::expr_path(make::ext::ident_path(r_name));
647                             l_fields.push(lhs);
648                             r_fields.push(rhs);
649                         }
650
651                         let left_pat = gen_record_pat(gen_variant_path(&variant)?, l_pat_fields);
652                         let right_pat = gen_record_pat(gen_variant_path(&variant)?, r_pat_fields);
653                         let tuple_pat = make::tuple_pat(vec![left_pat.into(), right_pat.into()]);
654
655                         let len = l_fields.len();
656                         if len != 0 {
657                             let mut expr = gen_partial_cmp_call2(l_fields, r_fields);
658                             if len >= 2 {
659                                 expr = make::block_expr(None, Some(expr))
660                                     .indent(ast::edit::IndentLevel(1))
661                                     .into();
662                             }
663                             arms.push(make::match_arm(Some(tuple_pat.into()), None, expr));
664                         }
665                     }
666
667                     Some(ast::FieldList::TupleFieldList(list)) => {
668                         let mut l_pat_fields = vec![];
669                         let mut r_pat_fields = vec![];
670                         let mut l_fields = vec![];
671                         let mut r_fields = vec![];
672
673                         for (i, _) in list.fields().enumerate() {
674                             let field_name = format!("{}", i);
675
676                             let l_name = format!("l{}", field_name);
677                             l_pat_fields.push(gen_tuple_field(&l_name));
678
679                             let r_name = format!("r{}", field_name);
680                             r_pat_fields.push(gen_tuple_field(&r_name));
681
682                             let lhs = make::expr_path(make::ext::ident_path(&l_name));
683                             let rhs = make::expr_path(make::ext::ident_path(&r_name));
684                             l_fields.push(lhs);
685                             r_fields.push(rhs);
686                         }
687
688                         let left_pat =
689                             make::tuple_struct_pat(gen_variant_path(&variant)?, l_pat_fields);
690                         let right_pat =
691                             make::tuple_struct_pat(gen_variant_path(&variant)?, r_pat_fields);
692                         let tuple_pat = make::tuple_pat(vec![left_pat.into(), right_pat.into()]);
693
694                         let len = l_fields.len();
695                         if len != 0 {
696                             let mut expr = gen_partial_cmp_call2(l_fields, r_fields);
697                             if len >= 2 {
698                                 expr = make::block_expr(None, Some(expr))
699                                     .indent(ast::edit::IndentLevel(1))
700                                     .into();
701                             }
702                             arms.push(make::match_arm(Some(tuple_pat.into()), None, expr));
703                         }
704                     }
705                     None => continue,
706                 }
707             }
708
709             let expr = match arms.len() {
710                 0 => ord_check,
711                 _ => {
712                     if case_count > arms.len() {
713                         let lhs = make::wildcard_pat().into();
714                         arms.push(make::match_arm(Some(lhs), None, ord_check));
715                     }
716
717                     let match_target = make::expr_tuple(vec![lhs_name, rhs_name]);
718                     let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
719                     make::expr_match(match_target, list)
720                 }
721             };
722
723             make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
724         }
725         ast::Adt::Struct(strukt) => match strukt.field_list() {
726             Some(ast::FieldList::RecordFieldList(field_list)) => {
727                 let mut l_fields = vec![];
728                 let mut r_fields = vec![];
729                 for field in field_list.fields() {
730                     let lhs = make::expr_path(make::ext::ident_path("self"));
731                     let lhs = make::expr_field(lhs, &field.name()?.to_string());
732                     let rhs = make::expr_path(make::ext::ident_path("other"));
733                     let rhs = make::expr_field(rhs, &field.name()?.to_string());
734                     l_fields.push(lhs);
735                     r_fields.push(rhs);
736                 }
737
738                 let expr = gen_partial_cmp_call2(l_fields, r_fields);
739                 make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
740             }
741
742             Some(ast::FieldList::TupleFieldList(field_list)) => {
743                 let mut l_fields = vec![];
744                 let mut r_fields = vec![];
745                 for (i, _) in field_list.fields().enumerate() {
746                     let idx = format!("{}", i);
747                     let lhs = make::expr_path(make::ext::ident_path("self"));
748                     let lhs = make::expr_field(lhs, &idx);
749                     let rhs = make::expr_path(make::ext::ident_path("other"));
750                     let rhs = make::expr_field(rhs, &idx);
751                     l_fields.push(lhs);
752                     r_fields.push(rhs);
753                 }
754                 let expr = gen_partial_cmp_call2(l_fields, r_fields);
755                 make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
756             }
757
758             // No fields in the body means there's nothing to hash.
759             None => {
760                 let expr = make::expr_literal("true").into();
761                 make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
762             }
763         },
764     };
765
766     ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
767     Some(())
768 }
769
770 fn make_discriminant() -> Option<ast::Expr> {
771     Some(make::expr_path(make::ext::path_from_idents(["core", "mem", "discriminant"])?))
772 }