Skip to content

Commit 63c5c92

Browse files
bors[bot]Veykril
andauthored
Merge #7668
7668: Finalize rename infra rewrite r=matklad a=Veykril This should be the final PR in regards to rewriting rename stuff, #4290. It addresses 3 things: - Currently renaming import aliases causes some undesired behavior(see #5198) which is why this PR causes us to just return an error if an attempt at renaming an alias is made for the time being. Though this only prevents it from happening when the alias import is renamed, so its not too helpful. - Fixes #6898 - If we are inside a macro file simply rename the input name node as there isn't really a way to do any of the fancy shorthand renames and similar things as for that we would have to exactly know what the macro generates and what not. Co-authored-by: Lukas Wirth <[email protected]>
2 parents 8638bcb + 7b64622 commit 63c5c92

File tree

2 files changed

+133
-51
lines changed

2 files changed

+133
-51
lines changed

crates/ide/src/references/rename.rs

Lines changed: 131 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@ pub(crate) fn rename_with_semantics(
7575
let source_file = sema.parse(position.file_id);
7676
let syntax = source_file.syntax();
7777

78-
let def = find_definition(sema, syntax, position)
79-
.ok_or_else(|| format_err!("No references found at position"))?;
78+
let def = find_definition(sema, syntax, position)?;
8079
match def {
8180
Definition::ModuleDef(ModuleDef::Module(module)) => rename_mod(&sema, module, new_name),
8281
def => rename_reference(sema, def, new_name),
@@ -149,18 +148,30 @@ fn find_definition(
149148
sema: &Semantics<RootDatabase>,
150149
syntax: &SyntaxNode,
151150
position: FilePosition,
152-
) -> Option<Definition> {
153-
let def = match find_name_like(sema, syntax, position)? {
154-
NameLike::Name(name) => NameClass::classify(sema, &name)?.referenced_or_defined(sema.db),
155-
NameLike::NameRef(name_ref) => NameRefClass::classify(sema, &name_ref)?.referenced(sema.db),
151+
) -> RenameResult<Definition> {
152+
match find_name_like(sema, syntax, position)
153+
.ok_or_else(|| format_err!("No references found at position"))?
154+
{
155+
// renaming aliases would rename the item being aliased as the HIR doesn't track aliases yet
156+
NameLike::Name(name)
157+
if name.syntax().parent().map_or(false, |it| ast::Rename::can_cast(it.kind())) =>
158+
{
159+
bail!("Renaming aliases is currently unsupported")
160+
}
161+
NameLike::Name(name) => {
162+
NameClass::classify(sema, &name).map(|class| class.referenced_or_defined(sema.db))
163+
}
164+
NameLike::NameRef(name_ref) => {
165+
NameRefClass::classify(sema, &name_ref).map(|class| class.referenced(sema.db))
166+
}
156167
NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime)
157168
.map(|class| NameRefClass::referenced(class, sema.db))
158169
.or_else(|| {
159170
NameClass::classify_lifetime(sema, &lifetime)
160171
.map(|it| it.referenced_or_defined(sema.db))
161-
})?,
162-
};
163-
Some(def)
172+
}),
173+
}
174+
.ok_or_else(|| format_err!("No references found at position"))
164175
}
165176

166177
fn source_edit_from_references(
@@ -173,21 +184,40 @@ fn source_edit_from_references(
173184
let mut edit = TextEdit::builder();
174185
for reference in references {
175186
let (range, replacement) = match &reference.name {
176-
NameLike::Name(_) => (None, format!("{}", new_name)),
177-
NameLike::NameRef(name_ref) => source_edit_from_name_ref(name_ref, new_name, def),
178-
NameLike::Lifetime(_) => (None, format!("{}", new_name)),
179-
};
180-
// FIXME: Some(range) will be incorrect when we are inside macros
181-
edit.replace(range.unwrap_or(reference.range), replacement);
187+
// if the ranges differ then the node is inside a macro call, we can't really attempt
188+
// to make special rewrites like shorthand syntax and such, so just rename the node in
189+
// the macro input
190+
NameLike::NameRef(name_ref) if name_ref.syntax().text_range() == reference.range => {
191+
source_edit_from_name_ref(name_ref, new_name, def)
192+
}
193+
NameLike::Name(name) if name.syntax().text_range() == reference.range => {
194+
source_edit_from_name(name, new_name)
195+
}
196+
_ => None,
197+
}
198+
.unwrap_or_else(|| (reference.range, new_name.to_string()));
199+
edit.replace(range, replacement);
182200
}
183201
(file_id, edit.finish())
184202
}
185203

204+
fn source_edit_from_name(name: &ast::Name, new_name: &str) -> Option<(TextRange, String)> {
205+
if let Some(_) = ast::RecordPatField::for_field_name(name) {
206+
if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
207+
return Some((
208+
TextRange::empty(ident_pat.syntax().text_range().start()),
209+
format!("{}: ", new_name),
210+
));
211+
}
212+
}
213+
None
214+
}
215+
186216
fn source_edit_from_name_ref(
187217
name_ref: &ast::NameRef,
188218
new_name: &str,
189219
def: Definition,
190-
) -> (Option<TextRange>, String) {
220+
) -> Option<(TextRange, String)> {
191221
if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) {
192222
let rcf_name_ref = record_field.name_ref();
193223
let rcf_expr = record_field.expr();
@@ -197,45 +227,40 @@ fn source_edit_from_name_ref(
197227
if field_name == *name_ref {
198228
if init.text() == new_name {
199229
mark::hit!(test_rename_field_put_init_shorthand);
200-
// same names, we can use a shorthand here instead
230+
// same names, we can use a shorthand here instead.
201231
// we do not want to erase attributes hence this range start
202232
let s = field_name.syntax().text_range().start();
203233
let e = record_field.syntax().text_range().end();
204-
return (Some(TextRange::new(s, e)), format!("{}", new_name));
234+
return Some((TextRange::new(s, e), new_name.to_owned()));
205235
}
206236
} else if init == *name_ref {
207237
if field_name.text() == new_name {
208238
mark::hit!(test_rename_local_put_init_shorthand);
209-
// same names, we can use a shorthand here instead
239+
// same names, we can use a shorthand here instead.
210240
// we do not want to erase attributes hence this range start
211241
let s = field_name.syntax().text_range().start();
212242
let e = record_field.syntax().text_range().end();
213-
return (Some(TextRange::new(s, e)), format!("{}", new_name));
243+
return Some((TextRange::new(s, e), new_name.to_owned()));
214244
}
215245
}
246+
None
216247
}
217248
// init shorthand
218-
(None, Some(_)) => {
219-
// FIXME: instead of splitting the shorthand, recursively trigger a rename of the
220-
// other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
221-
match def {
222-
Definition::Field(_) => {
223-
mark::hit!(test_rename_field_in_field_shorthand);
224-
let s = name_ref.syntax().text_range().start();
225-
return (Some(TextRange::empty(s)), format!("{}: ", new_name));
226-
}
227-
Definition::Local(_) => {
228-
mark::hit!(test_rename_local_in_field_shorthand);
229-
let s = name_ref.syntax().text_range().end();
230-
return (Some(TextRange::empty(s)), format!(": {}", new_name));
231-
}
232-
_ => {}
233-
}
249+
// FIXME: instead of splitting the shorthand, recursively trigger a rename of the
250+
// other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
251+
(None, Some(_)) if matches!(def, Definition::Field(_)) => {
252+
mark::hit!(test_rename_field_in_field_shorthand);
253+
let s = name_ref.syntax().text_range().start();
254+
Some((TextRange::empty(s), format!("{}: ", new_name)))
234255
}
235-
_ => {}
256+
(None, Some(_)) if matches!(def, Definition::Local(_)) => {
257+
mark::hit!(test_rename_local_in_field_shorthand);
258+
let s = name_ref.syntax().text_range().end();
259+
Some((TextRange::empty(s), format!(": {}", new_name)))
260+
}
261+
_ => None,
236262
}
237-
}
238-
if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) {
263+
} else if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) {
239264
let rcf_name_ref = record_field.name_ref();
240265
let rcf_pat = record_field.pat();
241266
match (rcf_name_ref, rcf_pat) {
@@ -244,17 +269,20 @@ fn source_edit_from_name_ref(
244269
// field name is being renamed
245270
if pat.name().map_or(false, |it| it.text() == new_name) {
246271
mark::hit!(test_rename_field_put_init_shorthand_pat);
247-
// same names, we can use a shorthand here instead
272+
// same names, we can use a shorthand here instead/
248273
// we do not want to erase attributes hence this range start
249274
let s = field_name.syntax().text_range().start();
250275
let e = record_field.syntax().text_range().end();
251-
return (Some(TextRange::new(s, e)), format!("{}", new_name));
276+
Some((TextRange::new(s, e), pat.to_string()))
277+
} else {
278+
None
252279
}
253280
}
254-
_ => {}
281+
_ => None,
255282
}
283+
} else {
284+
None
256285
}
257-
(None, format!("{}", new_name))
258286
}
259287

260288
fn rename_mod(
@@ -1477,24 +1505,24 @@ fn foo(i: i32) -> Foo {
14771505
}
14781506

14791507
#[test]
1480-
fn test_struct_field_destructure_into_shorthand() {
1508+
fn test_struct_field_pat_into_shorthand() {
14811509
mark::check!(test_rename_field_put_init_shorthand_pat);
14821510
check(
14831511
"baz",
14841512
r#"
14851513
struct Foo { i$0: i32 }
14861514
14871515
fn foo(foo: Foo) {
1488-
let Foo { i: baz } = foo;
1489-
let _ = baz;
1516+
let Foo { i: ref baz @ qux } = foo;
1517+
let _ = qux;
14901518
}
14911519
"#,
14921520
r#"
14931521
struct Foo { baz: i32 }
14941522
14951523
fn foo(foo: Foo) {
1496-
let Foo { baz } = foo;
1497-
let _ = baz;
1524+
let Foo { ref baz @ qux } = foo;
1525+
let _ = qux;
14981526
}
14991527
"#,
15001528
);
@@ -1567,6 +1595,27 @@ fn foo(Foo { i: bar }: foo) -> i32 {
15671595
)
15681596
}
15691597

1598+
#[test]
1599+
fn test_struct_field_complex_ident_pat() {
1600+
check(
1601+
"baz",
1602+
r#"
1603+
struct Foo { i$0: i32 }
1604+
1605+
fn foo(foo: Foo) {
1606+
let Foo { ref i } = foo;
1607+
}
1608+
"#,
1609+
r#"
1610+
struct Foo { baz: i32 }
1611+
1612+
fn foo(foo: Foo) {
1613+
let Foo { baz: ref i } = foo;
1614+
}
1615+
"#,
1616+
);
1617+
}
1618+
15701619
#[test]
15711620
fn test_rename_lifetimes() {
15721621
mark::check!(rename_lifetime);
@@ -1671,6 +1720,40 @@ struct Foo;
16711720
impl Foo {
16721721
fn foo(self) {}
16731722
}
1723+
"#,
1724+
)
1725+
}
1726+
1727+
#[test]
1728+
fn test_rename_field_in_pat_in_macro_doesnt_shorthand() {
1729+
// ideally we would be able to make this emit a short hand, but I doubt this is easily possible
1730+
check(
1731+
"baz",
1732+
r#"
1733+
macro_rules! foo {
1734+
($pattern:pat) => {
1735+
let $pattern = loop {};
1736+
};
1737+
}
1738+
struct Foo {
1739+
bar$0: u32,
1740+
}
1741+
fn foo() {
1742+
foo!(Foo { bar: baz });
1743+
}
1744+
"#,
1745+
r#"
1746+
macro_rules! foo {
1747+
($pattern:pat) => {
1748+
let $pattern = loop {};
1749+
};
1750+
}
1751+
struct Foo {
1752+
baz: u32,
1753+
}
1754+
fn foo() {
1755+
foo!(Foo { baz: baz });
1756+
}
16741757
"#,
16751758
)
16761759
}

crates/syntax/src/ast/node_ext.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
44
use std::fmt;
55

6-
use ast::AttrsOwner;
76
use itertools::Itertools;
87
use parser::SyntaxKind;
98

109
use crate::{
11-
ast::{self, support, AstNode, AstToken, NameOwner, SyntaxNode},
10+
ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode},
1211
SmolStr, SyntaxElement, SyntaxToken, T,
1312
};
1413

@@ -324,7 +323,7 @@ impl ast::RecordPatField {
324323

325324
pub fn for_field_name(field_name: &ast::Name) -> Option<ast::RecordPatField> {
326325
let candidate =
327-
field_name.syntax().ancestors().nth(3).and_then(ast::RecordPatField::cast)?;
326+
field_name.syntax().ancestors().nth(2).and_then(ast::RecordPatField::cast)?;
328327
match candidate.field_name()? {
329328
NameOrNameRef::Name(name) if name == *field_name => Some(candidate),
330329
_ => None,

0 commit comments

Comments
 (0)