From 9a84e66021c1e206e7b1b64ff6de81447ab122c2 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 4 Feb 2026 12:22:42 +0800 Subject: [PATCH] fix: Fix loses associated bounds for replace_derive_with_manual_impl Example --- ```rust #[derive(Clo$0ne)] struct Foo(T::Target); ``` **Before this PR** ```rust struct Foo(T::Target); impl Clone for Foo { $0fn clone(&self) -> Self { Self(self.0.clone()) } } ``` **After this PR** ```rust struct Foo(T::Target); impl Clone for Foo where T::Target: Clone { $0fn clone(&self) -> Self { Self(self.0.clone()) } } ``` --- .../replace_derive_with_manual_impl.rs | 23 ++++++++ .../crates/ide-assists/src/utils.rs | 53 ++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 11b3fd22faa3..7c024263b4a8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -1307,6 +1307,29 @@ impl Clone for Foo { ) } + #[test] + fn add_custom_impl_clone_generic_tuple_struct_with_associated() { + check_assist( + replace_derive_with_manual_impl, + r#" +//- minicore: clone, derive, deref +#[derive(Clo$0ne)] +struct Foo(T::Target); +"#, + r#" +struct Foo(T::Target); + +impl Clone for Foo +where T::Target: Clone +{ + $0fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +"#, + ) + } + #[test] fn test_ignore_derive_macro_without_input() { check_assist_not_applicable( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 4b8c19305793..1732d8c2018e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -15,6 +15,7 @@ use ide_db::{ path_transform::PathTransform, syntax_helpers::{node_ext::preorder_expr, prettify_macro_expansion}, }; +use itertools::Itertools; use stdx::format_to; use syntax::{ AstNode, AstToken, Direction, NodeOrToken, SourceFile, @@ -766,6 +767,11 @@ fn generate_impl_inner( }); let generic_args = generic_params.as_ref().map(|params| params.to_generic_args().clone_for_update()); + let trait_where_clause = trait_ + .as_ref() + .zip(generic_params.as_ref()) + .and_then(|(trait_, params)| generic_param_associated_bounds(adt, trait_, params)); + let ty = make::ty_path(make::ext::ident_path(&adt.name().unwrap().text())); let cfg_attrs = @@ -781,7 +787,7 @@ fn generate_impl_inner( false, trait_, ty, - None, + trait_where_clause, adt.where_clause(), body, ), @@ -790,6 +796,51 @@ fn generate_impl_inner( .clone_for_update() } +fn generic_param_associated_bounds( + adt: &ast::Adt, + trait_: &ast::Type, + generic_params: &ast::GenericParamList, +) -> Option { + let in_type_params = |name: &ast::NameRef| { + generic_params + .generic_params() + .filter_map(|param| match param { + ast::GenericParam::TypeParam(type_param) => type_param.name(), + _ => None, + }) + .any(|param| param.text() == name.text()) + }; + let adt_body = match adt { + ast::Adt::Enum(e) => e.variant_list().map(|it| it.syntax().clone()), + ast::Adt::Struct(s) => s.field_list().map(|it| it.syntax().clone()), + ast::Adt::Union(u) => u.record_field_list().map(|it| it.syntax().clone()), + }; + let mut trait_where_clause = adt_body + .into_iter() + .flat_map(|it| it.descendants()) + .filter_map(ast::Path::cast) + .filter_map(|path| { + let qualifier = path.qualifier()?.as_single_segment()?; + let qualifier = qualifier + .name_ref() + .or_else(|| match qualifier.type_anchor()?.ty()? { + ast::Type::PathType(path_type) => path_type.path()?.as_single_name_ref(), + _ => None, + }) + .filter(in_type_params)?; + Some((qualifier, path.segment()?.name_ref()?)) + }) + .map(|(qualifier, assoc_name)| { + let segments = [qualifier, assoc_name].map(make::path_segment); + let path = make::path_from_segments(segments, false); + let bounds = Some(make::type_bound(trait_.clone())); + make::where_pred(either::Either::Right(make::ty_path(path)), bounds) + }) + .unique_by(|it| it.syntax().to_string()) + .peekable(); + trait_where_clause.peek().is_some().then(|| make::where_clause(trait_where_clause)) +} + pub(crate) fn add_method_to_adt( builder: &mut SourceChangeBuilder, adt: &ast::Adt,