diff options
| author | Wilfred Hughes <me@wilfred.me.uk> | 2025-01-24 11:25:29 -0800 |
|---|---|---|
| committer | Wilfred Hughes <me@wilfred.me.uk> | 2025-01-24 11:30:19 -0800 |
| commit | fff24d52ee84ad49b780443cdbfe9ca38bb85611 (patch) | |
| tree | 08b393da5353587afd6424562d21e4c05dd9fd06 | |
| parent | 8063b1ec0407935432489d44b21111973044369c (diff) | |
| download | rust-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.rs | 20 | ||||
| -rw-r--r-- | src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs | 83 |
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#" |
