Skip to content

Commit bf77850

Browse files
bors[bot]m-n
andauthored
Merge #3542
3542: Renames work on struct field shorthands r=matklad a=m-n When renaming either a local or a struct field, struct field shorthands are now renamed correctly. Happy to refactor this if it doesn't fit the design of the code. Thanks for adding the suggestion of where to start on the issue. I wasn't sure if I should also look at the behavior of renaming when placing the cursor at the field shorthand; the following describes the behavior with this patch: ```rust #[test] fn test_rename_field_shorthand_for_unspecified() { // when renaming a shorthand, should we have a way to specify // between renaming the field and the local? // // If not is this the correct default? test_rename( r#" struct Foo { i: i32, } impl Foo { fn new(i: i32) -> Self { Self { i<|> } } } "#, "j", r#" struct Foo { i: i32, } impl Foo { fn new(j: i32) -> Self { Self { i: j } } } "#, ); } ``` Resolves #3431 Co-authored-by: Matt Niemeir <matt.niemeir@gmail.com>
2 parents 0714a06 + 13ccbb2 commit bf77850

File tree

2 files changed

+210
-26
lines changed

2 files changed

+210
-26
lines changed

crates/ra_ide/src/references/rename.rs

Lines changed: 187 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use ra_syntax::{
99
use ra_text_edit::TextEdit;
1010

1111
use crate::{
12-
FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange, SourceFileEdit, TextRange,
12+
FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, SourceChange,
13+
SourceFileEdit, TextRange,
1314
};
1415

1516
use super::find_all_refs;
@@ -46,12 +47,29 @@ fn find_name_and_module_at_offset(
4647
Some((ast_name, ast_module))
4748
}
4849

49-
fn source_edit_from_file_id_range(
50-
file_id: FileId,
51-
range: TextRange,
52-
new_name: &str,
53-
) -> SourceFileEdit {
54-
SourceFileEdit { file_id, edit: TextEdit::replace(range, new_name.into()) }
50+
fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFileEdit {
51+
let mut replacement_text = String::new();
52+
let file_id = reference.file_range.file_id;
53+
let range = match reference.kind {
54+
ReferenceKind::StructFieldShorthandForField => {
55+
replacement_text.push_str(new_name);
56+
replacement_text.push_str(": ");
57+
TextRange::from_to(
58+
reference.file_range.range.start(),
59+
reference.file_range.range.start(),
60+
)
61+
}
62+
ReferenceKind::StructFieldShorthandForLocal => {
63+
replacement_text.push_str(": ");
64+
replacement_text.push_str(new_name);
65+
TextRange::from_to(reference.file_range.range.end(), reference.file_range.range.end())
66+
}
67+
_ => {
68+
replacement_text.push_str(new_name);
69+
reference.file_range.range
70+
}
71+
};
72+
SourceFileEdit { file_id, edit: TextEdit::replace(range, replacement_text) }
5573
}
5674

5775
fn rename_mod(
@@ -99,13 +117,10 @@ fn rename_mod(
99117
source_file_edits.push(edit);
100118

101119
if let Some(RangeInfo { range: _, info: refs }) = find_all_refs(sema.db, position, None) {
102-
let ref_edits = refs.references.into_iter().map(|reference| {
103-
source_edit_from_file_id_range(
104-
reference.file_range.file_id,
105-
reference.file_range.range,
106-
new_name,
107-
)
108-
});
120+
let ref_edits = refs
121+
.references
122+
.into_iter()
123+
.map(|reference| source_edit_from_reference(reference, new_name));
109124
source_file_edits.extend(ref_edits);
110125
}
111126

@@ -121,13 +136,7 @@ fn rename_reference(
121136

122137
let edit = refs
123138
.into_iter()
124-
.map(|reference| {
125-
source_edit_from_file_id_range(
126-
reference.file_range.file_id,
127-
reference.file_range.range,
128-
new_name,
129-
)
130-
})
139+
.map(|reference| source_edit_from_reference(reference, new_name))
131140
.collect::<Vec<_>>();
132141

133142
if edit.is_empty() {
@@ -285,6 +294,163 @@ mod tests {
285294
);
286295
}
287296

297+
#[test]
298+
fn test_rename_struct_field() {
299+
test_rename(
300+
r#"
301+
struct Foo {
302+
i<|>: i32,
303+
}
304+
305+
impl Foo {
306+
fn new(i: i32) -> Self {
307+
Self { i: i }
308+
}
309+
}
310+
"#,
311+
"j",
312+
r#"
313+
struct Foo {
314+
j: i32,
315+
}
316+
317+
impl Foo {
318+
fn new(i: i32) -> Self {
319+
Self { j: i }
320+
}
321+
}
322+
"#,
323+
);
324+
}
325+
326+
#[test]
327+
fn test_rename_struct_field_for_shorthand() {
328+
test_rename(
329+
r#"
330+
struct Foo {
331+
i<|>: i32,
332+
}
333+
334+
impl Foo {
335+
fn new(i: i32) -> Self {
336+
Self { i }
337+
}
338+
}
339+
"#,
340+
"j",
341+
r#"
342+
struct Foo {
343+
j: i32,
344+
}
345+
346+
impl Foo {
347+
fn new(i: i32) -> Self {
348+
Self { j: i }
349+
}
350+
}
351+
"#,
352+
);
353+
}
354+
355+
#[test]
356+
fn test_rename_local_for_field_shorthand() {
357+
test_rename(
358+
r#"
359+
struct Foo {
360+
i: i32,
361+
}
362+
363+
impl Foo {
364+
fn new(i<|>: i32) -> Self {
365+
Self { i }
366+
}
367+
}
368+
"#,
369+
"j",
370+
r#"
371+
struct Foo {
372+
i: i32,
373+
}
374+
375+
impl Foo {
376+
fn new(j: i32) -> Self {
377+
Self { i: j }
378+
}
379+
}
380+
"#,
381+
);
382+
}
383+
384+
#[test]
385+
fn test_field_shorthand_correct_struct() {
386+
test_rename(
387+
r#"
388+
struct Foo {
389+
i<|>: i32,
390+
}
391+
392+
struct Bar {
393+
i: i32,
394+
}
395+
396+
impl Bar {
397+
fn new(i: i32) -> Self {
398+
Self { i }
399+
}
400+
}
401+
"#,
402+
"j",
403+
r#"
404+
struct Foo {
405+
j: i32,
406+
}
407+
408+
struct Bar {
409+
i: i32,
410+
}
411+
412+
impl Bar {
413+
fn new(i: i32) -> Self {
414+
Self { i }
415+
}
416+
}
417+
"#,
418+
);
419+
}
420+
421+
#[test]
422+
fn test_shadow_local_for_struct_shorthand() {
423+
test_rename(
424+
r#"
425+
struct Foo {
426+
i: i32,
427+
}
428+
429+
fn baz(i<|>: i32) -> Self {
430+
let x = Foo { i };
431+
{
432+
let i = 0;
433+
Foo { i }
434+
}
435+
}
436+
"#,
437+
"j",
438+
r#"
439+
struct Foo {
440+
i: i32,
441+
}
442+
443+
fn baz(j: i32) -> Self {
444+
let x = Foo { i: j };
445+
{
446+
let i = 0;
447+
Foo { i }
448+
}
449+
}
450+
"#,
451+
);
452+
}
453+
288454
#[test]
289455
fn test_rename_mod() {
290456
let (analysis, position) = analysis_and_position(

crates/ra_ide_db/src/search.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_hash::FxHashMap;
1717
use test_utils::tested_by;
1818

1919
use crate::{
20-
defs::{classify_name_ref, Definition},
20+
defs::{classify_name_ref, Definition, NameRefClass},
2121
RootDatabase,
2222
};
2323

@@ -30,6 +30,8 @@ pub struct Reference {
3030

3131
#[derive(Debug, Clone, PartialEq)]
3232
pub enum ReferenceKind {
33+
StructFieldShorthandForField,
34+
StructFieldShorthandForLocal,
3335
StructLiteral,
3436
Other,
3537
}
@@ -237,9 +239,8 @@ impl Definition {
237239
// FIXME: reuse sb
238240
// See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098
239241

240-
if let Some(d) = classify_name_ref(&sema, &name_ref) {
241-
let d = d.definition();
242-
if &d == self {
242+
match classify_name_ref(&sema, &name_ref) {
243+
Some(NameRefClass::Definition(def)) if &def == self => {
243244
let kind = if is_record_lit_name_ref(&name_ref)
244245
|| is_call_expr_name_ref(&name_ref)
245246
{
@@ -252,9 +253,26 @@ impl Definition {
252253
refs.push(Reference {
253254
file_range,
254255
kind,
255-
access: reference_access(&d, &name_ref),
256+
access: reference_access(&def, &name_ref),
256257
});
257258
}
259+
Some(NameRefClass::FieldShorthand { local, field }) => {
260+
match self {
261+
Definition::StructField(_) if &field == self => refs.push(Reference {
262+
file_range: sema.original_range(name_ref.syntax()),
263+
kind: ReferenceKind::StructFieldShorthandForField,
264+
access: reference_access(&field, &name_ref),
265+
}),
266+
Definition::Local(l) if &local == l => refs.push(Reference {
267+
file_range: sema.original_range(name_ref.syntax()),
268+
kind: ReferenceKind::StructFieldShorthandForLocal,
269+
access: reference_access(&Definition::Local(local), &name_ref),
270+
}),
271+
272+
_ => {} // not a usage
273+
};
274+
}
275+
_ => {} // not a usage
258276
}
259277
}
260278
}

0 commit comments

Comments
 (0)