Merge pull request #19023 from Wilfred/sequence_type_names

minor: Suggest better names when a type is a sequence
This commit is contained in:
Lukas Wirth 2025-01-25 08:13:42 +00:00 committed by GitHub
commit 914da2ece3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 93 additions and 10 deletions

View File

@ -1672,8 +1672,8 @@ macro_rules! vec {
() => {Vec} () => {Vec}
} }
fn main() { fn main() {
let $0vec = vec![]; let $0items = vec![];
let _ = vec; let _ = items;
} }
"#, "#,
"Extract into variable", "Extract into variable",
@ -1696,8 +1696,8 @@ macro_rules! vec {
() => {Vec} () => {Vec}
} }
fn main() { fn main() {
const $0VEC: Vec = vec![]; const $0ITEMS: Vec = vec![];
let _ = VEC; let _ = ITEMS;
} }
"#, "#,
"Extract into constant", "Extract into constant",
@ -1720,8 +1720,8 @@ macro_rules! vec {
() => {Vec} () => {Vec}
} }
fn main() { fn main() {
static $0VEC: Vec = vec![]; static $0ITEMS: Vec = vec![];
let _ = VEC; let _ = ITEMS;
} }
"#, "#,
"Extract into static", "Extract into static",
@ -2019,8 +2019,8 @@ impl<T> Vec<T> {
} }
fn foo(s: &mut S) { fn foo(s: &mut S) {
let $0vec = &mut s.vec; let $0items = &mut s.vec;
vec.push(0); items.push(0);
}"#, }"#,
"Extract into variable", "Extract into variable",
); );
@ -2106,8 +2106,8 @@ impl<T> Vec<T> {
} }
fn foo(f: &mut Y) { fn foo(f: &mut Y) {
let $0vec = &mut f.field.field.vec; let $0items = &mut f.field.field.vec;
vec.push(0); items.push(0);
}"#, }"#,
"Extract into variable", "Extract into variable",
); );

View File

@ -31,6 +31,12 @@ const USELESS_NAME_PREFIXES: &[&str] = &["from_", "with_", "into_"];
/// `Result<User, Error>` -> `User` /// `Result<User, Error>` -> `User`
const WRAPPER_TYPES: &[&str] = &["Box", "Arc", "Rc", "Option", "Result"]; 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 /// Prefixes to strip from methods names
/// ///
/// # Examples /// # 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); 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 name
} else if let Some(trait_) = ty.as_dyn_trait() { } else if let Some(trait_) = ty.as_dyn_trait() {
trait_name(&trait_, db, edition)? trait_name(&trait_, db, edition)?
@ -390,12 +401,32 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<S
name name
} else if let Some(inner_ty) = ty.remove_ref() { } else if let Some(inner_ty) = ty.remove_ref() {
return name_of_type(&inner_ty, db, edition); 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 { } else {
return None; return None;
}; };
normalize(&name) 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> { fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Option<String> {
let name = trait_.name(db).display(db, edition).to_string(); let name = trait_.name(db).display(db, edition).to_string();
if USELESS_TRAITS.contains(&name.as_str()) { if USELESS_TRAITS.contains(&name.as_str()) {
@ -897,6 +928,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] #[test]
fn ref_call() { fn ref_call() {
check( check(