diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index a9e2b526394..3a17035ae85 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -2732,8 +2732,8 @@ fn foo() { file_id: FileId( 1, ), - full_range: 252..434, - focus_range: 291..297, + full_range: 253..435, + focus_range: 292..298, name: "Future", kind: Trait, description: "pub trait Future", 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 1cd9ab222c5..bd0b2028a1a 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 @@ -370,6 +370,56 @@ impl Default for Foo { ) } + #[test] + fn add_custom_impl_hash_record_struct() { + check_assist( + replace_derive_with_manual_impl, + r#" +//- minicore: hash +#[derive(Has$0h)] +struct Foo { + bin: usize, + bar: usize, +} +"#, + r#" +struct Foo { + bin: usize, + bar: usize, +} + +impl core::hash::Hash for Foo { + $0fn hash(&self, state: &mut H) { + self.bin.hash(state); + self.bar.hash(state); + } +} +"#, + ) + } + + #[test] + fn add_custom_impl_hash_tuple_struct() { + check_assist( + replace_derive_with_manual_impl, + r#" +//- minicore: hash +#[derive(Has$0h)] +struct Foo(usize, usize); +"#, + r#" +struct Foo(usize, usize); + +impl core::hash::Hash for Foo { + $0fn hash(&self, state: &mut H) { + self.0.hash(state); + self.1.hash(state); + } +} +"#, + ) + } + #[test] fn add_custom_impl_hash_enum() { check_assist( 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 2908f62dd30..9ed8cbdbc73 100644 --- a/crates/ide_assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide_assists/src/utils/gen_trait_fn_body.rs @@ -155,6 +155,14 @@ fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { /// 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<()> { + 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))); + let stmt = make::expr_stmt(expr); + stmt.into() + } + let body = match adt { // `Hash` cannot be derived for unions, so no default impl can be provided. ast::Adt::Union(_) => return None, @@ -169,29 +177,35 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { 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); - let method = make::name_ref("hash"); - let arg = make::expr_path(make::ext::ident_path("state")); - let expr = make::expr_method_call(fn_call, method, make::arg_list(Some(arg))); - let stmt = make::expr_stmt(expr); - - make::block_expr(Some(stmt.into()), None).indent(ast::edit::IndentLevel(1)) + make::block_expr(Some(stmt), None).indent(ast::edit::IndentLevel(1)) } ast::Adt::Struct(strukt) => match strukt.field_list() { // => self..hash(state);* Some(ast::FieldList::RecordFieldList(field_list)) => { - // let mut stmts = vec![]; - for field in field_list.fields() {} - todo!(); + 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..hash(state);* Some(ast::FieldList::TupleFieldList(field_list)) => { - todo!(); + 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 => make::ext::empty_block_expr(), + None => return None, }, };