From 186553dab81c0b675cf41162c528c758e558d0ec Mon Sep 17 00:00:00 2001 From: roife Date: Sat, 9 Dec 2023 20:00:13 +0800 Subject: [PATCH] refactor: extracted the fn handling conflicts in generics and add docs * Extracted the function `for_unique_generic_name` that handling generics with identical names for reusability. * Renamed `for_generic_params` to `for_impl_trait_as_generic` for clarity * Added documentations for `for_impl_trait_as_generic` and `for_unique_generic_name` --- .../src/handlers/introduce_named_generic.rs | 2 +- crates/ide-assists/src/utils/suggest_name.rs | 72 +++++++++++-------- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/crates/ide-assists/src/handlers/introduce_named_generic.rs b/crates/ide-assists/src/handlers/introduce_named_generic.rs index abbdc5aee9..b1daa7802e 100644 --- a/crates/ide-assists/src/handlers/introduce_named_generic.rs +++ b/crates/ide-assists/src/handlers/introduce_named_generic.rs @@ -33,7 +33,7 @@ pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_> let fn_ = edit.make_mut(fn_); 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); + suggest_name::for_impl_trait_as_generic(&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(); diff --git a/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-assists/src/utils/suggest_name.rs index ae3fd30b65..8a698e4068 100644 --- a/crates/ide-assists/src/utils/suggest_name.rs +++ b/crates/ide-assists/src/utils/suggest_name.rs @@ -58,7 +58,48 @@ const USELESS_METHODS: &[&str] = &[ "into_future", ]; -pub(crate) fn for_generic_parameter( +/// Suggest a unique name for generic parameter. +/// +/// `existing_params` is used to check if the name conflicts with existing +/// generic parameters. +/// +/// The function checks if the name conflicts with existing generic parameters. +/// If so, it will try to resolve the conflict by adding a number suffix, e.g. +/// `T`, `T0`, `T1`, ... +pub(crate) fn for_unique_generic_name( + name: &str, + existing_params: &ast::GenericParamList, +) -> SmolStr { + let param_names = existing_params.generic_params().map(|param| param.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() +} + +/// Suggest name of impl trait type +/// +/// `existing_params` is used to check if the name conflicts with existing +/// generic parameters. +/// +/// # Current implementation +/// +/// In current implementation, the function tries to get the name from the first +/// character of the name for the first type bound. +/// +/// If the name conflicts with existing generic parameters, it will try to +/// resolve the conflict with `for_unique_generic_name`. +pub(crate) fn for_impl_trait_as_generic( ty: &ast::ImplTraitType, existing_params: &ast::GenericParamList, ) -> SmolStr { @@ -67,34 +108,7 @@ pub(crate) fn for_generic_parameter( .and_then(|bounds| bounds.syntax().text().char_at(0.into())) .unwrap_or('T'); - // 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() - } + for_unique_generic_name(c.encode_utf8(&mut [0; 4]), existing_params) } /// Suggest name of variable for given expression