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