Skip to content

Commit aadee11

Browse files
authored
Merge pull request rust-lang#22519 from A4-Tacks/gen-trait-from-impl-gen-args
fix: no generate unused generic params in trait sign
2 parents 1854319 + 832c8c2 commit aadee11

1 file changed

Lines changed: 114 additions & 3 deletions

File tree

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

Lines changed: 114 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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::{
44
AstNode, AstToken, SyntaxKind, T,
55
ast::{
@@ -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,35 @@ 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 impl_only_ranges = impl_ast
177+
.assoc_item_list()
178+
.into_iter()
179+
.flat_map(|list| list.assoc_items())
180+
.filter_map(|item| match item {
181+
ast::AssocItem::Fn(f) => Some(f.body()?.syntax().text_range()),
182+
_ => None,
183+
})
184+
.chain(impl_ast.self_ty().map(|it| it.syntax().text_range()))
185+
.collect::<Vec<_>>();
186+
let used_in_impl = |param: &ast::GenericParam| {
187+
let Some(def) = ctx.sema.to_def(param) else { return true };
188+
Definition::GenericParam(def)
189+
.usages(&ctx.sema)
190+
.in_scope(&SearchScope::single_file(ctx.file_id()))
191+
.all()
192+
.file_ranges()
193+
.any(|it| !impl_only_ranges.iter().any(|range| range.contains_range(it.range)))
194+
};
195+
let params = impl_ast.generic_param_list()?;
196+
let mut params = params.generic_params().filter(used_in_impl).peekable();
197+
params.peek().is_some().then(|| make.generic_param_list(params))
198+
}
199+
170200
fn trait_name(items: &ast::AssocItemList, make: &SyntaxFactory) -> ast::Name {
171201
let mut fn_names = items
172202
.assoc_items()
@@ -391,6 +421,87 @@ impl<const N: usize> NewTrait<N> for Foo<N> {
391421
)
392422
}
393423

424+
#[test]
425+
fn test_impl_with_generics_only_used_in_trait() {
426+
check_assist_no_snippet_cap(
427+
generate_trait_from_impl,
428+
r#"
429+
struct Foo<T, const N: usize>([T; N]);
430+
431+
impl<T, const N: usize> F$0oo<T, N> {
432+
fn spec_len(&self) -> usize {
433+
N
434+
}
435+
}
436+
"#,
437+
r#"
438+
struct Foo<T, const N: usize>([T; N]);
439+
440+
trait SpecLen {
441+
fn spec_len(&self) -> usize;
442+
}
443+
444+
impl<T, const N: usize> SpecLen for Foo<T, N> {
445+
fn spec_len(&self) -> usize {
446+
N
447+
}
448+
}
449+
"#,
450+
);
451+
452+
check_assist_no_snippet_cap(
453+
generate_trait_from_impl,
454+
r#"
455+
struct Foo<T, const N: usize>([T; N]);
456+
457+
impl<T, const N: usize> F$0oo<T, N> {
458+
fn spec_len(&self, other: [T; N]) -> usize {
459+
0
460+
}
461+
}
462+
"#,
463+
r#"
464+
struct Foo<T, const N: usize>([T; N]);
465+
466+
trait SpecLen<T, const N: usize> {
467+
fn spec_len(&self, other: [T; N]) -> usize;
468+
}
469+
470+
impl<T, const N: usize> SpecLen<T, N> for Foo<T, N> {
471+
fn spec_len(&self, other: [T; N]) -> usize {
472+
0
473+
}
474+
}
475+
"#,
476+
);
477+
478+
check_assist_no_snippet_cap(
479+
generate_trait_from_impl,
480+
r#"
481+
struct Foo<T, const N: usize>([T; N]);
482+
483+
impl<T, const N: usize> F$0oo<T, N> where T: Copy {
484+
fn spec_len(&self) -> usize {
485+
0
486+
}
487+
}
488+
"#,
489+
r#"
490+
struct Foo<T, const N: usize>([T; N]);
491+
492+
trait SpecLen<T> where T: Copy {
493+
fn spec_len(&self) -> usize;
494+
}
495+
496+
impl<T, const N: usize> SpecLen<T> for Foo<T, N> where T: Copy {
497+
fn spec_len(&self) -> usize {
498+
0
499+
}
500+
}
501+
"#,
502+
);
503+
}
504+
394505
#[test]
395506
fn test_trait_items_should_not_have_vis() {
396507
check_assist_no_snippet_cap(

0 commit comments

Comments
 (0)