aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide/src/references.rs25
-rw-r--r--crates/ide/src/references/rename.rs165
-rw-r--r--crates/ide_db/src/search.rs20
-rw-r--r--crates/syntax/src/ast/expr_ext.rs12
-rw-r--r--crates/syntax/src/ast/node_ext.rs10
5 files changed, 206 insertions, 26 deletions
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index e05465b32..5693dd400 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -110,14 +110,23 @@ pub(crate) fn find_all_refs(
110 .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) 110 .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)
111 .collect(); 111 .collect();
112 112
113 let decl_range = def.try_to_nav(sema.db)?.focus_or_full_range(); 113 let nav = def.try_to_nav(sema.db)?;
114 114 let decl_range = nav.focus_or_full_range();
115 let declaration = Declaration { 115
116 nav: def.try_to_nav(sema.db)?, 116 let mut kind = ReferenceKind::Other;
117 kind: ReferenceKind::Other, 117 if let Definition::Local(local) = def {
118 access: decl_access(&def, &syntax, decl_range), 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 }
119 }; 126 };
120 127
128 let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) };
129
121 Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) 130 Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references }))
122} 131}
123 132
@@ -613,7 +622,7 @@ fn foo() {
613 expect![[r#" 622 expect![[r#"
614 f RECORD_FIELD FileId(0) 15..21 15..16 Other 623 f RECORD_FIELD FileId(0) 15..21 15..16 Other
615 624
616 FileId(0) 55..56 Other Read 625 FileId(0) 55..56 RecordFieldExprOrPat Read
617 FileId(0) 68..69 Other Write 626 FileId(0) 68..69 Other Write
618 "#]], 627 "#]],
619 ); 628 );
@@ -748,7 +757,7 @@ fn f() -> m::En {
748 expect![[r#" 757 expect![[r#"
749 field RECORD_FIELD FileId(0) 56..65 56..61 Other 758 field RECORD_FIELD FileId(0) 56..65 56..61 Other
750 759
751 FileId(0) 125..130 Other Read 760 FileId(0) 125..130 RecordFieldExprOrPat Read
752 "#]], 761 "#]],
753 ); 762 );
754 } 763 }
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 26ac2371a..b8725693a 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -1,7 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{Module, ModuleDef, ModuleSource, Semantics}; 3use hir::{Module, ModuleDef, ModuleSource, Semantics};
4use ide_db::base_db::SourceDatabaseExt; 4use ide_db::base_db::{FileRange, SourceDatabaseExt};
5use ide_db::{ 5use ide_db::{
6 defs::{Definition, NameClass, NameRefClass}, 6 defs::{Definition, NameClass, NameRefClass},
7 RootDatabase, 7 RootDatabase,
@@ -106,9 +106,12 @@ fn find_module_at_offset(
106 Some(module) 106 Some(module)
107} 107}
108 108
109fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFileEdit { 109fn source_edit_from_reference(
110 sema: &Semantics<RootDatabase>,
111 reference: Reference,
112 new_name: &str,
113) -> SourceFileEdit {
110 let mut replacement_text = String::new(); 114 let mut replacement_text = String::new();
111 let file_id = reference.file_range.file_id;
112 let range = match reference.kind { 115 let range = match reference.kind {
113 ReferenceKind::FieldShorthandForField => { 116 ReferenceKind::FieldShorthandForField => {
114 mark::hit!(test_rename_struct_field_for_shorthand); 117 mark::hit!(test_rename_struct_field_for_shorthand);
@@ -122,12 +125,48 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
122 replacement_text.push_str(new_name); 125 replacement_text.push_str(new_name);
123 TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) 126 TextRange::new(reference.file_range.range.end(), reference.file_range.range.end())
124 } 127 }
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 }
125 _ => { 133 _ => {
126 replacement_text.push_str(new_name); 134 replacement_text.push_str(new_name);
127 reference.file_range.range 135 reference.file_range.range
128 } 136 }
129 }; 137 };
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
144fn 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)
131} 170}
132 171
133fn rename_mod( 172fn rename_mod(
@@ -170,7 +209,7 @@ fn rename_mod(
170 let ref_edits = refs 209 let ref_edits = refs
171 .references 210 .references
172 .into_iter() 211 .into_iter()
173 .map(|reference| source_edit_from_reference(reference, new_name)); 212 .map(|reference| source_edit_from_reference(sema, reference, new_name));
174 source_file_edits.extend(ref_edits); 213 source_file_edits.extend(ref_edits);
175 214
176 Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) 215 Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
@@ -211,7 +250,7 @@ fn rename_to_self(
211 250
212 let mut edits = usages 251 let mut edits = usages
213 .into_iter() 252 .into_iter()
214 .map(|reference| source_edit_from_reference(reference, "self")) 253 .map(|reference| source_edit_from_reference(sema, reference, "self"))
215 .collect::<Vec<_>>(); 254 .collect::<Vec<_>>();
216 255
217 edits.push(SourceFileEdit { 256 edits.push(SourceFileEdit {
@@ -300,7 +339,7 @@ fn rename_reference(
300 339
301 let edit = refs 340 let edit = refs
302 .into_iter() 341 .into_iter()
303 .map(|reference| source_edit_from_reference(reference, new_name)) 342 .map(|reference| source_edit_from_reference(sema, reference, new_name))
304 .collect::<Vec<_>>(); 343 .collect::<Vec<_>>();
305 344
306 if edit.is_empty() { 345 if edit.is_empty() {
@@ -1097,4 +1136,116 @@ impl Foo {
1097"#, 1136"#,
1098 ); 1137 );
1099 } 1138 }
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#"
1146struct Foo { i<|>: i32 }
1147
1148fn foo(bar: i32) -> Foo {
1149 Foo { i: bar }
1150}
1151"#,
1152 r#"
1153struct Foo { bar: i32 }
1154
1155fn 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#"
1167struct Foo { i<|>: i32 }
1168
1169fn foo(foo: Foo) {
1170 let Foo { i: baz } = foo;
1171 let _ = baz;
1172}
1173"#,
1174 r#"
1175struct Foo { baz: i32 }
1176
1177fn 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#"
1188struct Foo {
1189 i: i32,
1190}
1191
1192fn foo(foo: Foo) {
1193 let Foo { i: bar } = foo;
1194 let _ = bar;
1195}
1196"#;
1197 check(
1198 "bar",
1199 r#"
1200struct Foo {
1201 i: i32,
1202}
1203
1204fn foo(foo: Foo) {
1205 let Foo { i: b } = foo;
1206 let _ = b<|>;
1207}
1208"#,
1209 expected_fixture,
1210 );
1211 check(
1212 "bar",
1213 r#"
1214struct Foo {
1215 i: i32,
1216}
1217
1218fn 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#"
1232struct Foo {
1233 i: i32
1234}
1235
1236fn foo(Foo { i }: foo) -> i32 {
1237 i<|>
1238}
1239"#,
1240 r#"
1241struct Foo {
1242 i: i32
1243}
1244
1245fn foo(Foo { i: bar }: foo) -> i32 {
1246 bar
1247}
1248"#,
1249 )
1250 }
1100} 1251}
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index a24335240..a3e765d05 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -30,6 +30,7 @@ pub enum ReferenceKind {
30 FieldShorthandForField, 30 FieldShorthandForField,
31 FieldShorthandForLocal, 31 FieldShorthandForLocal,
32 StructLiteral, 32 StructLiteral,
33 RecordFieldExprOrPat,
33 Other, 34 Other,
34} 35}
35 36
@@ -278,8 +279,9 @@ impl<'a> FindUsages<'a> {
278 ) -> bool { 279 ) -> bool {
279 match NameRefClass::classify(self.sema, &name_ref) { 280 match NameRefClass::classify(self.sema, &name_ref) {
280 Some(NameRefClass::Definition(def)) if &def == self.def => { 281 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 let kind = if is_record_field_expr_or_pat(&name_ref) {
282 { 283 ReferenceKind::RecordFieldExprOrPat
284 } else if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) {
283 ReferenceKind::StructLiteral 285 ReferenceKind::StructLiteral
284 } else { 286 } else {
285 ReferenceKind::Other 287 ReferenceKind::Other
@@ -385,3 +387,17 @@ fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool {
385 .map(|p| p.name_ref().as_ref() == Some(name_ref)) 387 .map(|p| p.name_ref().as_ref() == Some(name_ref))
386 .unwrap_or(false) 388 .unwrap_or(false)
387} 389}
390
391fn 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}
diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs
index 9253c97d0..e4a9b945c 100644
--- a/crates/syntax/src/ast/expr_ext.rs
+++ b/crates/syntax/src/ast/expr_ext.rs
@@ -22,6 +22,18 @@ impl ast::Expr {
22 _ => false, 22 _ => false,
23 } 23 }
24 } 24 }
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 }
25} 37}
26 38
27#[derive(Debug, Clone, PartialEq, Eq)] 39#[derive(Debug, Clone, PartialEq, Eq)]
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index ce35ac01a..b70b840b8 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -203,15 +203,7 @@ impl ast::RecordExprField {
203 if let Some(name_ref) = self.name_ref() { 203 if let Some(name_ref) = self.name_ref() {
204 return Some(name_ref); 204 return Some(name_ref);
205 } 205 }
206 if let Some(ast::Expr::PathExpr(expr)) = self.expr() { 206 self.expr()?.name_ref()
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
215 } 207 }
216} 208}
217 209