Skip to content

Commit 99a4592

Browse files
authored
Merge pull request rust-lang#18695 from roife/improve-tuple-destruction
minor: improve name suggestion for destructure_tuple_binding
2 parents dd09410 + de55520 commit 99a4592

File tree

3 files changed

+74
-74
lines changed

3 files changed

+74
-74
lines changed

src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use hir::{FileRange, Semantics};
44
use ide_db::EditionedFileId;
55
use ide_db::{label::Label, FileId, RootDatabase};
6+
use syntax::Edition;
67
use syntax::{
78
algo::{self, find_node_at_offset, find_node_at_range},
89
AstNode, AstToken, Direction, SourceFile, SyntaxElement, SyntaxKind, SyntaxToken, TextRange,
@@ -94,6 +95,10 @@ impl<'a> AssistContext<'a> {
9495
self.frange.file_id
9596
}
9697

98+
pub(crate) fn edition(&self) -> Edition {
99+
self.frange.file_id.edition()
100+
}
101+
97102
pub(crate) fn has_empty_selection(&self) -> bool {
98103
self.trimmed_range.is_empty()
99104
}

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

Lines changed: 67 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
use ide_db::text_edit::TextRange;
21
use ide_db::{
32
assists::{AssistId, AssistKind},
43
defs::Definition,
5-
search::{FileReference, SearchScope, UsageSearchResult},
4+
search::{FileReference, SearchScope},
5+
syntax_helpers::suggest_name,
6+
text_edit::TextRange,
67
};
78
use itertools::Itertools;
9+
use syntax::SmolStr;
810
use syntax::{
911
ast::{self, make, AstNode, FieldExpr, HasName, IdentPat},
1012
ted,
@@ -122,33 +124,43 @@ fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleDat
122124
return None;
123125
}
124126

125-
let name = ident_pat.name()?.to_string();
126-
127-
let usages = ctx.sema.to_def(&ident_pat).map(|def| {
127+
let usages = ctx.sema.to_def(&ident_pat).and_then(|def| {
128128
Definition::Local(def)
129129
.usages(&ctx.sema)
130130
.in_scope(&SearchScope::single_file(ctx.file_id()))
131131
.all()
132+
.iter()
133+
.next()
134+
.map(|(_, refs)| refs.to_vec())
132135
});
133136

134-
let field_names = (0..field_types.len())
135-
.map(|i| generate_name(ctx, i, &name, &ident_pat, &usages))
137+
let mut name_generator = {
138+
let mut names = vec![];
139+
if let Some(scope) = ctx.sema.scope(ident_pat.syntax()) {
140+
scope.process_all_names(&mut |name, scope| {
141+
if let hir::ScopeDef::Local(_) = scope {
142+
names.push(name.as_str().into())
143+
}
144+
})
145+
}
146+
suggest_name::NameGenerator::new_with_names(names.iter().map(|s: &SmolStr| s.as_str()))
147+
};
148+
149+
let field_names = field_types
150+
.into_iter()
151+
.enumerate()
152+
.map(|(id, ty)| {
153+
match name_generator.for_type(&ty, ctx.db(), ctx.edition()) {
154+
Some(name) => name,
155+
None => name_generator.suggest_name(&format!("_{}", id)),
156+
}
157+
.to_string()
158+
})
136159
.collect::<Vec<_>>();
137160

138161
Some(TupleData { ident_pat, ref_type, field_names, usages })
139162
}
140163

141-
fn generate_name(
142-
_ctx: &AssistContext<'_>,
143-
index: usize,
144-
_tuple_name: &str,
145-
_ident_pat: &IdentPat,
146-
_usages: &Option<UsageSearchResult>,
147-
) -> String {
148-
// FIXME: detect if name already used
149-
format!("_{index}")
150-
}
151-
152164
enum RefType {
153165
ReadOnly,
154166
Mutable,
@@ -157,7 +169,7 @@ struct TupleData {
157169
ident_pat: IdentPat,
158170
ref_type: Option<RefType>,
159171
field_names: Vec<String>,
160-
usages: Option<UsageSearchResult>,
172+
usages: Option<Vec<FileReference>>,
161173
}
162174
fn edit_tuple_assignment(
163175
ctx: &AssistContext<'_>,
@@ -213,42 +225,23 @@ fn edit_tuple_usages(
213225
ctx: &AssistContext<'_>,
214226
in_sub_pattern: bool,
215227
) -> Option<Vec<EditTupleUsage>> {
216-
let mut current_file_usages = None;
217-
218-
if let Some(usages) = data.usages.as_ref() {
219-
// We need to collect edits first before actually applying them
220-
// as mapping nodes to their mutable node versions requires an
221-
// unmodified syntax tree.
222-
//
223-
// We also defer editing usages in the current file first since
224-
// tree mutation in the same file breaks when `builder.edit_file`
225-
// is called
226-
227-
if let Some((_, refs)) = usages.iter().find(|(file_id, _)| *file_id == ctx.file_id()) {
228-
current_file_usages = Some(
229-
refs.iter()
230-
.filter_map(|r| edit_tuple_usage(ctx, edit, r, data, in_sub_pattern))
231-
.collect_vec(),
232-
);
233-
}
234-
235-
for (file_id, refs) in usages.iter() {
236-
if file_id == ctx.file_id() {
237-
continue;
238-
}
239-
240-
edit.edit_file(file_id.file_id());
241-
242-
let tuple_edits = refs
243-
.iter()
244-
.filter_map(|r| edit_tuple_usage(ctx, edit, r, data, in_sub_pattern))
245-
.collect_vec();
246-
247-
tuple_edits.into_iter().for_each(|tuple_edit| tuple_edit.apply(edit))
248-
}
249-
}
250-
251-
current_file_usages
228+
// We need to collect edits first before actually applying them
229+
// as mapping nodes to their mutable node versions requires an
230+
// unmodified syntax tree.
231+
//
232+
// We also defer editing usages in the current file first since
233+
// tree mutation in the same file breaks when `builder.edit_file`
234+
// is called
235+
236+
let edits = data
237+
.usages
238+
.as_ref()?
239+
.as_slice()
240+
.iter()
241+
.filter_map(|r| edit_tuple_usage(ctx, edit, r, data, in_sub_pattern))
242+
.collect_vec();
243+
244+
Some(edits)
252245
}
253246
fn edit_tuple_usage(
254247
ctx: &AssistContext<'_>,
@@ -1769,14 +1762,14 @@ struct S4 {
17691762
}
17701763
17711764
fn foo() -> Option<()> {
1772-
let ($0_0, _1, _2, _3, _4, _5) = &(0, (1,"1"), Some(2), [3;3], S4 { value: 4 }, &5);
1765+
let ($0_0, _1, _2, _3, s4, _5) = &(0, (1,"1"), Some(2), [3;3], S4 { value: 4 }, &5);
17731766
let v: i32 = *_0; // deref, no parens
17741767
let v: &i32 = _0; // no deref, no parens, remove `&`
17751768
f1(*_0); // deref, no parens
17761769
f2(_0); // `&*` -> cancel out -> no deref, no parens
17771770
// https://github.com/rust-lang/rust-analyzer/issues/1109#issuecomment-658868639
17781771
// let v: i32 = t.1.0; // no deref, no parens
1779-
let v: i32 = _4.value; // no deref, no parens
1772+
let v: i32 = s4.value; // no deref, no parens
17801773
(*_0).do_stuff(); // deref, parens
17811774
let v: i32 = (*_2)?; // deref, parens
17821775
let v: i32 = _3[0]; // no deref, no parens
@@ -1815,8 +1808,8 @@ impl S {
18151808
}
18161809
18171810
fn main() {
1818-
let ($0_0, _1) = &(S,2);
1819-
let s = _0.f();
1811+
let ($0s, _1) = &(S,2);
1812+
let s = s.f();
18201813
}
18211814
"#,
18221815
)
@@ -1845,8 +1838,8 @@ impl S {
18451838
}
18461839
18471840
fn main() {
1848-
let ($0_0, _1) = &(S,2);
1849-
let s = (*_0).f();
1841+
let ($0s, _1) = &(S,2);
1842+
let s = (*s).f();
18501843
}
18511844
"#,
18521845
)
@@ -1882,8 +1875,8 @@ impl T for &S {
18821875
}
18831876
18841877
fn main() {
1885-
let ($0_0, _1) = &(S,2);
1886-
let s = (*_0).f();
1878+
let ($0s, _1) = &(S,2);
1879+
let s = (*s).f();
18871880
}
18881881
"#,
18891882
)
@@ -1923,8 +1916,8 @@ impl T for &S {
19231916
}
19241917
19251918
fn main() {
1926-
let ($0_0, _1) = &(S,2);
1927-
let s = (*_0).f();
1919+
let ($0s, _1) = &(S,2);
1920+
let s = (*s).f();
19281921
}
19291922
"#,
19301923
)
@@ -1951,8 +1944,8 @@ impl S {
19511944
fn do_stuff(&self) -> i32 { 42 }
19521945
}
19531946
fn main() {
1954-
let ($0_0, _1) = &(S,&S);
1955-
let v = _0.do_stuff();
1947+
let ($0s, s1) = &(S,&S);
1948+
let v = s.do_stuff();
19561949
}
19571950
"#,
19581951
)
@@ -1973,7 +1966,7 @@ fn main() {
19731966
// `t.0` gets auto-refed -> no deref needed -> no parens
19741967
let v = t.0.do_stuff(); // no deref, no parens
19751968
let v = &t.0.do_stuff(); // `&` is for result -> no deref, no parens
1976-
// deref: `_1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
1969+
// deref: `s1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
19771970
let v = t.1.do_stuff(); // deref, parens
19781971
}
19791972
"#,
@@ -1984,13 +1977,13 @@ impl S {
19841977
fn do_stuff(&self) -> i32 { 42 }
19851978
}
19861979
fn main() {
1987-
let ($0_0, _1) = &(S,&S);
1988-
let v = _0.do_stuff(); // no deref, remove parens
1980+
let ($0s, s1) = &(S,&S);
1981+
let v = s.do_stuff(); // no deref, remove parens
19891982
// `t.0` gets auto-refed -> no deref needed -> no parens
1990-
let v = _0.do_stuff(); // no deref, no parens
1991-
let v = &_0.do_stuff(); // `&` is for result -> no deref, no parens
1992-
// deref: `_1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
1993-
let v = (*_1).do_stuff(); // deref, parens
1983+
let v = s.do_stuff(); // no deref, no parens
1984+
let v = &s.do_stuff(); // `&` is for result -> no deref, no parens
1985+
// deref: `s1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
1986+
let v = (*s1).do_stuff(); // deref, parens
19941987
}
19951988
"#,
19961989
)

src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,8 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<S
377377
return None;
378378
}
379379
name
380+
} else if let Some(inner_ty) = ty.remove_ref() {
381+
return name_of_type(&inner_ty, db, edition);
380382
} else {
381383
return None;
382384
};

0 commit comments

Comments
 (0)