about summary refs log tree commit diff
diff options
context:
space:
mode:
authorroife <roifewu@gmail.com>2023-12-09 17:01:30 +0800
committerroife <roifewu@gmail.com>2024-01-02 21:30:55 +0800
commite8dc8ccc593e9972fc11c6d007083c0ab588a440 (patch)
tree396d8ae77ea366e3380b3a0ffdd157feeeda387a
parent34df29620aee42179bdfec13cc221460241f27d4 (diff)
downloadrust-e8dc8ccc593e9972fc11c6d007083c0ab588a440.tar.gz
rust-e8dc8ccc593e9972fc11c6d007083c0ab588a440.zip
fix: pick up new names when the name exists in 'introduce_named_generic'
-rw-r--r--crates/ide-assists/src/handlers/introduce_named_generic.rs20
-rw-r--r--crates/ide-assists/src/utils/suggest_name.rs57
2 files changed, 45 insertions, 32 deletions
diff --git a/crates/ide-assists/src/handlers/introduce_named_generic.rs b/crates/ide-assists/src/handlers/introduce_named_generic.rs
index e90a1ed79b4..abbdc5aee95 100644
--- a/crates/ide-assists/src/handlers/introduce_named_generic.rs
+++ b/crates/ide-assists/src/handlers/introduce_named_generic.rs
@@ -31,15 +31,16 @@ pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_>
         |edit| {
             let impl_trait_type = edit.make_mut(impl_trait_type);
             let fn_ = edit.make_mut(fn_);
-
-            let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type);
+            let fn_generic_param_list = fn_.get_or_create_generic_param_list();
+            let type_param_name =
+                suggest_name::for_generic_parameter(&impl_trait_type, &fn_generic_param_list);
 
             let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list))
                 .clone_for_update();
             let new_ty = make::ty(&type_param_name).clone_for_update();
 
             ted::replace(impl_trait_type.syntax(), new_ty.syntax());
-            fn_.get_or_create_generic_param_list().add_generic_param(type_param.into());
+            fn_generic_param_list.add_generic_param(type_param.into());
 
             if let Some(cap) = ctx.config.snippet_cap {
                 if let Some(generic_param) =
@@ -111,12 +112,19 @@ fn foo<$0B: Bar
 
     #[test]
     fn replace_impl_trait_with_exist_generic_letter() {
-        // FIXME: This is wrong, we should pick a different name if the one we
-        // want is already bound.
         check_assist(
             introduce_named_generic,
             r#"fn foo<B>(bar: $0impl Bar) {}"#,
-            r#"fn foo<B, $0B: Bar>(bar: B) {}"#,
+            r#"fn foo<B, $0B0: Bar>(bar: B0) {}"#,
+        );
+    }
+
+    #[test]
+    fn replace_impl_trait_with_more_exist_generic_letter() {
+        check_assist(
+            introduce_named_generic,
+            r#"fn foo<B, B0, B1, B3>(bar: $0impl Bar) {}"#,
+            r#"fn foo<B, B0, B1, B3, $0B2: Bar>(bar: B2) {}"#,
         );
     }
 
diff --git a/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-assists/src/utils/suggest_name.rs
index 05d38117d43..ae3fd30b65c 100644
--- a/crates/ide-assists/src/utils/suggest_name.rs
+++ b/crates/ide-assists/src/utils/suggest_name.rs
@@ -58,38 +58,43 @@ const USELESS_METHODS: &[&str] = &[
     "into_future",
 ];
 
-pub(crate) fn for_unique_generic_name(
-    name: &str,
+pub(crate) fn for_generic_parameter(
+    ty: &ast::ImplTraitType,
     existing_params: &ast::GenericParamList,
 ) -> SmolStr {
-    let param_names = existing_params
-        .generic_params()
-        .map(|param| match param {
-            ast::GenericParam::TypeParam(t) => t.name().unwrap().to_string(),
-            p => p.to_string(),
-        })
-        .collect_vec();
-    let mut name = name.to_string();
-    let base_len = name.len();
-    // 4*len bytes for base, and 2 bytes for 2 digits
-    name.reserve(4 * base_len + 2);
-
-    let mut count = 0;
-    while param_names.contains(&name) {
-        name.truncate(base_len);
-        name.push_str(&count.to_string());
-        count += 1;
-    }
-
-    name.into()
-}
-
-pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr {
     let c = ty
         .type_bound_list()
         .and_then(|bounds| bounds.syntax().text().char_at(0.into()))
         .unwrap_or('T');
-    c.encode_utf8(&mut [0; 4]).into()
+
+    // let existing_params = existing_params.generic_params();
+    let conflict = existing_params
+        .generic_params()
+        .filter(|param| {
+            param.syntax().text_range().len() == 1.into()
+                && param.syntax().text().char_at(0.into()).unwrap() == c
+        })
+        .count()
+        > 0;
+
+    let buffer = &mut [0; 4];
+    if conflict {
+        let mut name = String::from(c.encode_utf8(buffer));
+        name.reserve(6); // 4B for c, and 2B for 2 digits
+        let base_len = name.len();
+        let mut count = 0;
+        loop {
+            name.truncate(base_len);
+            name.push_str(&count.to_string());
+            if existing_params.generic_params().all(|param| param.to_string() != name) {
+                break;
+            }
+            count += 1;
+        }
+        SmolStr::from(name)
+    } else {
+        c.encode_utf8(buffer).into()
+    }
 }
 
 /// Suggest name of variable for given expression