Skip to content

Commit 3831b72

Browse files
committed
Auto merge of rust-lang#18038 - roife:fix-issue-18034, r=Veykril
feat: generate names for tuple-struct in add-missing-match-arms fix rust-lang#18034. This PR includes the following enhancement: - Introduced a `NameGenerator` in `suggest_name`, which implements an automatic renaming algorithm to avoid name conflicts. Here are a few examples: ```rust let mut generator = NameGenerator::new(); assert_eq!(generator.suggest_name("a"), "a"); assert_eq!(generator.suggest_name("a"), "a1"); assert_eq!(generator.suggest_name("a"), "a2"); assert_eq!(generator.suggest_name("b"), "b"); assert_eq!(generator.suggest_name("b"), "b1"); assert_eq!(generator.suggest_name("b2"), "b2"); assert_eq!(generator.suggest_name("b"), "b3"); assert_eq!(generator.suggest_name("b"), "b4"); assert_eq!(generator.suggest_name("b3"), "b5"); ``` - Updated existing testcases in ide-assists for the new `NameGenerator` (only modified generated names). - Generate names for tuple structs instead of using wildcard patterns in `add-missing-match-arms`.
2 parents 4fcad5a + b304701 commit 3831b72

File tree

6 files changed

+298
-105
lines changed

6 files changed

+298
-105
lines changed

src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs

Lines changed: 106 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use std::iter::{self, Peekable};
22

33
use either::Either;
4-
use hir::{sym, Adt, Crate, HasAttrs, HasSource, ImportPathConfig, ModuleDef, Semantics};
4+
use hir::{sym, Adt, Crate, HasAttrs, ImportPathConfig, ModuleDef, Semantics};
5+
use ide_db::syntax_helpers::suggest_name;
56
use ide_db::RootDatabase;
67
use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast};
78
use itertools::Itertools;
89
use syntax::ast::edit_in_place::Removable;
9-
use syntax::ast::{self, make, AstNode, HasName, MatchArmList, MatchExpr, Pat};
10+
use syntax::ast::{self, make, AstNode, MatchArmList, MatchExpr, Pat};
1011

1112
use crate::{utils, AssistContext, AssistId, AssistKind, Assists};
1213

@@ -90,7 +91,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
9091
.into_iter()
9192
.filter_map(|variant| {
9293
Some((
93-
build_pat(ctx.db(), module, variant, cfg)?,
94+
build_pat(ctx, module, variant, cfg)?,
9495
variant.should_be_hidden(ctx.db(), module.krate()),
9596
))
9697
})
@@ -141,9 +142,8 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
141142
let is_hidden = variants
142143
.iter()
143144
.any(|variant| variant.should_be_hidden(ctx.db(), module.krate()));
144-
let patterns = variants
145-
.into_iter()
146-
.filter_map(|variant| build_pat(ctx.db(), module, variant, cfg));
145+
let patterns =
146+
variants.into_iter().filter_map(|variant| build_pat(ctx, module, variant, cfg));
147147

148148
(ast::Pat::from(make::tuple_pat(patterns)), is_hidden)
149149
})
@@ -174,9 +174,8 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
174174
let is_hidden = variants
175175
.iter()
176176
.any(|variant| variant.should_be_hidden(ctx.db(), module.krate()));
177-
let patterns = variants
178-
.into_iter()
179-
.filter_map(|variant| build_pat(ctx.db(), module, variant, cfg));
177+
let patterns =
178+
variants.into_iter().filter_map(|variant| build_pat(ctx, module, variant, cfg));
180179
(ast::Pat::from(make::slice_pat(patterns)), is_hidden)
181180
})
182181
.filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
@@ -438,33 +437,39 @@ fn resolve_array_of_enum_def(
438437
}
439438

440439
fn build_pat(
441-
db: &RootDatabase,
440+
ctx: &AssistContext<'_>,
442441
module: hir::Module,
443442
var: ExtendedVariant,
444443
cfg: ImportPathConfig,
445444
) -> Option<ast::Pat> {
445+
let db = ctx.db();
446446
match var {
447447
ExtendedVariant::Variant(var) => {
448448
let edition = module.krate().edition(db);
449449
let path = mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?, edition);
450-
// FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
451-
Some(match var.source(db)?.value.kind() {
452-
ast::StructKind::Tuple(field_list) => {
453-
let pats =
454-
iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
450+
let fields = var.fields(db);
451+
let pat = match var.kind(db) {
452+
hir::StructKind::Tuple => {
453+
let mut name_generator = suggest_name::NameGenerator::new();
454+
let pats = fields.into_iter().map(|f| {
455+
let name = name_generator.for_type(&f.ty(db), db, edition);
456+
match name {
457+
Some(name) => make::ext::simple_ident_pat(make::name(&name)).into(),
458+
None => make::wildcard_pat().into(),
459+
}
460+
});
455461
make::tuple_struct_pat(path, pats).into()
456462
}
457-
ast::StructKind::Record(field_list) => {
458-
let pats = field_list.fields().map(|f| {
459-
make::ext::simple_ident_pat(
460-
f.name().expect("Record field must have a name"),
461-
)
462-
.into()
463-
});
463+
hir::StructKind::Record => {
464+
let pats = fields
465+
.into_iter()
466+
.map(|f| make::name(f.name(db).as_str()))
467+
.map(|name| make::ext::simple_ident_pat(name).into());
464468
make::record_pat(path, pats).into()
465469
}
466-
ast::StructKind::Unit => make::path_pat(path),
467-
})
470+
hir::StructKind::Unit => make::path_pat(path),
471+
};
472+
Some(pat)
468473
}
469474
ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))),
470475
ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))),
@@ -1976,4 +1981,81 @@ fn a() {
19761981
}"#,
19771982
)
19781983
}
1984+
1985+
#[test]
1986+
fn suggest_name_for_tuple_struct_patterns() {
1987+
// single tuple struct
1988+
check_assist(
1989+
add_missing_match_arms,
1990+
r#"
1991+
struct S;
1992+
1993+
pub enum E {
1994+
A
1995+
B(S),
1996+
}
1997+
1998+
fn f() {
1999+
let value = E::A;
2000+
match value {
2001+
$0
2002+
}
2003+
}
2004+
"#,
2005+
r#"
2006+
struct S;
2007+
2008+
pub enum E {
2009+
A
2010+
B(S),
2011+
}
2012+
2013+
fn f() {
2014+
let value = E::A;
2015+
match value {
2016+
$0E::A => todo!(),
2017+
E::B(s) => todo!(),
2018+
}
2019+
}
2020+
"#,
2021+
);
2022+
2023+
// multiple tuple struct patterns
2024+
check_assist(
2025+
add_missing_match_arms,
2026+
r#"
2027+
struct S1;
2028+
struct S2;
2029+
2030+
pub enum E {
2031+
A
2032+
B(S1, S2),
2033+
}
2034+
2035+
fn f() {
2036+
let value = E::A;
2037+
match value {
2038+
$0
2039+
}
2040+
}
2041+
"#,
2042+
r#"
2043+
struct S1;
2044+
struct S2;
2045+
2046+
pub enum E {
2047+
A
2048+
B(S1, S2),
2049+
}
2050+
2051+
fn f() {
2052+
let value = E::A;
2053+
match value {
2054+
$0E::A => todo!(),
2055+
E::B(s1, s2) => todo!(),
2056+
}
2057+
}
2058+
"#,
2059+
);
2060+
}
19792061
}

src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ fn resolve_name_conflicts(
584584

585585
for old_strukt_param in old_strukt_params.generic_params() {
586586
// Get old name from `strukt`
587-
let mut name = SmolStr::from(match &old_strukt_param {
587+
let name = SmolStr::from(match &old_strukt_param {
588588
ast::GenericParam::ConstParam(c) => c.name()?.to_string(),
589589
ast::GenericParam::LifetimeParam(l) => {
590590
l.lifetime()?.lifetime_ident_token()?.to_string()
@@ -593,8 +593,19 @@ fn resolve_name_conflicts(
593593
});
594594

595595
// The new name cannot be conflicted with generics in trait, and the renamed names.
596-
name = suggest_name::for_unique_generic_name(&name, old_impl_params);
597-
name = suggest_name::for_unique_generic_name(&name, &params);
596+
let param_list_to_names = |param_list: &GenericParamList| {
597+
param_list.generic_params().flat_map(|param| match param {
598+
ast::GenericParam::TypeParam(t) => t.name().map(|name| name.to_string()),
599+
p => Some(p.to_string()),
600+
})
601+
};
602+
let existing_names = param_list_to_names(old_impl_params)
603+
.chain(param_list_to_names(&params))
604+
.collect_vec();
605+
let mut name_generator = suggest_name::NameGenerator::new_with_names(
606+
existing_names.iter().map(|s| s.as_str()),
607+
);
608+
let name = name_generator.suggest_name(&name);
598609
match old_strukt_param {
599610
ast::GenericParam::ConstParam(c) => {
600611
if let Some(const_ty) = c.ty() {
@@ -1213,9 +1224,9 @@ struct S<T> {
12131224
b : B<T>,
12141225
}
12151226
1216-
impl<T0> Trait<T0> for S<T0> {
1217-
fn f(&self, a: T0) -> T0 {
1218-
<B<T0> as Trait<T0>>::f(&self.b, a)
1227+
impl<T1> Trait<T1> for S<T1> {
1228+
fn f(&self, a: T1) -> T1 {
1229+
<B<T1> as Trait<T1>>::f(&self.b, a)
12191230
}
12201231
}
12211232
"#,
@@ -1527,12 +1538,12 @@ where
15271538
b : B<T, T1>,
15281539
}
15291540
1530-
impl<T, T2, T10> Trait<T> for S<T2, T10>
1541+
impl<T, T2, T3> Trait<T> for S<T2, T3>
15311542
where
1532-
T10: AnotherTrait
1543+
T3: AnotherTrait
15331544
{
15341545
fn f(&self, a: T) -> T {
1535-
<B<T2, T10> as Trait<T>>::f(&self.b, a)
1546+
<B<T2, T3> as Trait<T>>::f(&self.b, a)
15361547
}
15371548
}"#,
15381549
);
@@ -1589,12 +1600,12 @@ where
15891600
b : B<T>,
15901601
}
15911602
1592-
impl<T, T0> Trait<T> for S<T0>
1603+
impl<T, T2> Trait<T> for S<T2>
15931604
where
1594-
T0: AnotherTrait
1605+
T2: AnotherTrait
15951606
{
15961607
fn f(&self, a: T) -> T {
1597-
<B<T0> as Trait<T>>::f(&self.b, a)
1608+
<B<T2> as Trait<T>>::f(&self.b, a)
15981609
}
15991610
}"#,
16001611
);

src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use ide_db::syntax_helpers::suggest_name;
2+
use itertools::Itertools;
23
use syntax::{
3-
ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams},
4+
ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams, HasName},
45
ted,
56
};
67

@@ -33,8 +34,18 @@ pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_>
3334
let impl_trait_type = edit.make_mut(impl_trait_type);
3435
let fn_ = edit.make_mut(fn_);
3536
let fn_generic_param_list = fn_.get_or_create_generic_param_list();
36-
let type_param_name =
37-
suggest_name::for_impl_trait_as_generic(&impl_trait_type, &fn_generic_param_list);
37+
38+
let existing_names = fn_generic_param_list
39+
.generic_params()
40+
.flat_map(|param| match param {
41+
ast::GenericParam::TypeParam(t) => t.name().map(|name| name.to_string()),
42+
p => Some(p.to_string()),
43+
})
44+
.collect_vec();
45+
let type_param_name = suggest_name::NameGenerator::new_with_names(
46+
existing_names.iter().map(|s| s.as_str()),
47+
)
48+
.for_impl_trait_as_generic(&impl_trait_type);
3849

3950
let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list))
4051
.clone_for_update();
@@ -116,7 +127,7 @@ fn foo<$0B: Bar
116127
check_assist(
117128
introduce_named_generic,
118129
r#"fn foo<B>(bar: $0impl Bar) {}"#,
119-
r#"fn foo<B, $0B0: Bar>(bar: B0) {}"#,
130+
r#"fn foo<B, $0B1: Bar>(bar: B1) {}"#,
120131
);
121132
}
122133

@@ -125,7 +136,7 @@ fn foo<$0B: Bar
125136
check_assist(
126137
introduce_named_generic,
127138
r#"fn foo<B, B0, B1, B3>(bar: $0impl Bar) {}"#,
128-
r#"fn foo<B, B0, B1, B3, $0B2: Bar>(bar: B2) {}"#,
139+
r#"fn foo<B, B0, B1, B3, $0B4: Bar>(bar: B4) {}"#,
129140
);
130141
}
131142

src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,12 @@ pub(crate) fn complete_pattern(
4848

4949
// Suggest name only in let-stmt and fn param
5050
if pattern_ctx.should_suggest_name {
51+
let mut name_generator = suggest_name::NameGenerator::new();
5152
if let Some(suggested) = ctx
5253
.expected_type
5354
.as_ref()
5455
.map(|ty| ty.strip_references())
55-
.and_then(|ty| suggest_name::for_type(&ty, ctx.db, ctx.edition))
56+
.and_then(|ty| name_generator.for_type(&ty, ctx.db, ctx.edition))
5657
{
5758
acc.suggest_name(ctx, &suggested);
5859
}

0 commit comments

Comments
 (0)