]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/utils/gen_trait_fn_body.rs
impl PartialOrd codegen for tuple records
[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_eq_chain(expr: Option<ast::Expr>, cmp: ast::Expr) -> Option<ast::Expr> {
578         match expr {
579             Some(expr) => Some(make::expr_op(ast::BinOp::BooleanAnd, expr, cmp)),
580             None => Some(cmp),
581         }
582     }
583
584     fn gen_partial_eq_match(match_target: ast::Expr) -> Option<ast::Stmt> {
585         let mut arms = vec![];
586
587         let variant_name =
588             make::path_pat(make::ext::path_from_idents(["core", "cmp", "Ordering", "Eq"])?);
589         let lhs = make::tuple_struct_pat(make::ext::path_from_idents(["Some"])?, [variant_name]);
590         arms.push(make::match_arm(Some(lhs.into()), None, make::expr_empty_block()));
591
592         arms.push(make::match_arm(
593             [make::ident_pat(false, false, make::name("ord")).into()],
594             None,
595             make::expr_return(Some(make::expr_path(make::ext::ident_path("ord")))),
596         ));
597         // let rhs = make::expr_path(make::ext::ident_path("other"));
598         let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
599         Some(make::expr_stmt(make::expr_match(match_target, list)).into())
600     }
601
602     fn gen_partial_cmp_call(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
603         let method = make::name_ref("partial_cmp");
604         make::expr_method_call(lhs, method, make::arg_list(Some(rhs)))
605     }
606
607     fn gen_record_pat_field(field_name: &str, pat_name: &str) -> ast::RecordPatField {
608         let pat = make::ext::simple_ident_pat(make::name(&pat_name));
609         let name_ref = make::name_ref(field_name);
610         make::record_pat_field(name_ref, pat.into())
611     }
612
613     fn gen_record_pat(record_name: ast::Path, fields: Vec<ast::RecordPatField>) -> ast::RecordPat {
614         let list = make::record_pat_field_list(fields);
615         make::record_pat_with_fields(record_name, list)
616     }
617
618     fn gen_variant_path(variant: &ast::Variant) -> Option<ast::Path> {
619         make::ext::path_from_idents(["Self", &variant.name()?.to_string()])
620     }
621
622     fn gen_tuple_field(field_name: &String) -> ast::Pat {
623         ast::Pat::IdentPat(make::ident_pat(false, false, make::name(field_name)))
624     }
625
626     // FIXME: return `None` if the trait carries a generic type; we can only
627     // generate this code `Self` for the time being.
628
629     let body = match adt {
630         // `Hash` cannot be derived for unions, so no default impl can be provided.
631         ast::Adt::Union(_) => return None,
632
633         ast::Adt::Enum(enum_) => {
634             // => std::mem::discriminant(self) == std::mem::discriminant(other)
635             let lhs_name = make::expr_path(make::ext::ident_path("self"));
636             let lhs = make::expr_call(make_discriminant()?, make::arg_list(Some(lhs_name.clone())));
637             let rhs_name = make::expr_path(make::ext::ident_path("other"));
638             let rhs = make::expr_call(make_discriminant()?, make::arg_list(Some(rhs_name.clone())));
639             let eq_check = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs);
640
641             let mut case_count = 0;
642             let mut arms = vec![];
643             for variant in enum_.variant_list()?.variants() {
644                 case_count += 1;
645                 match variant.field_list() {
646                     // => (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin,
647                     Some(ast::FieldList::RecordFieldList(list)) => {
648                         let mut expr = None;
649                         let mut l_fields = vec![];
650                         let mut r_fields = vec![];
651
652                         for field in list.fields() {
653                             let field_name = field.name()?.to_string();
654
655                             let l_name = &format!("l_{}", field_name);
656                             l_fields.push(gen_record_pat_field(&field_name, &l_name));
657
658                             let r_name = &format!("r_{}", field_name);
659                             r_fields.push(gen_record_pat_field(&field_name, &r_name));
660
661                             let lhs = make::expr_path(make::ext::ident_path(l_name));
662                             let rhs = make::expr_path(make::ext::ident_path(r_name));
663                             let cmp = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs);
664                             expr = gen_eq_chain(expr, cmp);
665                         }
666
667                         let left = gen_record_pat(gen_variant_path(&variant)?, l_fields);
668                         let right = gen_record_pat(gen_variant_path(&variant)?, r_fields);
669                         let tuple = make::tuple_pat(vec![left.into(), right.into()]);
670
671                         if let Some(expr) = expr {
672                             arms.push(make::match_arm(Some(tuple.into()), None, expr));
673                         }
674                     }
675
676                     Some(ast::FieldList::TupleFieldList(list)) => {
677                         let mut expr = None;
678                         let mut l_fields = vec![];
679                         let mut r_fields = vec![];
680
681                         for (i, _) in list.fields().enumerate() {
682                             let field_name = format!("{}", i);
683
684                             let l_name = format!("l{}", field_name);
685                             l_fields.push(gen_tuple_field(&l_name));
686
687                             let r_name = format!("r{}", field_name);
688                             r_fields.push(gen_tuple_field(&r_name));
689
690                             let lhs = make::expr_path(make::ext::ident_path(&l_name));
691                             let rhs = make::expr_path(make::ext::ident_path(&r_name));
692                             let cmp = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs);
693                             expr = gen_eq_chain(expr, cmp);
694                         }
695
696                         let left = make::tuple_struct_pat(gen_variant_path(&variant)?, l_fields);
697                         let right = make::tuple_struct_pat(gen_variant_path(&variant)?, r_fields);
698                         let tuple = make::tuple_pat(vec![left.into(), right.into()]);
699
700                         if let Some(expr) = expr {
701                             arms.push(make::match_arm(Some(tuple.into()), None, expr));
702                         }
703                     }
704                     None => continue,
705                 }
706             }
707
708             let expr = match arms.len() {
709                 0 => eq_check,
710                 _ => {
711                     if case_count > arms.len() {
712                         let lhs = make::wildcard_pat().into();
713                         arms.push(make::match_arm(Some(lhs), None, eq_check));
714                     }
715
716                     let match_target = make::expr_tuple(vec![lhs_name, rhs_name]);
717                     let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
718                     make::expr_match(match_target, list)
719                 }
720             };
721
722             make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
723         }
724         ast::Adt::Struct(strukt) => match strukt.field_list() {
725             Some(ast::FieldList::RecordFieldList(field_list)) => {
726                 let mut exprs = vec![];
727                 for field in field_list.fields() {
728                     let lhs = make::expr_path(make::ext::ident_path("self"));
729                     let lhs = make::expr_field(lhs, &field.name()?.to_string());
730                     let rhs = make::expr_path(make::ext::ident_path("other"));
731                     let rhs = make::expr_field(rhs, &field.name()?.to_string());
732                     let ord = gen_partial_cmp_call(lhs, rhs);
733                     exprs.push(ord);
734                 }
735
736                 let tail = exprs.pop();
737                 let stmts = exprs
738                     .into_iter()
739                     .map(gen_partial_eq_match)
740                     .collect::<Option<Vec<ast::Stmt>>>()?;
741                 make::block_expr(stmts.into_iter(), tail).indent(ast::edit::IndentLevel(1))
742             }
743
744             Some(ast::FieldList::TupleFieldList(field_list)) => {
745                 let mut expr = None;
746                 for (i, _) in field_list.fields().enumerate() {
747                     let idx = format!("{}", i);
748                     let lhs = make::expr_path(make::ext::ident_path("self"));
749                     let lhs = make::expr_field(lhs, &idx);
750                     let rhs = make::expr_path(make::ext::ident_path("other"));
751                     let rhs = make::expr_field(rhs, &idx);
752                     let cmp = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs);
753                     expr = gen_eq_chain(expr, cmp);
754                 }
755                 make::block_expr(None, 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 }