Skip to content

Commit 382361c

Browse files
bors[bot]Veykril
andauthored
Merge #6552
6552: Properly handle shorthands in destructure patterns when renaming r=SomeoneToIgnore a=Veykril Fixes #6548 and #6551. Co-authored-by: Lukas Wirth <[email protected]>
2 parents e8c8039 + f3e2973 commit 382361c

File tree

5 files changed

+206
-26
lines changed

5 files changed

+206
-26
lines changed

crates/ide/src/references.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,23 @@ pub(crate) fn find_all_refs(
110110
.filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)
111111
.collect();
112112

113-
let decl_range = def.try_to_nav(sema.db)?.focus_or_full_range();
114-
115-
let declaration = Declaration {
116-
nav: def.try_to_nav(sema.db)?,
117-
kind: ReferenceKind::Other,
118-
access: decl_access(&def, &syntax, decl_range),
113+
let nav = def.try_to_nav(sema.db)?;
114+
let decl_range = nav.focus_or_full_range();
115+
116+
let mut kind = ReferenceKind::Other;
117+
if let Definition::Local(local) = def {
118+
if let either::Either::Left(pat) = local.source(sema.db).value {
119+
if matches!(
120+
pat.syntax().parent().and_then(ast::RecordPatField::cast),
121+
Some(pat_field) if pat_field.name_ref().is_none()
122+
) {
123+
kind = ReferenceKind::FieldShorthandForLocal;
124+
}
125+
}
119126
};
120127

128+
let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) };
129+
121130
Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references }))
122131
}
123132

@@ -613,7 +622,7 @@ fn foo() {
613622
expect![[r#"
614623
f RECORD_FIELD FileId(0) 15..21 15..16 Other
615624
616-
FileId(0) 55..56 Other Read
625+
FileId(0) 55..56 RecordFieldExprOrPat Read
617626
FileId(0) 68..69 Other Write
618627
"#]],
619628
);
@@ -748,7 +757,7 @@ fn f() -> m::En {
748757
expect![[r#"
749758
field RECORD_FIELD FileId(0) 56..65 56..61 Other
750759
751-
FileId(0) 125..130 Other Read
760+
FileId(0) 125..130 RecordFieldExprOrPat Read
752761
"#]],
753762
);
754763
}

crates/ide/src/references/rename.rs

Lines changed: 158 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! FIXME: write short doc here
22
33
use hir::{Module, ModuleDef, ModuleSource, Semantics};
4-
use ide_db::base_db::SourceDatabaseExt;
4+
use ide_db::base_db::{FileRange, SourceDatabaseExt};
55
use ide_db::{
66
defs::{Definition, NameClass, NameRefClass},
77
RootDatabase,
@@ -106,9 +106,12 @@ fn find_module_at_offset(
106106
Some(module)
107107
}
108108

109-
fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFileEdit {
109+
fn source_edit_from_reference(
110+
sema: &Semantics<RootDatabase>,
111+
reference: Reference,
112+
new_name: &str,
113+
) -> SourceFileEdit {
110114
let mut replacement_text = String::new();
111-
let file_id = reference.file_range.file_id;
112115
let range = match reference.kind {
113116
ReferenceKind::FieldShorthandForField => {
114117
mark::hit!(test_rename_struct_field_for_shorthand);
@@ -122,12 +125,48 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
122125
replacement_text.push_str(new_name);
123126
TextRange::new(reference.file_range.range.end(), reference.file_range.range.end())
124127
}
128+
ReferenceKind::RecordFieldExprOrPat => {
129+
mark::hit!(test_rename_field_expr_pat);
130+
replacement_text.push_str(new_name);
131+
edit_text_range_for_record_field_expr_or_pat(sema, reference.file_range, new_name)
132+
}
125133
_ => {
126134
replacement_text.push_str(new_name);
127135
reference.file_range.range
128136
}
129137
};
130-
SourceFileEdit { file_id, edit: TextEdit::replace(range, replacement_text) }
138+
SourceFileEdit {
139+
file_id: reference.file_range.file_id,
140+
edit: TextEdit::replace(range, replacement_text),
141+
}
142+
}
143+
144+
fn edit_text_range_for_record_field_expr_or_pat(
145+
sema: &Semantics<RootDatabase>,
146+
file_range: FileRange,
147+
new_name: &str,
148+
) -> TextRange {
149+
let source_file = sema.parse(file_range.file_id);
150+
let file_syntax = source_file.syntax();
151+
let original_range = file_range.range;
152+
153+
syntax::algo::find_node_at_range::<ast::RecordExprField>(file_syntax, original_range)
154+
.and_then(|field_expr| match field_expr.expr().and_then(|e| e.name_ref()) {
155+
Some(name) if &name.to_string() == new_name => Some(field_expr.syntax().text_range()),
156+
_ => None,
157+
})
158+
.or_else(|| {
159+
syntax::algo::find_node_at_range::<ast::RecordPatField>(file_syntax, original_range)
160+
.and_then(|field_pat| match field_pat.pat() {
161+
Some(ast::Pat::IdentPat(pat))
162+
if pat.name().map(|n| n.to_string()).as_deref() == Some(new_name) =>
163+
{
164+
Some(field_pat.syntax().text_range())
165+
}
166+
_ => None,
167+
})
168+
})
169+
.unwrap_or(original_range)
131170
}
132171

133172
fn rename_mod(
@@ -170,7 +209,7 @@ fn rename_mod(
170209
let ref_edits = refs
171210
.references
172211
.into_iter()
173-
.map(|reference| source_edit_from_reference(reference, new_name));
212+
.map(|reference| source_edit_from_reference(sema, reference, new_name));
174213
source_file_edits.extend(ref_edits);
175214

176215
Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
@@ -211,7 +250,7 @@ fn rename_to_self(
211250

212251
let mut edits = usages
213252
.into_iter()
214-
.map(|reference| source_edit_from_reference(reference, "self"))
253+
.map(|reference| source_edit_from_reference(sema, reference, "self"))
215254
.collect::<Vec<_>>();
216255

217256
edits.push(SourceFileEdit {
@@ -300,7 +339,7 @@ fn rename_reference(
300339

301340
let edit = refs
302341
.into_iter()
303-
.map(|reference| source_edit_from_reference(reference, new_name))
342+
.map(|reference| source_edit_from_reference(sema, reference, new_name))
304343
.collect::<Vec<_>>();
305344

306345
if edit.is_empty() {
@@ -1097,4 +1136,116 @@ impl Foo {
10971136
"#,
10981137
);
10991138
}
1139+
1140+
#[test]
1141+
fn test_initializer_use_field_init_shorthand() {
1142+
mark::check!(test_rename_field_expr_pat);
1143+
check(
1144+
"bar",
1145+
r#"
1146+
struct Foo { i<|>: i32 }
1147+
1148+
fn foo(bar: i32) -> Foo {
1149+
Foo { i: bar }
1150+
}
1151+
"#,
1152+
r#"
1153+
struct Foo { bar: i32 }
1154+
1155+
fn foo(bar: i32) -> Foo {
1156+
Foo { bar }
1157+
}
1158+
"#,
1159+
);
1160+
}
1161+
1162+
#[test]
1163+
fn test_struct_field_destructure_into_shorthand() {
1164+
check(
1165+
"baz",
1166+
r#"
1167+
struct Foo { i<|>: i32 }
1168+
1169+
fn foo(foo: Foo) {
1170+
let Foo { i: baz } = foo;
1171+
let _ = baz;
1172+
}
1173+
"#,
1174+
r#"
1175+
struct Foo { baz: i32 }
1176+
1177+
fn foo(foo: Foo) {
1178+
let Foo { baz } = foo;
1179+
let _ = baz;
1180+
}
1181+
"#,
1182+
);
1183+
}
1184+
1185+
#[test]
1186+
fn test_rename_binding_in_destructure_pat() {
1187+
let expected_fixture = r#"
1188+
struct Foo {
1189+
i: i32,
1190+
}
1191+
1192+
fn foo(foo: Foo) {
1193+
let Foo { i: bar } = foo;
1194+
let _ = bar;
1195+
}
1196+
"#;
1197+
check(
1198+
"bar",
1199+
r#"
1200+
struct Foo {
1201+
i: i32,
1202+
}
1203+
1204+
fn foo(foo: Foo) {
1205+
let Foo { i: b } = foo;
1206+
let _ = b<|>;
1207+
}
1208+
"#,
1209+
expected_fixture,
1210+
);
1211+
check(
1212+
"bar",
1213+
r#"
1214+
struct Foo {
1215+
i: i32,
1216+
}
1217+
1218+
fn foo(foo: Foo) {
1219+
let Foo { i } = foo;
1220+
let _ = i<|>;
1221+
}
1222+
"#,
1223+
expected_fixture,
1224+
);
1225+
}
1226+
1227+
#[test]
1228+
fn test_rename_binding_in_destructure_param_pat() {
1229+
check(
1230+
"bar",
1231+
r#"
1232+
struct Foo {
1233+
i: i32
1234+
}
1235+
1236+
fn foo(Foo { i }: foo) -> i32 {
1237+
i<|>
1238+
}
1239+
"#,
1240+
r#"
1241+
struct Foo {
1242+
i: i32
1243+
}
1244+
1245+
fn foo(Foo { i: bar }: foo) -> i32 {
1246+
bar
1247+
}
1248+
"#,
1249+
)
1250+
}
11001251
}

crates/ide_db/src/search.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub enum ReferenceKind {
3030
FieldShorthandForField,
3131
FieldShorthandForLocal,
3232
StructLiteral,
33+
RecordFieldExprOrPat,
3334
Other,
3435
}
3536

@@ -278,8 +279,9 @@ impl<'a> FindUsages<'a> {
278279
) -> bool {
279280
match NameRefClass::classify(self.sema, &name_ref) {
280281
Some(NameRefClass::Definition(def)) if &def == self.def => {
281-
let kind = if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref)
282-
{
282+
let kind = if is_record_field_expr_or_pat(&name_ref) {
283+
ReferenceKind::RecordFieldExprOrPat
284+
} else if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) {
283285
ReferenceKind::StructLiteral
284286
} else {
285287
ReferenceKind::Other
@@ -385,3 +387,17 @@ fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool {
385387
.map(|p| p.name_ref().as_ref() == Some(name_ref))
386388
.unwrap_or(false)
387389
}
390+
391+
fn is_record_field_expr_or_pat(name_ref: &ast::NameRef) -> bool {
392+
if let Some(parent) = name_ref.syntax().parent() {
393+
match_ast! {
394+
match parent {
395+
ast::RecordExprField(it) => true,
396+
ast::RecordPatField(_it) => true,
397+
_ => false,
398+
}
399+
}
400+
} else {
401+
false
402+
}
403+
}

crates/syntax/src/ast/expr_ext.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ impl ast::Expr {
2222
_ => false,
2323
}
2424
}
25+
26+
pub fn name_ref(&self) -> Option<ast::NameRef> {
27+
if let ast::Expr::PathExpr(expr) = self {
28+
let path = expr.path()?;
29+
let segment = path.segment()?;
30+
let name_ref = segment.name_ref()?;
31+
if path.qualifier().is_none() {
32+
return Some(name_ref);
33+
}
34+
}
35+
None
36+
}
2537
}
2638

2739
#[derive(Debug, Clone, PartialEq, Eq)]

crates/syntax/src/ast/node_ext.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -203,15 +203,7 @@ impl ast::RecordExprField {
203203
if let Some(name_ref) = self.name_ref() {
204204
return Some(name_ref);
205205
}
206-
if let Some(ast::Expr::PathExpr(expr)) = self.expr() {
207-
let path = expr.path()?;
208-
let segment = path.segment()?;
209-
let name_ref = segment.name_ref()?;
210-
if path.qualifier().is_none() {
211-
return Some(name_ref);
212-
}
213-
}
214-
None
206+
self.expr()?.name_ref()
215207
}
216208
}
217209

0 commit comments

Comments
 (0)