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}
}
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",
);

View File

@ -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()) {
@ -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]
fn ref_call() {
check(