feat: improve name generation in destructure_tuple_binding

This commit is contained in:
roife 2024-12-13 23:34:03 +08:00
parent f50969b239
commit cb1533f7e9
3 changed files with 49 additions and 33 deletions

View file

@ -3,6 +3,7 @@
use hir::{FileRange, Semantics};
use ide_db::EditionedFileId;
use ide_db::{label::Label, FileId, RootDatabase};
use syntax::Edition;
use syntax::{
algo::{self, find_node_at_offset, find_node_at_range},
AstNode, AstToken, Direction, SourceFile, SyntaxElement, SyntaxKind, SyntaxToken, TextRange,
@ -94,6 +95,10 @@ impl<'a> AssistContext<'a> {
self.frange.file_id
}
pub(crate) fn edition(&self) -> Edition {
self.frange.file_id.edition()
}
pub(crate) fn has_empty_selection(&self) -> bool {
self.trimmed_range.is_empty()
}

View file

@ -1,10 +1,12 @@
use ide_db::text_edit::TextRange;
use ide_db::{
assists::{AssistId, AssistKind},
defs::Definition,
search::{FileReference, SearchScope, UsageSearchResult},
syntax_helpers::suggest_name,
text_edit::TextRange,
};
use itertools::Itertools;
use syntax::SmolStr;
use syntax::{
ast::{self, make, AstNode, FieldExpr, HasName, IdentPat},
ted,
@ -131,24 +133,31 @@ fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleDat
.all()
});
let field_names = (0..field_types.len())
.map(|i| generate_name(ctx, i, &name, &ident_pat, &usages))
let mut name_generator = {
let mut names = vec![];
ctx.sema.scope(ident_pat.syntax())?.process_all_names(&mut |name, scope| {
if let hir::ScopeDef::Local(_) = scope {
names.push(name.as_str().into())
}
});
suggest_name::NameGenerator::new_with_names(names.iter().map(|s: &SmolStr| s.as_str()))
};
let field_names = field_types
.into_iter()
.enumerate()
.map(|(id, ty)| {
match name_generator.for_type(&ty, ctx.db(), ctx.edition()) {
Some(name) => name,
None => name_generator.suggest_name(&format!("_{}", id)),
}
.to_string()
})
.collect::<Vec<_>>();
Some(TupleData { ident_pat, ref_type, field_names, usages })
}
fn generate_name(
_ctx: &AssistContext<'_>,
index: usize,
_tuple_name: &str,
_ident_pat: &IdentPat,
_usages: &Option<UsageSearchResult>,
) -> String {
// FIXME: detect if name already used
format!("_{index}")
}
enum RefType {
ReadOnly,
Mutable,
@ -1769,14 +1778,14 @@ struct S4 {
}
fn foo() -> Option<()> {
let ($0_0, _1, _2, _3, _4, _5) = &(0, (1,"1"), Some(2), [3;3], S4 { value: 4 }, &5);
let ($0_0, _1, _2, _3, s4, _5) = &(0, (1,"1"), Some(2), [3;3], S4 { value: 4 }, &5);
let v: i32 = *_0; // deref, no parens
let v: &i32 = _0; // no deref, no parens, remove `&`
f1(*_0); // deref, no parens
f2(_0); // `&*` -> cancel out -> no deref, no parens
// https://github.com/rust-lang/rust-analyzer/issues/1109#issuecomment-658868639
// let v: i32 = t.1.0; // no deref, no parens
let v: i32 = _4.value; // no deref, no parens
let v: i32 = s4.value; // no deref, no parens
(*_0).do_stuff(); // deref, parens
let v: i32 = (*_2)?; // deref, parens
let v: i32 = _3[0]; // no deref, no parens
@ -1815,8 +1824,8 @@ impl S {
}
fn main() {
let ($0_0, _1) = &(S,2);
let s = _0.f();
let ($0s, _1) = &(S,2);
let s = s.f();
}
"#,
)
@ -1845,8 +1854,8 @@ impl S {
}
fn main() {
let ($0_0, _1) = &(S,2);
let s = (*_0).f();
let ($0s, _1) = &(S,2);
let s = (*s).f();
}
"#,
)
@ -1882,8 +1891,8 @@ impl T for &S {
}
fn main() {
let ($0_0, _1) = &(S,2);
let s = (*_0).f();
let ($0s, _1) = &(S,2);
let s = (*s).f();
}
"#,
)
@ -1923,8 +1932,8 @@ impl T for &S {
}
fn main() {
let ($0_0, _1) = &(S,2);
let s = (*_0).f();
let ($0s, _1) = &(S,2);
let s = (*s).f();
}
"#,
)
@ -1951,8 +1960,8 @@ impl S {
fn do_stuff(&self) -> i32 { 42 }
}
fn main() {
let ($0_0, _1) = &(S,&S);
let v = _0.do_stuff();
let ($0s, s1) = &(S,&S);
let v = s.do_stuff();
}
"#,
)
@ -1973,7 +1982,7 @@ fn main() {
// `t.0` gets auto-refed -> no deref needed -> no parens
let v = t.0.do_stuff(); // no deref, no parens
let v = &t.0.do_stuff(); // `&` is for result -> no deref, no parens
// deref: `_1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
// deref: `s1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
let v = t.1.do_stuff(); // deref, parens
}
"#,
@ -1984,13 +1993,13 @@ impl S {
fn do_stuff(&self) -> i32 { 42 }
}
fn main() {
let ($0_0, _1) = &(S,&S);
let v = _0.do_stuff(); // no deref, remove parens
let ($0s, s1) = &(S,&S);
let v = s.do_stuff(); // no deref, remove parens
// `t.0` gets auto-refed -> no deref needed -> no parens
let v = _0.do_stuff(); // no deref, no parens
let v = &_0.do_stuff(); // `&` is for result -> no deref, no parens
// deref: `_1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
let v = (*_1).do_stuff(); // deref, parens
let v = s.do_stuff(); // no deref, no parens
let v = &s.do_stuff(); // `&` is for result -> no deref, no parens
// deref: `s1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
let v = (*s1).do_stuff(); // deref, parens
}
"#,
)

View file

@ -377,6 +377,8 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<S
return None;
}
name
} else if let Some(inner_ty) = ty.remove_ref() {
return name_of_type(&inner_ty, db, edition);
} else {
return None;
};