summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs661
1 files changed, 661 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
new file mode 100644
index 000000000..7a0c91295
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs
@@ -0,0 +1,661 @@
+//! This module contains functions to generate default trait impl function bodies where possible.
+
+use syntax::{
+ ast::{self, edit::AstNodeEdit, make, AstNode, BinaryOp, CmpOp, HasName, LogicOp},
+ ted,
+};
+
+/// Generate custom trait bodies without default implementation where possible.
+///
+/// Returns `Option` so that we can use `?` rather than `if let Some`. Returning
+/// `None` means that generating a custom trait body failed, and the body will remain
+/// as `todo!` instead.
+pub(crate) fn gen_trait_fn_body(
+ func: &ast::Fn,
+ trait_path: &ast::Path,
+ adt: &ast::Adt,
+) -> Option<()> {
+ match trait_path.segment()?.name_ref()?.text().as_str() {
+ "Clone" => gen_clone_impl(adt, func),
+ "Debug" => gen_debug_impl(adt, func),
+ "Default" => gen_default_impl(adt, func),
+ "Hash" => gen_hash_impl(adt, func),
+ "PartialEq" => gen_partial_eq(adt, func),
+ "PartialOrd" => gen_partial_ord(adt, func),
+ _ => None,
+ }
+}
+
+/// Generate a `Clone` impl based on the fields and members of the target type.
+fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
+ stdx::always!(func.name().map_or(false, |name| name.text() == "clone"));
+ fn gen_clone_call(target: ast::Expr) -> ast::Expr {
+ let method = make::name_ref("clone");
+ make::expr_method_call(target, method, make::arg_list(None))
+ }
+ let expr = match adt {
+ // `Clone` cannot be derived for unions, so no default impl can be provided.
+ ast::Adt::Union(_) => return None,
+ ast::Adt::Enum(enum_) => {
+ let list = enum_.variant_list()?;
+ let mut arms = vec![];
+ for variant in list.variants() {
+ let name = variant.name()?;
+ let variant_name = make::ext::path_from_idents(["Self", &format!("{}", name)])?;
+
+ match variant.field_list() {
+ // => match self { Self::Name { x } => Self::Name { x: x.clone() } }
+ Some(ast::FieldList::RecordFieldList(list)) => {
+ let mut pats = vec![];
+ let mut fields = vec![];
+ for field in list.fields() {
+ let field_name = field.name()?;
+ let pat = make::ident_pat(false, false, field_name.clone());
+ pats.push(pat.into());
+
+ let path = make::ext::ident_path(&field_name.to_string());
+ let method_call = gen_clone_call(make::expr_path(path));
+ let name_ref = make::name_ref(&field_name.to_string());
+ let field = make::record_expr_field(name_ref, Some(method_call));
+ fields.push(field);
+ }
+ let pat = make::record_pat(variant_name.clone(), pats.into_iter());
+ let fields = make::record_expr_field_list(fields);
+ let record_expr = make::record_expr(variant_name, fields).into();
+ arms.push(make::match_arm(Some(pat.into()), None, record_expr));
+ }
+
+ // => match self { Self::Name(arg1) => Self::Name(arg1.clone()) }
+ Some(ast::FieldList::TupleFieldList(list)) => {
+ let mut pats = vec![];
+ let mut fields = vec![];
+ for (i, _) in list.fields().enumerate() {
+ let field_name = format!("arg{}", i);
+ let pat = make::ident_pat(false, false, make::name(&field_name));
+ pats.push(pat.into());
+
+ let f_path = make::expr_path(make::ext::ident_path(&field_name));
+ fields.push(gen_clone_call(f_path));
+ }
+ let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter());
+ let struct_name = make::expr_path(variant_name);
+ let tuple_expr = make::expr_call(struct_name, make::arg_list(fields));
+ arms.push(make::match_arm(Some(pat.into()), None, tuple_expr));
+ }
+
+ // => match self { Self::Name => Self::Name }
+ None => {
+ let pattern = make::path_pat(variant_name.clone());
+ let variant_expr = make::expr_path(variant_name);
+ arms.push(make::match_arm(Some(pattern), None, variant_expr));
+ }
+ }
+ }
+
+ let match_target = make::expr_path(make::ext::ident_path("self"));
+ let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
+ make::expr_match(match_target, list)
+ }
+ ast::Adt::Struct(strukt) => {
+ match strukt.field_list() {
+ // => Self { name: self.name.clone() }
+ Some(ast::FieldList::RecordFieldList(field_list)) => {
+ let mut fields = vec![];
+ for field in field_list.fields() {
+ let base = make::expr_path(make::ext::ident_path("self"));
+ let target = make::expr_field(base, &field.name()?.to_string());
+ let method_call = gen_clone_call(target);
+ let name_ref = make::name_ref(&field.name()?.to_string());
+ let field = make::record_expr_field(name_ref, Some(method_call));
+ fields.push(field);
+ }
+ let struct_name = make::ext::ident_path("Self");
+ let fields = make::record_expr_field_list(fields);
+ make::record_expr(struct_name, fields).into()
+ }
+ // => Self(self.0.clone(), self.1.clone())
+ Some(ast::FieldList::TupleFieldList(field_list)) => {
+ let mut fields = vec![];
+ for (i, _) in field_list.fields().enumerate() {
+ let f_path = make::expr_path(make::ext::ident_path("self"));
+ let target = make::expr_field(f_path, &format!("{}", i));
+ fields.push(gen_clone_call(target));
+ }
+ let struct_name = make::expr_path(make::ext::ident_path("Self"));
+ make::expr_call(struct_name, make::arg_list(fields))
+ }
+ // => Self { }
+ None => {
+ let struct_name = make::ext::ident_path("Self");
+ let fields = make::record_expr_field_list(None);
+ make::record_expr(struct_name, fields).into()
+ }
+ }
+ }
+ };
+ let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
+ ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
+ Some(())
+}
+
+/// Generate a `Debug` impl based on the fields and members of the target type.
+fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
+ let annotated_name = adt.name()?;
+ match adt {
+ // `Debug` cannot be derived for unions, so no default impl can be provided.
+ ast::Adt::Union(_) => None,
+
+ // => match self { Self::Variant => write!(f, "Variant") }
+ ast::Adt::Enum(enum_) => {
+ let list = enum_.variant_list()?;
+ let mut arms = vec![];
+ for variant in list.variants() {
+ let name = variant.name()?;
+ let variant_name = make::ext::path_from_idents(["Self", &format!("{}", name)])?;
+ let target = make::expr_path(make::ext::ident_path("f"));
+
+ match variant.field_list() {
+ Some(ast::FieldList::RecordFieldList(list)) => {
+ // => f.debug_struct(name)
+ let target = make::expr_path(make::ext::ident_path("f"));
+ let method = make::name_ref("debug_struct");
+ let struct_name = format!("\"{}\"", name);
+ let args = make::arg_list(Some(make::expr_literal(&struct_name).into()));
+ let mut expr = make::expr_method_call(target, method, args);
+
+ let mut pats = vec![];
+ for field in list.fields() {
+ let field_name = field.name()?;
+
+ // create a field pattern for use in `MyStruct { fields.. }`
+ let pat = make::ident_pat(false, false, field_name.clone());
+ pats.push(pat.into());
+
+ // => <expr>.field("field_name", field)
+ let method_name = make::name_ref("field");
+ let name = make::expr_literal(&(format!("\"{}\"", field_name))).into();
+ let path = &format!("{}", field_name);
+ let path = make::expr_path(make::ext::ident_path(path));
+ let args = make::arg_list(vec![name, path]);
+ expr = make::expr_method_call(expr, method_name, args);
+ }
+
+ // => <expr>.finish()
+ let method = make::name_ref("finish");
+ let expr = make::expr_method_call(expr, method, make::arg_list(None));
+
+ // => MyStruct { fields.. } => f.debug_struct("MyStruct")...finish(),
+ let pat = make::record_pat(variant_name.clone(), pats.into_iter());
+ arms.push(make::match_arm(Some(pat.into()), None, expr));
+ }
+ Some(ast::FieldList::TupleFieldList(list)) => {
+ // => f.debug_tuple(name)
+ let target = make::expr_path(make::ext::ident_path("f"));
+ let method = make::name_ref("debug_tuple");
+ let struct_name = format!("\"{}\"", name);
+ let args = make::arg_list(Some(make::expr_literal(&struct_name).into()));
+ let mut expr = make::expr_method_call(target, method, args);
+
+ let mut pats = vec![];
+ for (i, _) in list.fields().enumerate() {
+ let name = format!("arg{}", i);
+
+ // create a field pattern for use in `MyStruct(fields..)`
+ let field_name = make::name(&name);
+ let pat = make::ident_pat(false, false, field_name.clone());
+ pats.push(pat.into());
+
+ // => <expr>.field(field)
+ let method_name = make::name_ref("field");
+ let field_path = &name.to_string();
+ let field_path = make::expr_path(make::ext::ident_path(field_path));
+ let args = make::arg_list(vec![field_path]);
+ expr = make::expr_method_call(expr, method_name, args);
+ }
+
+ // => <expr>.finish()
+ let method = make::name_ref("finish");
+ let expr = make::expr_method_call(expr, method, make::arg_list(None));
+
+ // => MyStruct (fields..) => f.debug_tuple("MyStruct")...finish(),
+ let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter());
+ arms.push(make::match_arm(Some(pat.into()), None, expr));
+ }
+ None => {
+ let fmt_string = make::expr_literal(&(format!("\"{}\"", name))).into();
+ let args = make::arg_list([target, fmt_string]);
+ let macro_name = make::expr_path(make::ext::ident_path("write"));
+ let macro_call = make::expr_macro_call(macro_name, args);
+
+ let variant_name = make::path_pat(variant_name);
+ arms.push(make::match_arm(Some(variant_name), None, macro_call));
+ }
+ }
+ }
+
+ let match_target = make::expr_path(make::ext::ident_path("self"));
+ let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
+ let match_expr = make::expr_match(match_target, list);
+
+ let body = make::block_expr(None, Some(match_expr));
+ let body = body.indent(ast::edit::IndentLevel(1));
+ ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
+ Some(())
+ }
+
+ ast::Adt::Struct(strukt) => {
+ let name = format!("\"{}\"", annotated_name);
+ let args = make::arg_list(Some(make::expr_literal(&name).into()));
+ let target = make::expr_path(make::ext::ident_path("f"));
+
+ let expr = match strukt.field_list() {
+ // => f.debug_struct("Name").finish()
+ None => make::expr_method_call(target, make::name_ref("debug_struct"), args),
+
+ // => f.debug_struct("Name").field("foo", &self.foo).finish()
+ Some(ast::FieldList::RecordFieldList(field_list)) => {
+ let method = make::name_ref("debug_struct");
+ let mut expr = make::expr_method_call(target, method, args);
+ for field in field_list.fields() {
+ let name = field.name()?;
+ let f_name = make::expr_literal(&(format!("\"{}\"", name))).into();
+ let f_path = make::expr_path(make::ext::ident_path("self"));
+ let f_path = make::expr_ref(f_path, false);
+ let f_path = make::expr_field(f_path, &format!("{}", name));
+ let args = make::arg_list([f_name, f_path]);
+ expr = make::expr_method_call(expr, make::name_ref("field"), args);
+ }
+ expr
+ }
+
+ // => f.debug_tuple("Name").field(self.0).finish()
+ Some(ast::FieldList::TupleFieldList(field_list)) => {
+ let method = make::name_ref("debug_tuple");
+ let mut expr = make::expr_method_call(target, method, args);
+ for (i, _) in field_list.fields().enumerate() {
+ let f_path = make::expr_path(make::ext::ident_path("self"));
+ let f_path = make::expr_ref(f_path, false);
+ let f_path = make::expr_field(f_path, &format!("{}", i));
+ let method = make::name_ref("field");
+ expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path)));
+ }
+ expr
+ }
+ };
+
+ let method = make::name_ref("finish");
+ let expr = make::expr_method_call(expr, method, make::arg_list(None));
+ let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
+ ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
+ Some(())
+ }
+ }
+}
+
+/// Generate a `Debug` impl based on the fields and members of the target type.
+fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
+ fn gen_default_call() -> Option<ast::Expr> {
+ let fn_name = make::ext::path_from_idents(["Default", "default"])?;
+ Some(make::expr_call(make::expr_path(fn_name), make::arg_list(None)))
+ }
+ match adt {
+ // `Debug` cannot be derived for unions, so no default impl can be provided.
+ ast::Adt::Union(_) => None,
+ // Deriving `Debug` for enums is not stable yet.
+ ast::Adt::Enum(_) => None,
+ ast::Adt::Struct(strukt) => {
+ let expr = match strukt.field_list() {
+ Some(ast::FieldList::RecordFieldList(field_list)) => {
+ let mut fields = vec![];
+ for field in field_list.fields() {
+ let method_call = gen_default_call()?;
+ let name_ref = make::name_ref(&field.name()?.to_string());
+ let field = make::record_expr_field(name_ref, Some(method_call));
+ fields.push(field);
+ }
+ let struct_name = make::ext::ident_path("Self");
+ let fields = make::record_expr_field_list(fields);
+ make::record_expr(struct_name, fields).into()
+ }
+ Some(ast::FieldList::TupleFieldList(field_list)) => {
+ let struct_name = make::expr_path(make::ext::ident_path("Self"));
+ let fields = field_list
+ .fields()
+ .map(|_| gen_default_call())
+ .collect::<Option<Vec<ast::Expr>>>()?;
+ make::expr_call(struct_name, make::arg_list(fields))
+ }
+ None => {
+ let struct_name = make::ext::ident_path("Self");
+ let fields = make::record_expr_field_list(None);
+ make::record_expr(struct_name, fields).into()
+ }
+ };
+ let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
+ ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
+ Some(())
+ }
+ }
+}
+
+/// Generate a `Hash` impl based on the fields and members of the target type.
+fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
+ stdx::always!(func.name().map_or(false, |name| name.text() == "hash"));
+ fn gen_hash_call(target: ast::Expr) -> ast::Stmt {
+ let method = make::name_ref("hash");
+ let arg = make::expr_path(make::ext::ident_path("state"));
+ let expr = make::expr_method_call(target, method, make::arg_list(Some(arg)));
+ make::expr_stmt(expr).into()
+ }
+
+ let body = match adt {
+ // `Hash` cannot be derived for unions, so no default impl can be provided.
+ ast::Adt::Union(_) => return None,
+
+ // => std::mem::discriminant(self).hash(state);
+ ast::Adt::Enum(_) => {
+ let fn_name = make_discriminant()?;
+
+ let arg = make::expr_path(make::ext::ident_path("self"));
+ let fn_call = make::expr_call(fn_name, make::arg_list(Some(arg)));
+ let stmt = gen_hash_call(fn_call);
+
+ make::block_expr(Some(stmt), None).indent(ast::edit::IndentLevel(1))
+ }
+ ast::Adt::Struct(strukt) => match strukt.field_list() {
+ // => self.<field>.hash(state);
+ Some(ast::FieldList::RecordFieldList(field_list)) => {
+ let mut stmts = vec![];
+ for field in field_list.fields() {
+ let base = make::expr_path(make::ext::ident_path("self"));
+ let target = make::expr_field(base, &field.name()?.to_string());
+ stmts.push(gen_hash_call(target));
+ }
+ make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1))
+ }
+
+ // => self.<field_index>.hash(state);
+ Some(ast::FieldList::TupleFieldList(field_list)) => {
+ let mut stmts = vec![];
+ for (i, _) in field_list.fields().enumerate() {
+ let base = make::expr_path(make::ext::ident_path("self"));
+ let target = make::expr_field(base, &format!("{}", i));
+ stmts.push(gen_hash_call(target));
+ }
+ make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1))
+ }
+
+ // No fields in the body means there's nothing to hash.
+ None => return None,
+ },
+ };
+
+ ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
+ Some(())
+}
+
+/// Generate a `PartialEq` impl based on the fields and members of the target type.
+fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
+ stdx::always!(func.name().map_or(false, |name| name.text() == "eq"));
+ fn gen_eq_chain(expr: Option<ast::Expr>, cmp: ast::Expr) -> Option<ast::Expr> {
+ match expr {
+ Some(expr) => Some(make::expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)),
+ None => Some(cmp),
+ }
+ }
+
+ fn gen_record_pat_field(field_name: &str, pat_name: &str) -> ast::RecordPatField {
+ let pat = make::ext::simple_ident_pat(make::name(pat_name));
+ let name_ref = make::name_ref(field_name);
+ make::record_pat_field(name_ref, pat.into())
+ }
+
+ fn gen_record_pat(record_name: ast::Path, fields: Vec<ast::RecordPatField>) -> ast::RecordPat {
+ let list = make::record_pat_field_list(fields);
+ make::record_pat_with_fields(record_name, list)
+ }
+
+ fn gen_variant_path(variant: &ast::Variant) -> Option<ast::Path> {
+ make::ext::path_from_idents(["Self", &variant.name()?.to_string()])
+ }
+
+ fn gen_tuple_field(field_name: &String) -> ast::Pat {
+ ast::Pat::IdentPat(make::ident_pat(false, false, make::name(field_name)))
+ }
+
+ // FIXME: return `None` if the trait carries a generic type; we can only
+ // generate this code `Self` for the time being.
+
+ let body = match adt {
+ // `PartialEq` cannot be derived for unions, so no default impl can be provided.
+ ast::Adt::Union(_) => return None,
+
+ ast::Adt::Enum(enum_) => {
+ // => std::mem::discriminant(self) == std::mem::discriminant(other)
+ let lhs_name = make::expr_path(make::ext::ident_path("self"));
+ let lhs = make::expr_call(make_discriminant()?, make::arg_list(Some(lhs_name.clone())));
+ let rhs_name = make::expr_path(make::ext::ident_path("other"));
+ let rhs = make::expr_call(make_discriminant()?, make::arg_list(Some(rhs_name.clone())));
+ let eq_check =
+ make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
+
+ let mut n_cases = 0;
+ let mut arms = vec![];
+ for variant in enum_.variant_list()?.variants() {
+ n_cases += 1;
+ match variant.field_list() {
+ // => (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin,
+ Some(ast::FieldList::RecordFieldList(list)) => {
+ let mut expr = None;
+ let mut l_fields = vec![];
+ let mut r_fields = vec![];
+
+ for field in list.fields() {
+ let field_name = field.name()?.to_string();
+
+ let l_name = &format!("l_{}", field_name);
+ l_fields.push(gen_record_pat_field(&field_name, l_name));
+
+ let r_name = &format!("r_{}", field_name);
+ r_fields.push(gen_record_pat_field(&field_name, r_name));
+
+ let lhs = make::expr_path(make::ext::ident_path(l_name));
+ let rhs = make::expr_path(make::ext::ident_path(r_name));
+ let cmp = make::expr_bin_op(
+ lhs,
+ BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
+ rhs,
+ );
+ expr = gen_eq_chain(expr, cmp);
+ }
+
+ let left = gen_record_pat(gen_variant_path(&variant)?, l_fields);
+ let right = gen_record_pat(gen_variant_path(&variant)?, r_fields);
+ let tuple = make::tuple_pat(vec![left.into(), right.into()]);
+
+ if let Some(expr) = expr {
+ arms.push(make::match_arm(Some(tuple.into()), None, expr));
+ }
+ }
+
+ Some(ast::FieldList::TupleFieldList(list)) => {
+ let mut expr = None;
+ let mut l_fields = vec![];
+ let mut r_fields = vec![];
+
+ for (i, _) in list.fields().enumerate() {
+ let field_name = format!("{}", i);
+
+ let l_name = format!("l{}", field_name);
+ l_fields.push(gen_tuple_field(&l_name));
+
+ let r_name = format!("r{}", field_name);
+ r_fields.push(gen_tuple_field(&r_name));
+
+ let lhs = make::expr_path(make::ext::ident_path(&l_name));
+ let rhs = make::expr_path(make::ext::ident_path(&r_name));
+ let cmp = make::expr_bin_op(
+ lhs,
+ BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
+ rhs,
+ );
+ expr = gen_eq_chain(expr, cmp);
+ }
+
+ let left = make::tuple_struct_pat(gen_variant_path(&variant)?, l_fields);
+ let right = make::tuple_struct_pat(gen_variant_path(&variant)?, r_fields);
+ let tuple = make::tuple_pat(vec![left.into(), right.into()]);
+
+ if let Some(expr) = expr {
+ arms.push(make::match_arm(Some(tuple.into()), None, expr));
+ }
+ }
+ None => continue,
+ }
+ }
+
+ let expr = match arms.len() {
+ 0 => eq_check,
+ _ => {
+ if n_cases > arms.len() {
+ let lhs = make::wildcard_pat().into();
+ arms.push(make::match_arm(Some(lhs), None, eq_check));
+ }
+
+ let match_target = make::expr_tuple(vec![lhs_name, rhs_name]);
+ let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
+ make::expr_match(match_target, list)
+ }
+ };
+
+ make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
+ }
+ ast::Adt::Struct(strukt) => match strukt.field_list() {
+ Some(ast::FieldList::RecordFieldList(field_list)) => {
+ let mut expr = None;
+ for field in field_list.fields() {
+ let lhs = make::expr_path(make::ext::ident_path("self"));
+ let lhs = make::expr_field(lhs, &field.name()?.to_string());
+ let rhs = make::expr_path(make::ext::ident_path("other"));
+ let rhs = make::expr_field(rhs, &field.name()?.to_string());
+ let cmp =
+ make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
+ expr = gen_eq_chain(expr, cmp);
+ }
+ make::block_expr(None, expr).indent(ast::edit::IndentLevel(1))
+ }
+
+ Some(ast::FieldList::TupleFieldList(field_list)) => {
+ let mut expr = None;
+ for (i, _) in field_list.fields().enumerate() {
+ let idx = format!("{}", i);
+ let lhs = make::expr_path(make::ext::ident_path("self"));
+ let lhs = make::expr_field(lhs, &idx);
+ let rhs = make::expr_path(make::ext::ident_path("other"));
+ let rhs = make::expr_field(rhs, &idx);
+ let cmp =
+ make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
+ expr = gen_eq_chain(expr, cmp);
+ }
+ make::block_expr(None, expr).indent(ast::edit::IndentLevel(1))
+ }
+
+ // No fields in the body means there's nothing to hash.
+ None => {
+ let expr = make::expr_literal("true").into();
+ make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
+ }
+ },
+ };
+
+ ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
+ Some(())
+}
+
+fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
+ stdx::always!(func.name().map_or(false, |name| name.text() == "partial_cmp"));
+ fn gen_partial_eq_match(match_target: ast::Expr) -> Option<ast::Stmt> {
+ let mut arms = vec![];
+
+ let variant_name =
+ make::path_pat(make::ext::path_from_idents(["core", "cmp", "Ordering", "Equal"])?);
+ let lhs = make::tuple_struct_pat(make::ext::path_from_idents(["Some"])?, [variant_name]);
+ arms.push(make::match_arm(Some(lhs.into()), None, make::expr_empty_block()));
+
+ arms.push(make::match_arm(
+ [make::ident_pat(false, false, make::name("ord")).into()],
+ None,
+ make::expr_return(Some(make::expr_path(make::ext::ident_path("ord")))),
+ ));
+ let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
+ Some(make::expr_stmt(make::expr_match(match_target, list)).into())
+ }
+
+ fn gen_partial_cmp_call(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
+ let rhs = make::expr_ref(rhs, false);
+ let method = make::name_ref("partial_cmp");
+ make::expr_method_call(lhs, method, make::arg_list(Some(rhs)))
+ }
+
+ // FIXME: return `None` if the trait carries a generic type; we can only
+ // generate this code `Self` for the time being.
+
+ let body = match adt {
+ // `PartialOrd` cannot be derived for unions, so no default impl can be provided.
+ ast::Adt::Union(_) => return None,
+ // `core::mem::Discriminant` does not implement `PartialOrd` in stable Rust today.
+ ast::Adt::Enum(_) => return None,
+ ast::Adt::Struct(strukt) => match strukt.field_list() {
+ Some(ast::FieldList::RecordFieldList(field_list)) => {
+ let mut exprs = vec![];
+ for field in field_list.fields() {
+ let lhs = make::expr_path(make::ext::ident_path("self"));
+ let lhs = make::expr_field(lhs, &field.name()?.to_string());
+ let rhs = make::expr_path(make::ext::ident_path("other"));
+ let rhs = make::expr_field(rhs, &field.name()?.to_string());
+ let ord = gen_partial_cmp_call(lhs, rhs);
+ exprs.push(ord);
+ }
+
+ let tail = exprs.pop();
+ let stmts = exprs
+ .into_iter()
+ .map(gen_partial_eq_match)
+ .collect::<Option<Vec<ast::Stmt>>>()?;
+ make::block_expr(stmts.into_iter(), tail).indent(ast::edit::IndentLevel(1))
+ }
+
+ Some(ast::FieldList::TupleFieldList(field_list)) => {
+ let mut exprs = vec![];
+ for (i, _) in field_list.fields().enumerate() {
+ let idx = format!("{}", i);
+ let lhs = make::expr_path(make::ext::ident_path("self"));
+ let lhs = make::expr_field(lhs, &idx);
+ let rhs = make::expr_path(make::ext::ident_path("other"));
+ let rhs = make::expr_field(rhs, &idx);
+ let ord = gen_partial_cmp_call(lhs, rhs);
+ exprs.push(ord);
+ }
+ let tail = exprs.pop();
+ let stmts = exprs
+ .into_iter()
+ .map(gen_partial_eq_match)
+ .collect::<Option<Vec<ast::Stmt>>>()?;
+ make::block_expr(stmts.into_iter(), tail).indent(ast::edit::IndentLevel(1))
+ }
+
+ // No fields in the body means there's nothing to compare.
+ None => {
+ let expr = make::expr_literal("true").into();
+ make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
+ }
+ },
+ };
+
+ ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
+ Some(())
+}
+
+fn make_discriminant() -> Option<ast::Expr> {
+ Some(make::expr_path(make::ext::path_from_idents(["core", "mem", "discriminant"])?))
+}