From 0d634d9b1096b5bfb643507af00636683d8435d6 Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Mon, 25 May 2026 21:09:59 +0600 Subject: [PATCH 1/2] fix: generate method on generic type now includes impl generics Fixes rust-lang/rust-analyzer#19806 --- .../src/handlers/generate_function.rs | 105 +++++++++++++++++- 1 file changed, 100 insertions(+), 5 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index 14dd4061e72f..24921dec64b2 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -734,10 +734,25 @@ fn insert_rendered_impl( adt.name(ctx.db()).display(ctx.db(), function_builder.target_edition) ))); - // FIXME: adt may have generic params. + let (adt_generic_params, adt_where_clause) = adt + .source(ctx.sema.db) + .map(|src| match src.value { + ast::Adt::Struct(it) => (it.generic_param_list(), it.where_clause()), + ast::Adt::Union(it) => (it.generic_param_list(), it.where_clause()), + ast::Adt::Enum(it) => (it.generic_param_list(), it.where_clause()), + }) + .unwrap_or_default(); + let generic_args = adt_generic_params.as_ref().map(|p| p.to_generic_args(make)); + let fn_ = function_builder.render(make).indent(IndentLevel(1)); - let impl_ = - make.impl_(None, None, None, name.into(), None, Some(make.assoc_item_list([fn_.into()]))); + let impl_ = make.impl_( + None, + adt_generic_params, + generic_args, + name.into(), + adt_where_clause, + Some(make.assoc_item_list([fn_.into()])), + ); let impl_ = impl_.indent(impl_indent); if let Some(fn_) = impl_.syntax().descendants().find_map(ast::Fn::cast) { add_generated_fn_annotation(editor, edit, function_builder, &fn_, cap); @@ -1901,7 +1916,7 @@ fn bar(t: T, u: U) { #[test] fn generic_param_in_receiver_type() { - // FIXME: Generic parameter `T` should be part of impl, not method. + // FIXME: Generic parameter `T` from the impl should not also appear in the method's generics. check_assist( generate_function, r" @@ -1910,7 +1925,7 @@ fn foo(s: S, u: U) { s.$0foo(u) } ", r" struct S(T); -impl S { +impl S { fn foo(&self, u: U) { ${0:todo!()} } @@ -2565,6 +2580,86 @@ impl Foo { ) } + #[test] + fn create_method_on_generic_struct_no_existing_impl() { + check_assist( + generate_function, + r#" +struct Generic(T); + +fn foo() { + Generic("test").do_a_thing$0() +} +"#, + r#" +struct Generic(T); +impl Generic { + fn do_a_thing(&self) { + ${0:todo!()} + } +} + +fn foo() { + Generic("test").do_a_thing() +} +"#, + ) + } + + #[test] + fn create_method_on_generic_struct_with_where_clause() { + check_assist( + generate_function, + r#" +struct Generic(T) where T: Send; + +fn foo() { + Generic("test").do_a_thing$0() +} +"#, + r#" +struct Generic(T) where T: Send; +impl Generic +where T: Send +{ + fn do_a_thing(&self) { + ${0:todo!()} + } +} + +fn foo() { + Generic("test").do_a_thing() +} +"#, + ) + } + + #[test] + fn create_method_on_generic_enum_no_existing_impl() { + check_assist( + generate_function, + r#" +enum MyEnum { A(T), B } + +fn foo() { + MyEnum::B.do_a_thing$0() +} +"#, + r#" +enum MyEnum { A(T), B } +impl MyEnum { + fn do_a_thing(&self) { + ${0:todo!()} + } +} + +fn foo() { + MyEnum::B.do_a_thing() +} +"#, + ) + } + #[test] fn create_function_with_async() { check_assist( From 47c6cf46c57ff57dc53d30c909caae5c06962b8e Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Tue, 26 May 2026 15:50:06 +0600 Subject: [PATCH 2/2] fix: apply PathTransform to ADT generic params in generate_function --- .../src/handlers/generate_function.rs | 59 ++++++++++++++++++- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index 24921dec64b2..95278c0e3fce 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -581,6 +581,7 @@ impl GeneratedFunctionTarget { function_builder, adt, position, + item, indent, indent, cap, @@ -603,6 +604,7 @@ impl GeneratedFunctionTarget { function_builder, adt, position, + item_list, indent, leading_indent, cap, @@ -723,6 +725,7 @@ fn insert_rendered_impl( function_builder: &FunctionBuilder, adt: Adt, position: Position, + target_node: &SyntaxNode, impl_indent: IndentLevel, leading_ws_indent: IndentLevel, cap: Option, @@ -734,14 +737,34 @@ fn insert_rendered_impl( adt.name(ctx.db()).display(ctx.db(), function_builder.target_edition) ))); - let (adt_generic_params, adt_where_clause) = adt - .source(ctx.sema.db) - .map(|src| match src.value { + let adt_src = adt.source(ctx.sema.db); + let (adt_generic_params, adt_where_clause) = adt_src + .as_ref() + .map(|src| match &src.value { ast::Adt::Struct(it) => (it.generic_param_list(), it.where_clause()), ast::Adt::Union(it) => (it.generic_param_list(), it.where_clause()), ast::Adt::Enum(it) => (it.generic_param_list(), it.where_clause()), }) .unwrap_or_default(); + + let source_scope = adt_src.as_ref().and_then(|src| ctx.sema.scope(src.value.syntax())); + let target_scope = source_scope.as_ref().and_then(|_| ctx.sema.scope(target_node)); + let (adt_generic_params, adt_where_clause) = if let Some(source_scope) = source_scope + && let Some(target_scope) = target_scope + && source_scope.module() != target_scope.module() + { + let transform = PathTransform::generic_transformation(&target_scope, &source_scope); + let transformed_params = adt_generic_params.as_ref().map(|p| { + ast::GenericParamList::cast(transform.apply(p.syntax())).unwrap_or_else(|| p.clone()) + }); + let transformed_where = adt_where_clause.as_ref().map(|w| { + ast::WhereClause::cast(transform.apply(w.syntax())).unwrap_or_else(|| w.clone()) + }); + (transformed_params, transformed_where) + } else { + (adt_generic_params, adt_where_clause) + }; + let generic_args = adt_generic_params.as_ref().map(|p| p.to_generic_args(make)); let fn_ = function_builder.render(make).indent(IndentLevel(1)); @@ -2634,6 +2657,36 @@ fn foo() { ) } + #[test] + fn create_method_on_generic_struct_in_different_module() { + check_assist( + generate_function, + r#" +mod foo { + pub struct Generic(T); +} + +fn bar() { + foo::Generic("test").do_a_thing$0() +} +"#, + r#" +mod foo { + pub struct Generic(T); + impl Generic { + pub(crate) fn do_a_thing(&self) { + ${0:todo!()} + } + } +} + +fn bar() { + foo::Generic("test").do_a_thing() +} +"#, + ) + } + #[test] fn create_method_on_generic_enum_no_existing_impl() { check_assist(