Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22519 from A4-Tacks/gen-trait-from-impl-gen-args
fix: no generate unused generic params in trait sign
| -rw-r--r-- | crates/ide-assists/src/handlers/generate_trait_from_impl.rs | 117 |
1 files changed, 114 insertions, 3 deletions
diff --git a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index 2493ba6632..12afd9ae6a 100644 --- a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs @@ -1,5 +1,5 @@ use crate::assist_context::{AssistContext, Assists}; -use ide_db::assists::AssistId; +use ide_db::{assists::AssistId, defs::Definition, search::SearchScope}; use syntax::{ AstNode, AstToken, SyntaxKind, T, ast::{ @@ -114,10 +114,11 @@ pub(crate) fn generate_trait_from_impl( let editor = builder.make_editor(impl_ast.syntax()); let make = editor.make(); + let params = used_params(&impl_ast, make, ctx); let trait_ast = make.trait_( false, &trait_name(&impl_assoc_items, make).text(), - impl_ast.generic_param_list(), + params.clone(), impl_ast.where_clause(), trait_items, ); @@ -133,7 +134,7 @@ pub(crate) fn generate_trait_from_impl( make.whitespace(" ").into(), ]; - if let Some(params) = impl_ast.generic_param_list() { + if let Some(params) = params { let gen_args = ¶ms.to_generic_args(make); elements.insert(1, gen_args.syntax().clone().into()); } @@ -167,6 +168,35 @@ pub(crate) fn generate_trait_from_impl( Some(()) } +fn used_params( + impl_ast: &ast::Impl, + make: &SyntaxFactory, + ctx: &AssistContext<'_, '_>, +) -> Option<ast::GenericParamList> { + let impl_only_ranges = impl_ast + .assoc_item_list() + .into_iter() + .flat_map(|list| list.assoc_items()) + .filter_map(|item| match item { + ast::AssocItem::Fn(f) => Some(f.body()?.syntax().text_range()), + _ => None, + }) + .chain(impl_ast.self_ty().map(|it| it.syntax().text_range())) + .collect::<Vec<_>>(); + let used_in_impl = |param: &ast::GenericParam| { + let Some(def) = ctx.sema.to_def(param) else { return true }; + Definition::GenericParam(def) + .usages(&ctx.sema) + .in_scope(&SearchScope::single_file(ctx.file_id())) + .all() + .file_ranges() + .any(|it| !impl_only_ranges.iter().any(|range| range.contains_range(it.range))) + }; + let params = impl_ast.generic_param_list()?; + let mut params = params.generic_params().filter(used_in_impl).peekable(); + params.peek().is_some().then(|| make.generic_param_list(params)) +} + fn trait_name(items: &ast::AssocItemList, make: &SyntaxFactory) -> ast::Name { let mut fn_names = items .assoc_items() @@ -392,6 +422,87 @@ impl<const N: usize> NewTrait<N> for Foo<N> { } #[test] + fn test_impl_with_generics_only_used_in_trait() { + check_assist_no_snippet_cap( + generate_trait_from_impl, + r#" +struct Foo<T, const N: usize>([T; N]); + +impl<T, const N: usize> F$0oo<T, N> { + fn spec_len(&self) -> usize { + N + } +} + "#, + r#" +struct Foo<T, const N: usize>([T; N]); + +trait SpecLen { + fn spec_len(&self) -> usize; +} + +impl<T, const N: usize> SpecLen for Foo<T, N> { + fn spec_len(&self) -> usize { + N + } +} + "#, + ); + + check_assist_no_snippet_cap( + generate_trait_from_impl, + r#" +struct Foo<T, const N: usize>([T; N]); + +impl<T, const N: usize> F$0oo<T, N> { + fn spec_len(&self, other: [T; N]) -> usize { + 0 + } +} + "#, + r#" +struct Foo<T, const N: usize>([T; N]); + +trait SpecLen<T, const N: usize> { + fn spec_len(&self, other: [T; N]) -> usize; +} + +impl<T, const N: usize> SpecLen<T, N> for Foo<T, N> { + fn spec_len(&self, other: [T; N]) -> usize { + 0 + } +} + "#, + ); + + check_assist_no_snippet_cap( + generate_trait_from_impl, + r#" +struct Foo<T, const N: usize>([T; N]); + +impl<T, const N: usize> F$0oo<T, N> where T: Copy { + fn spec_len(&self) -> usize { + 0 + } +} + "#, + r#" +struct Foo<T, const N: usize>([T; N]); + +trait SpecLen<T> where T: Copy { + fn spec_len(&self) -> usize; +} + +impl<T, const N: usize> SpecLen<T> for Foo<T, N> where T: Copy { + fn spec_len(&self) -> usize { + 0 + } +} + "#, + ); + } + + #[test] fn test_trait_items_should_not_have_vis() { check_assist_no_snippet_cap( generate_trait_from_impl, |