From 20c3b20db3354729df606f3b5a1ddd474ca7c78c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 10 Aug 2021 19:48:32 +0200 Subject: [PATCH 01/13] add make::expr_op --- crates/syntax/src/ast/expr_ext.rs | 36 +++++++++++++++++++++++++++++++ crates/syntax/src/ast/make.rs | 3 +++ 2 files changed, 39 insertions(+) diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs index 01ab562c7ee..b615037b7e3 100644 --- a/crates/syntax/src/ast/expr_ext.rs +++ b/crates/syntax/src/ast/expr_ext.rs @@ -350,6 +350,42 @@ impl ast::BinExpr { } } +impl std::fmt::Display for BinOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BinOp::BooleanOr => write!(f, "||"), + BinOp::BooleanAnd => write!(f, "&&"), + BinOp::EqualityTest => write!(f, "=="), + BinOp::NegatedEqualityTest => write!(f, "!="), + BinOp::LesserEqualTest => write!(f, "<="), + BinOp::GreaterEqualTest => write!(f, ">="), + BinOp::LesserTest => write!(f, "<"), + BinOp::GreaterTest => write!(f, ">"), + BinOp::Addition => write!(f, "+"), + BinOp::Multiplication => write!(f, "*"), + BinOp::Subtraction => write!(f, "-"), + BinOp::Division => write!(f, "/"), + BinOp::Remainder => write!(f, "%"), + BinOp::LeftShift => write!(f, "<<"), + BinOp::RightShift => write!(f, ">>"), + BinOp::BitwiseXor => write!(f, "^"), + BinOp::BitwiseOr => write!(f, "|"), + BinOp::BitwiseAnd => write!(f, "&"), + BinOp::Assignment => write!(f, "="), + BinOp::AddAssign => write!(f, "+="), + BinOp::DivAssign => write!(f, "/="), + BinOp::MulAssign => write!(f, "*="), + BinOp::RemAssign => write!(f, "%="), + BinOp::ShrAssign => write!(f, ">>="), + BinOp::ShlAssign => write!(f, "<<="), + BinOp::SubAssign => write!(f, "-"), + BinOp::BitOrAssign => write!(f, "|="), + BinOp::BitAndAssign => write!(f, "&="), + BinOp::BitXorAssign => write!(f, "^="), + } + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum RangeOp { /// `..` diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index ec90be35a6e..74f2e5c314c 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -264,6 +264,9 @@ pub fn expr_path(path: ast::Path) -> ast::Expr { pub fn expr_continue() -> ast::Expr { expr_from_text("continue") } +pub fn expr_op(op: ast::BinOp, lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr { + expr_from_text(&format!("{} {} {}", lhs, op, rhs)) +} pub fn expr_break(expr: Option) -> ast::Expr { match expr { Some(expr) => expr_from_text(&format!("break {}", expr)), From 3cad2c4feba94c0a2090b65a1fe6156836feb32b Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 10 Aug 2021 19:48:50 +0200 Subject: [PATCH 02/13] add partial_eq gen tests --- .../replace_derive_with_manual_impl.rs | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs index f256bf035ba..49750e354be 100644 --- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs @@ -606,6 +606,181 @@ impl Clone for Foo { } } } +"#, + ) + } + + #[test] + fn add_custom_impl_partial_eq_record_struct() { + check_assist( + replace_derive_with_manual_impl, + r#" +//- minicore: eq +#[derive(Partial$0Eq)] +struct Foo { + bin: usize, + bar: usize, +} +"#, + r#" +struct Foo { + bin: usize, + bar: usize, +} + +impl PartialEq for Foo { + $0fn eq(&self, other: &Self) -> bool { + self.bin == other.bin && self.bar == other.bar + } +} +"#, + ) + } + + #[test] + fn add_custom_impl_partial_eq_tuple_struct() { + check_assist( + replace_derive_with_manual_impl, + r#" +//- minicore: eq +#[derive(Partial$0Eq)] +struct Foo(usize, usize); +"#, + r#" +struct Foo(usize, usize); + +impl PartialEq for Foo { + $0fn eq(&self, other: &Self) -> bool { + self.0 == other.0 && self.1 == other.1 + } +} +"#, + ) + } + + #[test] + fn add_custom_impl_partial_eq_empty_struct() { + check_assist( + replace_derive_with_manual_impl, + r#" +//- minicore: eq +#[derive(Partial$0Eq)] +struct Foo; +"#, + r#" +struct Foo; + +impl PartialEq for Foo { + $0fn eq(&self, other: &Self) -> bool { + true + } +} +"#, + ) + } + + #[test] + fn add_custom_impl_partial_eq_enum() { + check_assist( + replace_derive_with_manual_impl, + r#" +//- minicore: eq +#[derive(Partial$0Eq)] +enum Foo { + Bar, + Baz, +} +"#, + r#" +enum Foo { + Bar, + Baz, +} + +impl PartialEq for Foo { + $0fn eq(&self, other: &Self) -> bool { + std::mem::discriminant(self) == std::mem::discriminant(other) + } +} +"#, + ) + } + + #[test] + fn add_custom_impl_partial_eq_tuple_enum() { + check_assist( + replace_derive_with_manual_impl, + r#" +//- minicore: eq +#[derive(Partial$0Eq)] +enum Foo { + Bar(String), + Baz, +} +"#, + r#" +enum Foo { + Bar(String), + Baz, +} + +impl PartialEq for Foo { + $0fn eq(&self, other: &Self) -> bool { + if std::mem::discriminant(self) == std::mem::discriminant(other) { + match (self, other) { + (Self::Bar(l0), Self::Bar(r0)) => l0 == r0, + _ => true, + } + } else { + false + } + } +} +"#, + ) + } + + #[test] + fn add_custom_impl_partial_eq_record_enum() { + check_assist( + replace_derive_with_manual_impl, + r#" +//- minicore: eq +#[derive(Partial$0Eq)] +enum Foo { + Bar { + bin: String, + }, + Baz { + quz: String, + fez: String, + }, +} +"#, + r#" +enum Foo { + Bar { + bin: String, + }, + Baz { + quz: String, + fez: String, + } +} + +impl PartialEq for Foo { + $0fn eq(&self, other: &Self) -> bool { + if std::mem::discriminant(self) == std::mem::discriminant(other) { + match (self, other) { + (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin, + (Self::Baz { qux: l_qux, fez: l_fez }, Self::Bar { qux: r_qux, fez: r_fez }) => l_qux == r_qux && l_fez == r_fez, + _ => true, + } + } else { + false + } + } +} "#, ) } From f8a64c044a1e2d67adf7567ff5a8dea60833702d Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 10 Aug 2021 19:49:06 +0200 Subject: [PATCH 03/13] 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 04f396d46e2..972d14f4c9e 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(()) +} From 65ce87cad827349fe2c86489b90762a4585de85c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 10 Aug 2021 21:05:23 +0200 Subject: [PATCH 04/13] gen PartialEq for basic enums --- .../replace_derive_with_manual_impl.rs | 6 +++--- .../src/utils/gen_trait_fn_body.rs | 21 ++++++++++++++++--- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs index 49750e354be..d8d62b73b88 100644 --- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs @@ -699,7 +699,7 @@ enum Foo { impl PartialEq for Foo { $0fn eq(&self, other: &Self) -> bool { - std::mem::discriminant(self) == std::mem::discriminant(other) + core::mem::discriminant(self) == core::mem::discriminant(other) } } "#, @@ -726,7 +726,7 @@ enum Foo { impl PartialEq for Foo { $0fn eq(&self, other: &Self) -> bool { - if std::mem::discriminant(self) == std::mem::discriminant(other) { + if core::mem::discriminant(self) == core::mem::discriminant(other) { match (self, other) { (Self::Bar(l0), Self::Bar(r0)) => l0 == r0, _ => true, @@ -770,7 +770,7 @@ enum Foo { impl PartialEq for Foo { $0fn eq(&self, other: &Self) -> bool { - if std::mem::discriminant(self) == std::mem::discriminant(other) { + if core::mem::discriminant(self) == core::mem::discriminant(other) { match (self, other) { (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin, (Self::Baz { qux: l_qux, fez: l_fez }, Self::Bar { qux: r_qux, fez: r_fez }) => l_qux == r_qux && l_fez == r_fez, 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 972d14f4c9e..af870f38969 100644 --- a/crates/ide_assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide_assists/src/utils/gen_trait_fn_body.rs @@ -330,6 +330,15 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { /// 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<()> { + fn gen_discriminant() -> ast::Expr { + let root = make::ext::ident_path("core"); + let submodule = make::ext::ident_path("mem"); + let fn_name = make::ext::ident_path("discriminant"); + let fn_name = make::path_concat(submodule, fn_name); + let fn_name = make::expr_path(make::path_concat(root, fn_name)); + fn_name + } + // FIXME: return `None` if the trait carries a generic type; we can only // generate this code `Self` for the time being. @@ -338,9 +347,16 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { ast::Adt::Union(_) => return None, // FIXME: generate trait variants - ast::Adt::Enum(_) => todo!(), + ast::Adt::Enum(enum_) => { + // => std::mem::discriminant(self) == std::mem::discriminant(other) + let lhs = make::expr_path(make::ext::ident_path("self")); + let lhs = make::expr_call(gen_discriminant(), make::arg_list(Some(lhs))); + let rhs = make::expr_path(make::ext::ident_path("other")); + let rhs = make::expr_call(gen_discriminant(), make::arg_list(Some(rhs))); + let cmp = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs); + make::block_expr(None, Some(cmp)).indent(ast::edit::IndentLevel(1)) + } 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() { @@ -357,7 +373,6 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { 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() { From 1cf7af70326372ed2e11cb94f4ec868f32780193 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 10 Aug 2021 22:08:14 +0200 Subject: [PATCH 05/13] wip enum record/tuple generation --- .../replace_derive_with_manual_impl.rs | 2 +- .../src/utils/gen_trait_fn_body.rs | 72 +++++++++++++++++-- crates/syntax/src/ast/expr_ext.rs | 12 ++++ 3 files changed, 78 insertions(+), 8 deletions(-) diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs index d8d62b73b88..7aa99bb937e 100644 --- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs @@ -765,7 +765,7 @@ enum Foo { Baz { quz: String, fez: String, - } + }, } impl PartialEq for Foo { 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 af870f38969..782bc56ab32 100644 --- a/crates/ide_assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide_assists/src/utils/gen_trait_fn_body.rs @@ -346,15 +346,73 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // `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(enum_) => { // => std::mem::discriminant(self) == std::mem::discriminant(other) - let lhs = make::expr_path(make::ext::ident_path("self")); - let lhs = make::expr_call(gen_discriminant(), make::arg_list(Some(lhs))); - let rhs = make::expr_path(make::ext::ident_path("other")); - let rhs = make::expr_call(gen_discriminant(), make::arg_list(Some(rhs))); - let cmp = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs); - make::block_expr(None, Some(cmp)).indent(ast::edit::IndentLevel(1)) + let self_name = make::expr_path(make::ext::ident_path("self")); + let lhs = make::expr_call(gen_discriminant(), make::arg_list(Some(self_name.clone()))); + let other_name = make::expr_path(make::ext::ident_path("other")); + let rhs = make::expr_call(gen_discriminant(), make::arg_list(Some(other_name.clone()))); + let eq_check = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs); + + let mut case_count = 0; + let mut arms = vec![]; + for variant in enum_.variant_list()?.variants() { + case_count += 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 pats = vec![]; + // let mut fields = vec![]; + for field in list.fields() { + // let field_name = field.name()?; + // let pat = make::record_pat(path, pats); + // 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)); + todo!("implement tuple record iteration") + } + Some(ast::FieldList::TupleFieldList(list)) => { + todo!("implement tuple enum iteration") + } + None => continue, + } + } + + if !arms.is_empty() && case_count > arms.len() { + let lhs = make::wildcard_pat().into(); + arms.push(make::match_arm(Some(lhs), None, make::expr_literal("true").into())); + } + + let expr = match arms.len() { + 0 => eq_check, + _ => { + let condition = make::condition(eq_check, None); + + let match_target = make::expr_tuple(vec![self_name, other_name]); + let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); + let match_expr = Some(make::expr_match(match_target, list)); + let then_branch = make::block_expr(None, match_expr); + let then_branch = then_branch.indent(ast::edit::IndentLevel(1)); + + let else_branche = make::expr_literal("false"); + let else_branche = make::block_expr(None, Some(else_branche.into())) + .indent(ast::edit::IndentLevel(1)); + + make::expr_if(condition, then_branch, Some(else_branche.into())) + } + }; + + 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)) => { diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs index b615037b7e3..2dfb0d1ad31 100644 --- a/crates/syntax/src/ast/expr_ext.rs +++ b/crates/syntax/src/ast/expr_ext.rs @@ -160,6 +160,18 @@ pub enum ElseBranch { IfExpr(ast::IfExpr), } +impl From for ElseBranch { + fn from(block_expr: ast::BlockExpr) -> Self { + Self::Block(block_expr) + } +} + +impl From for ElseBranch { + fn from(if_expr: ast::IfExpr) -> Self { + Self::IfExpr(if_expr) + } +} + impl ast::IfExpr { pub fn then_branch(&self) -> Option { self.blocks().next() From fc17eb482d082135c477c18154179dc6acde810b Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 11 Aug 2021 17:15:12 +0200 Subject: [PATCH 06/13] gen PartialEq for Record enums Co-Authored-By: Ryan Levick --- .../replace_derive_with_manual_impl.rs | 10 ++- .../src/utils/gen_trait_fn_body.rs | 62 ++++++++++++++----- 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs index 7aa99bb937e..30d291bff9b 100644 --- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs @@ -752,9 +752,11 @@ enum Foo { bin: String, }, Baz { - quz: String, + qux: String, fez: String, }, + Qux {}, + Bin, } "#, r#" @@ -763,9 +765,11 @@ enum Foo { bin: String, }, Baz { - quz: String, + qux: String, fez: String, }, + Qux {}, + Bin, } impl PartialEq for Foo { @@ -773,7 +777,7 @@ impl PartialEq for Foo { if core::mem::discriminant(self) == core::mem::discriminant(other) { match (self, other) { (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin, - (Self::Baz { qux: l_qux, fez: l_fez }, Self::Bar { qux: r_qux, fez: r_fez }) => l_qux == r_qux && l_fez == r_fez, + (Self::Baz { qux: l_qux, fez: l_fez }, Self::Baz { qux: r_qux, fez: r_fez }) => l_qux == r_qux && l_fez == r_fez, _ => true, } } else { 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 782bc56ab32..814a6ab02bf 100644 --- a/crates/ide_assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide_assists/src/utils/gen_trait_fn_body.rs @@ -361,26 +361,56 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { 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 pats = vec![]; + let mut expr = None; + let mut l_fields = vec![]; + let mut r_fields = vec![]; // let mut fields = vec![]; - for field in list.fields() { - // let field_name = field.name()?; - // let pat = make::record_pat(path, pats); - // 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); + // !! make::record_pat_field{list, etc}; + + for field in list.fields() { + let field_name = field.name()?.to_string(); + + let l_name = &format!("l_{}", field_name); + let pat = make::ext::simple_ident_pat(make::name(&l_name)); + let name_ref = make::name_ref(&field_name); + let field = make::record_pat_field(name_ref, pat.into()); + l_fields.push(field); + + let r_name = &format!("r_{}", field_name); + let pat = make::ext::simple_ident_pat(make::name(&r_name)); + let name_ref = make::name_ref(&field_name); + let field = make::record_pat_field(name_ref, pat.into()); + r_fields.push(field); + + 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_op(ast::BinOp::EqualityTest, lhs, rhs); + expr = match expr { + Some(expr) => { + Some(make::expr_op(ast::BinOp::BooleanAnd, expr, cmp)) + } + None => Some(cmp), + }; + } + let first = make::ext::ident_path("Self"); + let second = make::path_from_text(&variant.name()?.to_string()); + let record_name = make::path_concat(first, second); + let list = make::record_pat_field_list(l_fields); + let l_record = make::record_pat_with_fields(record_name, list); + + let first = make::ext::ident_path("Self"); + let second = make::path_from_text(&variant.name()?.to_string()); + let record_name = make::path_concat(first, second); + let list = make::record_pat_field_list(r_fields); + let r_record = make::record_pat_with_fields(record_name, list); + + let tuple = make::tuple_pat(vec![l_record.into(), r_record.into()]); + if let Some(expr) = expr { + arms.push(make::match_arm(Some(tuple.into()), None, expr)); } - // 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)); - todo!("implement tuple record iteration") } + // todo!("implement tuple record iteration") Some(ast::FieldList::TupleFieldList(list)) => { todo!("implement tuple enum iteration") } From 741e27b41408e42fc14c41f8001eeefb92fbf00b Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 11 Aug 2021 19:40:47 +0200 Subject: [PATCH 07/13] dedup PartialEq for Record enums --- .../src/utils/gen_trait_fn_body.rs | 75 ++++++++++--------- 1 file changed, 38 insertions(+), 37 deletions(-) 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 814a6ab02bf..0db2aa32085 100644 --- a/crates/ide_assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide_assists/src/utils/gen_trait_fn_body.rs @@ -339,6 +339,34 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { fn_name } + fn gen_eq_chain(expr: Option, cmp: ast::Expr) -> Option { + match expr { + Some(expr) => Some(make::expr_op(ast::BinOp::BooleanAnd, expr, 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); + let field = make::record_pat_field(name_ref, pat.into()); + field + } + + fn gen_record_pat( + record_name: ast::Path, + r_fields: Vec, + ) -> ast::RecordPat { + let list = make::record_pat_field_list(r_fields); + make::record_pat_with_fields(record_name, list) + } + + fn gen_variant_path(variant: &ast::Variant) -> Option { + let first = make::ext::ident_path("Self"); + let second = make::path_from_text(&variant.name()?.to_string()); + let record_name = make::path_concat(first, second); + Some(record_name) + } // FIXME: return `None` if the trait carries a generic type; we can only // generate this code `Self` for the time being. @@ -364,52 +392,31 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut expr = None; let mut l_fields = vec![]; let mut r_fields = vec![]; - // let mut fields = vec![]; - - // !! make::record_pat_field{list, etc}; for field in list.fields() { let field_name = field.name()?.to_string(); let l_name = &format!("l_{}", field_name); - let pat = make::ext::simple_ident_pat(make::name(&l_name)); - let name_ref = make::name_ref(&field_name); - let field = make::record_pat_field(name_ref, pat.into()); - l_fields.push(field); + l_fields.push(gen_record_pat_field(&field_name, &l_name)); - let r_name = &format!("r_{}", field_name); - let pat = make::ext::simple_ident_pat(make::name(&r_name)); - let name_ref = make::name_ref(&field_name); - let field = make::record_pat_field(name_ref, pat.into()); - r_fields.push(field); + let r_name = &format!("l_{}", 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_op(ast::BinOp::EqualityTest, lhs, rhs); - expr = match expr { - Some(expr) => { - Some(make::expr_op(ast::BinOp::BooleanAnd, expr, cmp)) - } - None => Some(cmp), - }; + expr = gen_eq_chain(expr, cmp); } - let first = make::ext::ident_path("Self"); - let second = make::path_from_text(&variant.name()?.to_string()); - let record_name = make::path_concat(first, second); - let list = make::record_pat_field_list(l_fields); - let l_record = make::record_pat_with_fields(record_name, list); - - let first = make::ext::ident_path("Self"); - let second = make::path_from_text(&variant.name()?.to_string()); - let record_name = make::path_concat(first, second); - let list = make::record_pat_field_list(r_fields); - let r_record = make::record_pat_with_fields(record_name, list); + let l_record = gen_record_pat(gen_variant_path(&variant)?, l_fields); + let r_record = gen_record_pat(gen_variant_path(&variant)?, r_fields); let tuple = make::tuple_pat(vec![l_record.into(), r_record.into()]); + if let Some(expr) = expr { arms.push(make::match_arm(Some(tuple.into()), None, expr)); } } + // todo!("implement tuple record iteration") Some(ast::FieldList::TupleFieldList(list)) => { todo!("implement tuple enum iteration") @@ -453,10 +460,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { 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), - }; + expr = gen_eq_chain(expr, cmp); } make::block_expr(None, expr).indent(ast::edit::IndentLevel(1)) } @@ -470,10 +474,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { 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), - }; + expr = gen_eq_chain(expr, cmp); } make::block_expr(None, expr).indent(ast::edit::IndentLevel(1)) } From c4e10c81b9d1ed5ab82f07ff467b175b0099a7e8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 11 Aug 2021 20:01:19 +0200 Subject: [PATCH 08/13] gen partialeq for tuple enums --- .../src/utils/gen_trait_fn_body.rs | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) 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 0db2aa32085..3aa4afdce4a 100644 --- a/crates/ide_assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide_assists/src/utils/gen_trait_fn_body.rs @@ -367,6 +367,10 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let record_name = make::path_concat(first, second); Some(record_name) } + + 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. @@ -399,7 +403,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let l_name = &format!("l_{}", field_name); l_fields.push(gen_record_pat_field(&field_name, &l_name)); - let r_name = &format!("l_{}", field_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)); @@ -408,9 +412,9 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { expr = gen_eq_chain(expr, cmp); } - let l_record = gen_record_pat(gen_variant_path(&variant)?, l_fields); - let r_record = gen_record_pat(gen_variant_path(&variant)?, r_fields); - let tuple = make::tuple_pat(vec![l_record.into(), r_record.into()]); + 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)); @@ -419,7 +423,32 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // todo!("implement tuple record iteration") Some(ast::FieldList::TupleFieldList(list)) => { - todo!("implement tuple enum iteration") + 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_op(ast::BinOp::EqualityTest, lhs, 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, } From 6c4a94b03a04f9d4ce1c72c77e95248edabbbbe7 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 11 Aug 2021 20:04:05 +0200 Subject: [PATCH 09/13] finishing touches --- .../ide_assists/src/utils/gen_trait_fn_body.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) 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 3aa4afdce4a..701e763bcea 100644 --- a/crates/ide_assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide_assists/src/utils/gen_trait_fn_body.rs @@ -274,8 +274,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { 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))); - let stmt = make::expr_stmt(expr); - stmt.into() + make::expr_stmt(expr).into() } let body = match adt { @@ -335,8 +334,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let submodule = make::ext::ident_path("mem"); let fn_name = make::ext::ident_path("discriminant"); let fn_name = make::path_concat(submodule, fn_name); - let fn_name = make::expr_path(make::path_concat(root, fn_name)); - fn_name + make::expr_path(make::path_concat(root, fn_name)) } fn gen_eq_chain(expr: Option, cmp: ast::Expr) -> Option { @@ -349,15 +347,11 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { 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); - let field = make::record_pat_field(name_ref, pat.into()); - field + make::record_pat_field(name_ref, pat.into()) } - fn gen_record_pat( - record_name: ast::Path, - r_fields: Vec, - ) -> ast::RecordPat { - let list = make::record_pat_field_list(r_fields); + fn gen_record_pat(record_name: ast::Path, fields: Vec) -> ast::RecordPat { + let list = make::record_pat_field_list(fields); make::record_pat_with_fields(record_name, list) } @@ -371,6 +365,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { 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. From 2fff019b6b965a97cd92bc7551f00c2a3baaab87 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 11 Aug 2021 20:33:13 +0200 Subject: [PATCH 10/13] improve codegen --- .../replace_derive_with_manual_impl.rs | 22 ++++++------------- .../src/utils/gen_trait_fn_body.rs | 20 +++++------------ 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs index 30d291bff9b..d29a312eba5 100644 --- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs @@ -726,13 +726,9 @@ enum Foo { impl PartialEq for Foo { $0fn eq(&self, other: &Self) -> bool { - if core::mem::discriminant(self) == core::mem::discriminant(other) { - match (self, other) { - (Self::Bar(l0), Self::Bar(r0)) => l0 == r0, - _ => true, - } - } else { - false + match (self, other) { + (Self::Bar(l0), Self::Bar(r0)) => l0 == r0, + _ => core::mem::discriminant(self) == core::mem::discriminant(other), } } } @@ -774,14 +770,10 @@ enum Foo { impl PartialEq for Foo { $0fn eq(&self, other: &Self) -> bool { - if core::mem::discriminant(self) == core::mem::discriminant(other) { - match (self, other) { - (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin, - (Self::Baz { qux: l_qux, fez: l_fez }, Self::Baz { qux: r_qux, fez: r_fez }) => l_qux == r_qux && l_fez == r_fez, - _ => true, - } - } else { - false + match (self, other) { + (Self::Bar { bin: l_bin }, Self::Bar { bin: r_bin }) => l_bin == r_bin, + (Self::Baz { qux: l_qux, fez: l_fez }, Self::Baz { qux: r_qux, fez: r_fez }) => l_qux == r_qux && l_fez == r_fez, + _ => core::mem::discriminant(self) == core::mem::discriminant(other), } } } 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 701e763bcea..65d39c370ae 100644 --- a/crates/ide_assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide_assists/src/utils/gen_trait_fn_body.rs @@ -449,27 +449,17 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { } } - if !arms.is_empty() && case_count > arms.len() { - let lhs = make::wildcard_pat().into(); - arms.push(make::match_arm(Some(lhs), None, make::expr_literal("true").into())); - } - let expr = match arms.len() { 0 => eq_check, _ => { - let condition = make::condition(eq_check, None); + if case_count > 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![self_name, other_name]); let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1)); - let match_expr = Some(make::expr_match(match_target, list)); - let then_branch = make::block_expr(None, match_expr); - let then_branch = then_branch.indent(ast::edit::IndentLevel(1)); - - let else_branche = make::expr_literal("false"); - let else_branche = make::block_expr(None, Some(else_branche.into())) - .indent(ast::edit::IndentLevel(1)); - - make::expr_if(condition, then_branch, Some(else_branche.into())) + make::expr_match(match_target, list) } }; From 8ed24029c1312c24942dc8003b05bb5a957f532b Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 11 Aug 2021 23:30:00 +0200 Subject: [PATCH 11/13] rm remaining todo comment --- crates/ide_assists/src/utils/gen_trait_fn_body.rs | 1 - 1 file changed, 1 deletion(-) 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 65d39c370ae..99204bd0676 100644 --- a/crates/ide_assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide_assists/src/utils/gen_trait_fn_body.rs @@ -416,7 +416,6 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { } } - // todo!("implement tuple record iteration") Some(ast::FieldList::TupleFieldList(list)) => { let mut expr = None; let mut l_fields = vec![]; From df19895ba6e8429d7ffae03e95c787fd2d2bd6f8 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 12 Aug 2021 11:50:01 +0200 Subject: [PATCH 12/13] implement feedback from review --- .../src/utils/gen_trait_fn_body.rs | 66 +++++++------------ 1 file changed, 24 insertions(+), 42 deletions(-) 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 99204bd0676..5916e5676e8 100644 --- a/crates/ide_assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide_assists/src/utils/gen_trait_fn_body.rs @@ -38,10 +38,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let list = enum_.variant_list()?; let mut arms = vec![]; for variant in list.variants() { - let name = variant.name()?; - let left = make::ext::ident_path("Self"); - let right = make::ext::ident_path(&format!("{}", name)); - let variant_name = make::path_concat(left, right); + let variant_name = make_variant_path(&variant)?; match variant.field_list() { // => match self { Self::Name { x } => Self::Name { x: x.clone() } } @@ -151,9 +148,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut arms = vec![]; for variant in list.variants() { let name = variant.name()?; - let left = make::ext::ident_path("Self"); - let right = make::ext::ident_path(&format!("{}", name)); - let variant_name = make::path_pat(make::path_concat(left, right)); + let variant_name = make::path_pat(make::path_from_text(&format!("Self::{}", name))); let target = make::expr_path(make::ext::ident_path("f").into()); let fmt_string = make::expr_literal(&(format!("\"{}\"", name))).into(); @@ -226,10 +221,8 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { /// 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() -> ast::Expr { - let trait_name = make::ext::ident_path("Default"); - let method_name = make::ext::ident_path("default"); - let fn_name = make::expr_path(make::path_concat(trait_name, method_name)); - make::expr_call(fn_name, make::arg_list(None)) + let fn_name = make::path_from_text(&"Default::default"); + 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. @@ -283,11 +276,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // => std::mem::discriminant(self).hash(state); ast::Adt::Enum(_) => { - let root = make::ext::ident_path("core"); - let submodule = make::ext::ident_path("mem"); - let fn_name = make::ext::ident_path("discriminant"); - let fn_name = make::path_concat(submodule, fn_name); - let fn_name = make::expr_path(make::path_concat(root, fn_name)); + 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))); @@ -329,14 +318,6 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { /// 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<()> { - fn gen_discriminant() -> ast::Expr { - let root = make::ext::ident_path("core"); - let submodule = make::ext::ident_path("mem"); - let fn_name = make::ext::ident_path("discriminant"); - let fn_name = make::path_concat(submodule, fn_name); - make::expr_path(make::path_concat(root, fn_name)) - } - fn gen_eq_chain(expr: Option, cmp: ast::Expr) -> Option { match expr { Some(expr) => Some(make::expr_op(ast::BinOp::BooleanAnd, expr, cmp)), @@ -355,13 +336,6 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { make::record_pat_with_fields(record_name, list) } - fn gen_variant_path(variant: &ast::Variant) -> Option { - let first = make::ext::ident_path("Self"); - let second = make::path_from_text(&variant.name()?.to_string()); - let record_name = make::path_concat(first, second); - Some(record_name) - } - fn gen_tuple_field(field_name: &String) -> ast::Pat { ast::Pat::IdentPat(make::ident_pat(false, false, make::name(field_name))) } @@ -370,15 +344,15 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // 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. + // `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 self_name = make::expr_path(make::ext::ident_path("self")); - let lhs = make::expr_call(gen_discriminant(), make::arg_list(Some(self_name.clone()))); - let other_name = make::expr_path(make::ext::ident_path("other")); - let rhs = make::expr_call(gen_discriminant(), make::arg_list(Some(other_name.clone()))); + 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_op(ast::BinOp::EqualityTest, lhs, rhs); let mut case_count = 0; @@ -407,8 +381,8 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { 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 left = gen_record_pat(make_variant_path(&variant)?, l_fields); + let right = gen_record_pat(make_variant_path(&variant)?, r_fields); let tuple = make::tuple_pat(vec![left.into(), right.into()]); if let Some(expr) = expr { @@ -436,8 +410,8 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { 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 left = make::tuple_struct_pat(make_variant_path(&variant)?, l_fields); + let right = make::tuple_struct_pat(make_variant_path(&variant)?, r_fields); let tuple = make::tuple_pat(vec![left.into(), right.into()]); if let Some(expr) = expr { @@ -456,7 +430,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { arms.push(make::match_arm(Some(lhs), None, eq_check)); } - let match_target = make::expr_tuple(vec![self_name, other_name]); + 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) } @@ -492,7 +466,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { make::block_expr(None, expr).indent(ast::edit::IndentLevel(1)) } - // No fields in the body means there's nothing to hash. + // 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)) @@ -503,3 +477,11 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { ted::replace(func.body()?.syntax(), body.clone_for_update().syntax()); Some(()) } + +fn make_discriminant() -> ast::Expr { + make::expr_path(make::path_from_text("core::mem::discriminant")) +} + +fn make_variant_path(variant: &ast::Variant) -> Option { + Some(make::path_from_text(&format!("Self::{}", &variant.name()?))) +} From 97ec6a27ecb9bd643903c343397e69fd2c01d77e Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 12 Aug 2021 12:17:09 +0200 Subject: [PATCH 13/13] add `make::ext::path_from_idents` --- .../src/utils/gen_trait_fn_body.rs | 49 ++++++++++--------- crates/syntax/src/ast/make.rs | 12 +++++ 2 files changed, 39 insertions(+), 22 deletions(-) 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 5916e5676e8..111fa0b23fc 100644 --- a/crates/ide_assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide_assists/src/utils/gen_trait_fn_body.rs @@ -38,7 +38,8 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let list = enum_.variant_list()?; let mut arms = vec![]; for variant in list.variants() { - let variant_name = make_variant_path(&variant)?; + 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() } } @@ -148,7 +149,8 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut arms = vec![]; for variant in list.variants() { let name = variant.name()?; - let variant_name = make::path_pat(make::path_from_text(&format!("Self::{}", name))); + let variant_name = + make::path_pat(make::ext::path_from_idents(["Self", &format!("{}", name)])?); let target = make::expr_path(make::ext::ident_path("f").into()); let fmt_string = make::expr_literal(&(format!("\"{}\"", name))).into(); @@ -220,9 +222,9 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { /// 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() -> ast::Expr { - let fn_name = make::path_from_text(&"Default::default"); - make::expr_call(make::expr_path(fn_name), make::arg_list(None)) + fn gen_default_call() -> Option { + 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. @@ -234,7 +236,7 @@ fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { Some(ast::FieldList::RecordFieldList(field_list)) => { let mut fields = vec![]; for field in field_list.fields() { - let method_call = gen_default_call(); + 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); @@ -245,7 +247,10 @@ fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { } 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()); + let fields = field_list + .fields() + .map(|_| gen_default_call()) + .collect::>>()?; make::expr_call(struct_name, make::arg_list(fields)) } None => { @@ -276,7 +281,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // => std::mem::discriminant(self).hash(state); ast::Adt::Enum(_) => { - let fn_name = make_discriminant(); + 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))); @@ -336,6 +341,10 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { make::record_pat_with_fields(record_name, list) } + fn gen_variant_path(variant: &ast::Variant) -> Option { + 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))) } @@ -344,15 +353,15 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // 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. + // `Hash` 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 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 rhs = make::expr_call(make_discriminant()?, make::arg_list(Some(rhs_name.clone()))); let eq_check = make::expr_op(ast::BinOp::EqualityTest, lhs, rhs); let mut case_count = 0; @@ -381,8 +390,8 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { expr = gen_eq_chain(expr, cmp); } - let left = gen_record_pat(make_variant_path(&variant)?, l_fields); - let right = gen_record_pat(make_variant_path(&variant)?, r_fields); + 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 { @@ -410,8 +419,8 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { expr = gen_eq_chain(expr, cmp); } - let left = make::tuple_struct_pat(make_variant_path(&variant)?, l_fields); - let right = make::tuple_struct_pat(make_variant_path(&variant)?, r_fields); + 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 { @@ -466,7 +475,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { make::block_expr(None, expr).indent(ast::edit::IndentLevel(1)) } - // No fields in the body means there's nothing to compare. + // 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)) @@ -478,10 +487,6 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { Some(()) } -fn make_discriminant() -> ast::Expr { - make::expr_path(make::path_from_text("core::mem::discriminant")) -} - -fn make_variant_path(variant: &ast::Variant) -> Option { - Some(make::path_from_text(&format!("Self::{}", &variant.name()?))) +fn make_discriminant() -> Option { + Some(make::expr_path(make::ext::path_from_idents(["core", "mem", "discriminant"])?)) } diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 74f2e5c314c..e6fab72ac05 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -32,6 +32,18 @@ pub mod ext { path_unqualified(path_segment(name_ref(ident))) } + pub fn path_from_idents<'a>( + parts: impl std::iter::IntoIterator, + ) -> Option { + let mut iter = parts.into_iter(); + let base = ext::ident_path(iter.next()?); + let path = iter.fold(base, |base, s| { + let path = ext::ident_path(s); + path_concat(base, path) + }); + Some(path) + } + pub fn expr_unreachable() -> ast::Expr { expr_from_text("unreachable!()") }