diff options
-rw-r--r-- | crates/ide/src/references.rs | 25 | ||||
-rw-r--r-- | crates/ide/src/references/rename.rs | 165 | ||||
-rw-r--r-- | crates/ide_db/src/search.rs | 20 | ||||
-rw-r--r-- | crates/syntax/src/ast/expr_ext.rs | 12 | ||||
-rw-r--r-- | crates/syntax/src/ast/node_ext.rs | 10 |
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 | ||
3 | use hir::{Module, ModuleDef, ModuleSource, Semantics}; | 3 | use hir::{Module, ModuleDef, ModuleSource, Semantics}; |
4 | use ide_db::base_db::SourceDatabaseExt; | 4 | use ide_db::base_db::{FileRange, SourceDatabaseExt}; |
5 | use ide_db::{ | 5 | use 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 | ||
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 { | ||
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 | |||
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) | ||
131 | } | 170 | } |
132 | 171 | ||
133 | fn rename_mod( | 172 | fn 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#" | ||
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 | } | ||
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 | |||
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 | } | ||
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 | ||