Skip to content

Commit b5bad9e

Browse files
committed
fix: no generate unused generic params in trait sign
Example --- ```rust struct Foo<T, const N: usize>([T; N]); impl<T, const N: usize> F$0oo<T, N> { fn spec_len(&self) -> usize { N } } ``` **Before this PR** ```rust struct Foo<T, const N: usize>([T; N]); trait SpecLen<T, const N: usize> { fn spec_len(&self) -> usize; } impl<T, const N: usize> SpecLen<T, N> for Foo<T, N> { fn spec_len(&self) -> usize { N } } ``` **After this PR** ```rust 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 } } ```
1 parent 123c166 commit b5bad9e

1 file changed

Lines changed: 96 additions & 4 deletions

File tree

crates/ide-assists/src/handlers/generate_trait_from_impl.rs

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::assist_context::{AssistContext, Assists};
2-
use ide_db::assists::AssistId;
2+
use ide_db::{assists::AssistId, defs::Definition, search::SearchScope};
33
use syntax::{
4-
AstNode, AstToken, SyntaxKind, T,
4+
AstNode, AstToken, SyntaxKind, T, TextRange,
55
ast::{
66
self, HasDocComments, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit,
77
syntax_factory::SyntaxFactory,
@@ -114,10 +114,11 @@ pub(crate) fn generate_trait_from_impl(
114114

115115
let editor = builder.make_editor(impl_ast.syntax());
116116
let make = editor.make();
117+
let params = used_params(&impl_ast, make, ctx);
117118
let trait_ast = make.trait_(
118119
false,
119120
&trait_name(&impl_assoc_items, make).text(),
120-
impl_ast.generic_param_list(),
121+
params.clone(),
121122
impl_ast.where_clause(),
122123
trait_items,
123124
);
@@ -133,7 +134,7 @@ pub(crate) fn generate_trait_from_impl(
133134
make.whitespace(" ").into(),
134135
];
135136

136-
if let Some(params) = impl_ast.generic_param_list() {
137+
if let Some(params) = params {
137138
let gen_args = &params.to_generic_args(make);
138139
elements.insert(1, gen_args.syntax().clone().into());
139140
}
@@ -167,6 +168,42 @@ pub(crate) fn generate_trait_from_impl(
167168
Some(())
168169
}
169170

171+
fn used_params(
172+
impl_ast: &ast::Impl,
173+
make: &SyntaxFactory,
174+
ctx: &AssistContext<'_, '_>,
175+
) -> Option<ast::GenericParamList> {
176+
let item_in_trait_range = |item| match item {
177+
ast::AssocItem::Const(_) | ast::AssocItem::MacroCall(_) | ast::AssocItem::TypeAlias(_) => {
178+
item.syntax().text_range()
179+
}
180+
ast::AssocItem::Fn(fn_item) => {
181+
let range = fn_item.syntax().text_range();
182+
fn_item.body().map_or(range, |body| {
183+
TextRange::new(range.start(), body.syntax().text_range().start())
184+
})
185+
}
186+
};
187+
let trait_ranges = impl_ast
188+
.assoc_item_list()
189+
.into_iter()
190+
.flat_map(|list| list.assoc_items())
191+
.map(item_in_trait_range)
192+
.collect::<Vec<_>>();
193+
let used_in_impl = |param: &ast::GenericParam| {
194+
let Some(def) = ctx.sema.to_def(param) else { return true };
195+
Definition::GenericParam(def)
196+
.usages(&ctx.sema)
197+
.in_scope(&SearchScope::single_file(ctx.file_id()))
198+
.all()
199+
.file_ranges()
200+
.any(|it| trait_ranges.iter().any(|range| range.contains_range(it.range)))
201+
};
202+
let params = impl_ast.generic_param_list()?;
203+
let mut params = params.generic_params().filter(used_in_impl).peekable();
204+
params.peek().is_some().then(|| make.generic_param_list(params))
205+
}
206+
170207
fn trait_name(items: &ast::AssocItemList, make: &SyntaxFactory) -> ast::Name {
171208
let mut fn_names = items
172209
.assoc_items()
@@ -391,6 +428,61 @@ impl<const N: usize> NewTrait<N> for Foo<N> {
391428
)
392429
}
393430

431+
#[test]
432+
fn test_impl_with_generics_only_used_in_trait() {
433+
check_assist_no_snippet_cap(
434+
generate_trait_from_impl,
435+
r#"
436+
struct Foo<T, const N: usize>([T; N]);
437+
438+
impl<T, const N: usize> F$0oo<T, N> {
439+
fn spec_len(&self) -> usize {
440+
N
441+
}
442+
}
443+
"#,
444+
r#"
445+
struct Foo<T, const N: usize>([T; N]);
446+
447+
trait SpecLen {
448+
fn spec_len(&self) -> usize;
449+
}
450+
451+
impl<T, const N: usize> SpecLen for Foo<T, N> {
452+
fn spec_len(&self) -> usize {
453+
N
454+
}
455+
}
456+
"#,
457+
);
458+
459+
check_assist_no_snippet_cap(
460+
generate_trait_from_impl,
461+
r#"
462+
struct Foo<T, const N: usize>([T; N]);
463+
464+
impl<T, const N: usize> F$0oo<T, N> {
465+
fn spec_len(&self, other: [T; N]) -> usize {
466+
0
467+
}
468+
}
469+
"#,
470+
r#"
471+
struct Foo<T, const N: usize>([T; N]);
472+
473+
trait SpecLen<T, const N: usize> {
474+
fn spec_len(&self, other: [T; N]) -> usize;
475+
}
476+
477+
impl<T, const N: usize> SpecLen<T, N> for Foo<T, N> {
478+
fn spec_len(&self, other: [T; N]) -> usize {
479+
0
480+
}
481+
}
482+
"#,
483+
);
484+
}
485+
394486
#[test]
395487
fn test_trait_items_should_not_have_vis() {
396488
check_assist_no_snippet_cap(

0 commit comments

Comments
 (0)