From f8a64c044a1e2d67adf7567ff5a8dea60833702d Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 10 Aug 2021 19:49:06 +0200 Subject: [PATCH] gen PartialEq for structs --- .../src/utils/gen_trait_fn_body.rs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/crates/ide_assists/src/utils/gen_trait_fn_body.rs b/crates/ide_assists/src/utils/gen_trait_fn_body.rs index 04f396d46e..972d14f4c9 100644 --- a/crates/ide_assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide_assists/src/utils/gen_trait_fn_body.rs @@ -20,6 +20,7 @@ pub(crate) fn gen_trait_fn_body( "Debug" => gen_debug_impl(adt, func), "Default" => gen_default_impl(adt, func), "Hash" => gen_hash_impl(adt, func), + "PartialEq" => gen_partial_eq(adt, func), _ => None, } } @@ -326,3 +327,62 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { 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<()> { + // 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 { + // `Hash` cannot be derived for unions, so no default impl can be provided. + ast::Adt::Union(_) => return None, + + // FIXME: generate trait variants + ast::Adt::Enum(_) => todo!(), + ast::Adt::Struct(strukt) => match strukt.field_list() { + // => self..hash(state); + 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_op(ast::BinOp::EqualityTest, lhs, rhs); + expr = match expr { + Some(expr) => Some(make::expr_op(ast::BinOp::BooleanAnd, expr, cmp)), + None => Some(cmp), + }; + } + make::block_expr(None, expr).indent(ast::edit::IndentLevel(1)) + } + + // => self..hash(state); + 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_op(ast::BinOp::EqualityTest, lhs, rhs); + expr = match expr { + Some(expr) => Some(make::expr_op(ast::BinOp::BooleanAnd, expr, cmp)), + None => Some(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(()) +}