about summary refs log tree commit diff
diff options
context:
space:
mode:
authorWilfred Hughes <me@wilfred.me.uk>2025-01-24 11:25:29 -0800
committerWilfred Hughes <me@wilfred.me.uk>2025-01-24 11:30:19 -0800
commitfff24d52ee84ad49b780443cdbfe9ca38bb85611 (patch)
tree08b393da5353587afd6424562d21e4c05dd9fd06
parent8063b1ec0407935432489d44b21111973044369c (diff)
downloadrust-fff24d52ee84ad49b780443cdbfe9ca38bb85611.tar.gz
rust-fff24d52ee84ad49b780443cdbfe9ca38bb85611.zip
minor: Suggest better names when a type is a sequence
Previously, we'd suggest a type of `vec` for a value of type `Vec<T>`,
which is rarely what the user wants. We also had no suggestions for
values of type `&[T]`.

Instead, try to pluralise the inner type name, and fall back to
`items`.
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs83
2 files changed, 93 insertions, 10 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
index 97321f4ec1e..7b6f76d0045 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
@@ -1672,8 +1672,8 @@ macro_rules! vec {
     () => {Vec}
 }
 fn main() {
-    let $0vec = vec![];
-    let _ = vec;
+    let $0items = vec![];
+    let _ = items;
 }
 "#,
             "Extract into variable",
@@ -1696,8 +1696,8 @@ macro_rules! vec {
     () => {Vec}
 }
 fn main() {
-    const $0VEC: Vec = vec![];
-    let _ = VEC;
+    const $0ITEMS: Vec = vec![];
+    let _ = ITEMS;
 }
 "#,
             "Extract into constant",
@@ -1720,8 +1720,8 @@ macro_rules! vec {
     () => {Vec}
 }
 fn main() {
-    static $0VEC: Vec = vec![];
-    let _ = VEC;
+    static $0ITEMS: Vec = vec![];
+    let _ = ITEMS;
 }
 "#,
             "Extract into static",
@@ -2019,8 +2019,8 @@ impl<T> Vec<T> {
 }
 
 fn foo(s: &mut S) {
-    let $0vec = &mut s.vec;
-    vec.push(0);
+    let $0items = &mut s.vec;
+    items.push(0);
 }"#,
             "Extract into variable",
         );
@@ -2106,8 +2106,8 @@ impl<T> Vec<T> {
 }
 
 fn foo(f: &mut Y) {
-    let $0vec = &mut f.field.field.vec;
-    vec.push(0);
+    let $0items = &mut f.field.field.vec;
+    items.push(0);
 }"#,
             "Extract into variable",
         );
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs
index 557c95f704b..0a7141c19b6 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs
@@ -31,6 +31,12 @@ const USELESS_NAME_PREFIXES: &[&str] = &["from_", "with_", "into_"];
 /// `Result<User, Error>` -> `User`
 const WRAPPER_TYPES: &[&str] = &["Box", "Arc", "Rc", "Option", "Result"];
 
+/// Generic types replaced by a plural of their first argument.
+///
+/// # Examples
+/// `Vec<Name>` -> "names"
+const SEQUENCE_TYPES: &[&str] = &["Vec", "VecDeque", "LinkedList"];
+
 /// Prefixes to strip from methods names
 ///
 /// # Examples
@@ -378,6 +384,11 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<S
             return name_of_type(&inner_ty, db, edition);
         }
 
+        if SEQUENCE_TYPES.contains(&name.as_str()) {
+            let inner_ty = ty.type_arguments().next();
+            return Some(sequence_name(inner_ty.as_ref(), db, edition));
+        }
+
         name
     } else if let Some(trait_) = ty.as_dyn_trait() {
         trait_name(&trait_, db, edition)?
@@ -390,12 +401,32 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<S
         name
     } else if let Some(inner_ty) = ty.remove_ref() {
         return name_of_type(&inner_ty, db, edition);
+    } else if let Some(inner_ty) = ty.as_slice() {
+        return Some(sequence_name(Some(&inner_ty), db, edition));
     } else {
         return None;
     };
     normalize(&name)
 }
 
+fn sequence_name(inner_ty: Option<&hir::Type>, db: &RootDatabase, edition: Edition) -> SmolStr {
+    let items_str = SmolStr::new_static("items");
+    let Some(inner_ty) = inner_ty else {
+        return items_str;
+    };
+    let Some(name) = name_of_type(inner_ty, db, edition) else {
+        return items_str;
+    };
+
+    if name.ends_with(['s', 'x', 'y']) {
+        // Given a type called e.g. "Boss", "Fox" or "Story", don't try to
+        // create a plural.
+        items_str
+    } else {
+        SmolStr::new(format!("{name}s"))
+    }
+}
+
 fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Option<String> {
     let name = trait_.name(db).display(db, edition).to_string();
     if USELESS_TRAITS.contains(&name.as_str()) {
@@ -898,6 +929,58 @@ fn foo() { $0(bar())$0; }
     }
 
     #[test]
+    fn vec_value() {
+        check(
+            r#"
+struct Vec<T> {};
+struct Seed;
+fn bar() -> Vec<Seed> {}
+fn foo() { $0(bar())$0; }
+"#,
+            "seeds",
+        );
+    }
+
+    #[test]
+    fn vec_value_ends_with_s() {
+        check(
+            r#"
+struct Vec<T> {};
+struct Boss;
+fn bar() -> Vec<Boss> {}
+fn foo() { $0(bar())$0; }
+"#,
+            "items",
+        );
+    }
+
+    #[test]
+    fn vecdeque_value() {
+        check(
+            r#"
+struct VecDeque<T> {};
+struct Seed;
+fn bar() -> VecDeque<Seed> {}
+fn foo() { $0(bar())$0; }
+"#,
+            "seeds",
+        );
+    }
+
+    #[test]
+    fn slice_value() {
+        check(
+            r#"
+struct Vec<T> {};
+struct Seed;
+fn bar() -> &[Seed] {}
+fn foo() { $0(bar())$0; }
+"#,
+            "seeds",
+        );
+    }
+
+    #[test]
     fn ref_call() {
         check(
             r#"