Skip to content

Commit 7040cef

Browse files
committed
Auto merge of #16068 - roife:fix-introduce-named-generic-name-conflicts, r=Veykril
fix: pick up new names when the name conflicts in 'introduce_named_generic' Improve generation of names for generic parameters in `introduce_named_generics`. fix #15731. ### Changes - Modified `for_generic_parameter` function in `suggest_name.rs` to handle conflicts with existing generic parameters and generate unique names accordingly. - Update `introduce_named_generic` function and pass existing params to `for_generic_parameter`, enabling the detection and handling of name collisions.
2 parents f7a29e4 + 919ecc6 commit 7040cef

File tree

2 files changed

+43
-12
lines changed

2 files changed

+43
-12
lines changed

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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,16 @@ pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_>
3131
|edit| {
3232
let impl_trait_type = edit.make_mut(impl_trait_type);
3333
let fn_ = edit.make_mut(fn_);
34-
35-
let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type);
34+
let fn_generic_param_list = fn_.get_or_create_generic_param_list();
35+
let type_param_name =
36+
suggest_name::for_impl_trait_as_generic(&impl_trait_type, &fn_generic_param_list);
3637

3738
let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list))
3839
.clone_for_update();
3940
let new_ty = make::ty(&type_param_name).clone_for_update();
4041

4142
ted::replace(impl_trait_type.syntax(), new_ty.syntax());
42-
fn_.get_or_create_generic_param_list().add_generic_param(type_param.into());
43+
fn_generic_param_list.add_generic_param(type_param.into());
4344

4445
if let Some(cap) = ctx.config.snippet_cap {
4546
if let Some(generic_param) =
@@ -111,12 +112,19 @@ fn foo<$0B: Bar
111112

112113
#[test]
113114
fn replace_impl_trait_with_exist_generic_letter() {
114-
// FIXME: This is wrong, we should pick a different name if the one we
115-
// want is already bound.
116115
check_assist(
117116
introduce_named_generic,
118117
r#"fn foo<B>(bar: $0impl Bar) {}"#,
119-
r#"fn foo<B, $0B: Bar>(bar: B) {}"#,
118+
r#"fn foo<B, $0B0: Bar>(bar: B0) {}"#,
119+
);
120+
}
121+
122+
#[test]
123+
fn replace_impl_trait_with_more_exist_generic_letter() {
124+
check_assist(
125+
introduce_named_generic,
126+
r#"fn foo<B, B0, B1, B3>(bar: $0impl Bar) {}"#,
127+
r#"fn foo<B, B0, B1, B3, $0B2: Bar>(bar: B2) {}"#,
120128
);
121129
}
122130

crates/ide-assists/src/utils/suggest_name.rs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! This module contains functions to suggest names for expressions, functions and other items
22
3+
use std::collections::HashSet;
4+
35
use hir::Semantics;
46
use ide_db::RootDatabase;
57
use itertools::Itertools;
@@ -58,6 +60,14 @@ const USELESS_METHODS: &[&str] = &[
5860
"into_future",
5961
];
6062

63+
/// Suggest a unique name for generic parameter.
64+
///
65+
/// `existing_params` is used to check if the name conflicts with existing
66+
/// generic parameters.
67+
///
68+
/// The function checks if the name conflicts with existing generic parameters.
69+
/// If so, it will try to resolve the conflict by adding a number suffix, e.g.
70+
/// `T`, `T0`, `T1`, ...
6171
pub(crate) fn for_unique_generic_name(
6272
name: &str,
6373
existing_params: &ast::GenericParamList,
@@ -68,12 +78,9 @@ pub(crate) fn for_unique_generic_name(
6878
ast::GenericParam::TypeParam(t) => t.name().unwrap().to_string(),
6979
p => p.to_string(),
7080
})
71-
.collect_vec();
81+
.collect::<HashSet<_>>();
7282
let mut name = name.to_string();
7383
let base_len = name.len();
74-
// 4*len bytes for base, and 2 bytes for 2 digits
75-
name.reserve(4 * base_len + 2);
76-
7784
let mut count = 0;
7885
while param_names.contains(&name) {
7986
name.truncate(base_len);
@@ -84,12 +91,28 @@ pub(crate) fn for_unique_generic_name(
8491
name.into()
8592
}
8693

87-
pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr {
94+
/// Suggest name of impl trait type
95+
///
96+
/// `existing_params` is used to check if the name conflicts with existing
97+
/// generic parameters.
98+
///
99+
/// # Current implementation
100+
///
101+
/// In current implementation, the function tries to get the name from the first
102+
/// character of the name for the first type bound.
103+
///
104+
/// If the name conflicts with existing generic parameters, it will try to
105+
/// resolve the conflict with `for_unique_generic_name`.
106+
pub(crate) fn for_impl_trait_as_generic(
107+
ty: &ast::ImplTraitType,
108+
existing_params: &ast::GenericParamList,
109+
) -> SmolStr {
88110
let c = ty
89111
.type_bound_list()
90112
.and_then(|bounds| bounds.syntax().text().char_at(0.into()))
91113
.unwrap_or('T');
92-
c.encode_utf8(&mut [0; 4]).into()
114+
115+
for_unique_generic_name(c.encode_utf8(&mut [0; 4]), existing_params)
93116
}
94117

95118
/// Suggest name of variable for given expression

0 commit comments

Comments
 (0)