diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-02-12 18:01:36 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2021-02-12 18:01:36 +0000 |
commit | 88253907f4bc3beaa7b8f2e58cb652f653f92d56 (patch) | |
tree | 1669ff0e6ab614c679f245f84d0f9a8202366e0e /crates/ide | |
parent | 4d51b5644458c7dcb97a4d445f1b379cd2548a78 (diff) | |
parent | fd6cf4d566174dbdb50259bbbfdaf5a12f81544d (diff) |
Merge #7358
7358: Refactor reference searching to work with the ast r=matklad a=Veykril
Addresses #4290
This PR is still a bit unpolished. Its main purpose for now is to discuss the direction of the changes as to whether this seems to be the right approach or not. I annotated a few parts with reviews to give a better overwiew without having to read into it too much.
Big part of the diff are test output changes in the `references` module.
Co-authored-by: Lukas Wirth <[email protected]>
Diffstat (limited to 'crates/ide')
-rw-r--r-- | crates/ide/src/call_hierarchy.rs | 8 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ide/src/references.rs | 370 | ||||
-rw-r--r-- | crates/ide/src/references/rename.rs | 197 |
4 files changed, 304 insertions, 275 deletions
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index b10a0a78b..b848945d7 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs | |||
@@ -47,11 +47,11 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
47 | 47 | ||
48 | let mut calls = CallLocations::default(); | 48 | let mut calls = CallLocations::default(); |
49 | 49 | ||
50 | for (&file_id, references) in refs.references().iter() { | 50 | for (file_id, references) in refs.references { |
51 | let file = sema.parse(file_id); | 51 | let file = sema.parse(file_id); |
52 | let file = file.syntax(); | 52 | let file = file.syntax(); |
53 | for reference in references { | 53 | for (r_range, _) in references { |
54 | let token = file.token_at_offset(reference.range.start()).next()?; | 54 | let token = file.token_at_offset(r_range.start()).next()?; |
55 | let token = sema.descend_into_macros(token); | 55 | let token = sema.descend_into_macros(token); |
56 | let syntax = token.parent(); | 56 | let syntax = token.parent(); |
57 | 57 | ||
@@ -61,7 +61,7 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
61 | let def = sema.to_def(&fn_)?; | 61 | let def = sema.to_def(&fn_)?; |
62 | def.try_to_nav(sema.db) | 62 | def.try_to_nav(sema.db) |
63 | }) { | 63 | }) { |
64 | let relative_range = reference.range; | 64 | let relative_range = r_range; |
65 | calls.add(&nav, relative_range); | 65 | calls.add(&nav, relative_range); |
66 | } | 66 | } |
67 | } | 67 | } |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 989e94a31..592b12925 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -73,7 +73,7 @@ pub use crate::{ | |||
73 | inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, | 73 | inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, |
74 | markup::Markup, | 74 | markup::Markup, |
75 | prime_caches::PrimeCachesProgress, | 75 | prime_caches::PrimeCachesProgress, |
76 | references::{rename::RenameError, Declaration, ReferenceSearchResult}, | 76 | references::{rename::RenameError, ReferenceSearchResult}, |
77 | runnables::{Runnable, RunnableKind, TestId}, | 77 | runnables::{Runnable, RunnableKind, TestId}, |
78 | syntax_highlighting::{ | 78 | syntax_highlighting::{ |
79 | tags::{Highlight, HlMod, HlMods, HlPunct, HlTag}, | 79 | tags::{Highlight, HlMod, HlMods, HlPunct, HlTag}, |
@@ -94,7 +94,7 @@ pub use ide_db::{ | |||
94 | call_info::CallInfo, | 94 | call_info::CallInfo, |
95 | label::Label, | 95 | label::Label, |
96 | line_index::{LineCol, LineIndex}, | 96 | line_index::{LineCol, LineIndex}, |
97 | search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope}, | 97 | search::{ReferenceAccess, SearchScope}, |
98 | source_change::{FileSystemEdit, SourceChange}, | 98 | source_change::{FileSystemEdit, SourceChange}, |
99 | symbol_index::Query, | 99 | symbol_index::Query, |
100 | RootDatabase, | 100 | RootDatabase, |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 6999dacee..c7cefb3b6 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -11,14 +11,14 @@ | |||
11 | 11 | ||
12 | pub(crate) mod rename; | 12 | pub(crate) mod rename; |
13 | 13 | ||
14 | use either::Either; | ||
15 | use hir::Semantics; | 14 | use hir::Semantics; |
16 | use ide_db::{ | 15 | use ide_db::{ |
17 | base_db::FileId, | 16 | base_db::FileId, |
18 | defs::{Definition, NameClass, NameRefClass}, | 17 | defs::{Definition, NameClass, NameRefClass}, |
19 | search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope, UsageSearchResult}, | 18 | search::{ReferenceAccess, SearchScope}, |
20 | RootDatabase, | 19 | RootDatabase, |
21 | }; | 20 | }; |
21 | use rustc_hash::FxHashMap; | ||
22 | use syntax::{ | 22 | use syntax::{ |
23 | algo::find_node_at_offset, | 23 | algo::find_node_at_offset, |
24 | ast::{self, NameOwner}, | 24 | ast::{self, NameOwner}, |
@@ -29,52 +29,16 @@ use crate::{display::TryToNav, FilePosition, NavigationTarget}; | |||
29 | 29 | ||
30 | #[derive(Debug, Clone)] | 30 | #[derive(Debug, Clone)] |
31 | pub struct ReferenceSearchResult { | 31 | pub struct ReferenceSearchResult { |
32 | declaration: Declaration, | 32 | pub declaration: Declaration, |
33 | references: UsageSearchResult, | 33 | pub references: FxHashMap<FileId, Vec<(TextRange, Option<ReferenceAccess>)>>, |
34 | } | 34 | } |
35 | 35 | ||
36 | #[derive(Debug, Clone)] | 36 | #[derive(Debug, Clone)] |
37 | pub struct Declaration { | 37 | pub struct Declaration { |
38 | pub nav: NavigationTarget, | 38 | pub nav: NavigationTarget, |
39 | pub kind: ReferenceKind, | ||
40 | pub access: Option<ReferenceAccess>, | 39 | pub access: Option<ReferenceAccess>, |
41 | } | 40 | } |
42 | 41 | ||
43 | impl ReferenceSearchResult { | ||
44 | pub fn references(&self) -> &UsageSearchResult { | ||
45 | &self.references | ||
46 | } | ||
47 | |||
48 | pub fn references_with_declaration(mut self) -> UsageSearchResult { | ||
49 | let decl_ref = FileReference { | ||
50 | range: self.declaration.nav.focus_or_full_range(), | ||
51 | kind: self.declaration.kind, | ||
52 | access: self.declaration.access, | ||
53 | }; | ||
54 | let file_id = self.declaration.nav.file_id; | ||
55 | self.references.references.entry(file_id).or_default().push(decl_ref); | ||
56 | self.references | ||
57 | } | ||
58 | |||
59 | /// Total number of references | ||
60 | /// At least 1 since all valid references should | ||
61 | /// Have a declaration | ||
62 | pub fn len(&self) -> usize { | ||
63 | self.references.len() + 1 | ||
64 | } | ||
65 | } | ||
66 | |||
67 | // allow turning ReferenceSearchResult into an iterator | ||
68 | // over References | ||
69 | impl IntoIterator for ReferenceSearchResult { | ||
70 | type Item = (FileId, Vec<FileReference>); | ||
71 | type IntoIter = std::collections::hash_map::IntoIter<FileId, Vec<FileReference>>; | ||
72 | |||
73 | fn into_iter(self) -> Self::IntoIter { | ||
74 | self.references_with_declaration().into_iter() | ||
75 | } | ||
76 | } | ||
77 | |||
78 | pub(crate) fn find_all_refs( | 42 | pub(crate) fn find_all_refs( |
79 | sema: &Semantics<RootDatabase>, | 43 | sema: &Semantics<RootDatabase>, |
80 | position: FilePosition, | 44 | position: FilePosition, |
@@ -83,83 +47,72 @@ pub(crate) fn find_all_refs( | |||
83 | let _p = profile::span("find_all_refs"); | 47 | let _p = profile::span("find_all_refs"); |
84 | let syntax = sema.parse(position.file_id).syntax().clone(); | 48 | let syntax = sema.parse(position.file_id).syntax().clone(); |
85 | 49 | ||
86 | let (opt_name, search_kind) = if let Some(name) = | 50 | let (opt_name, ctor_filter): (_, Option<fn(&_) -> bool>) = if let Some(name) = |
87 | get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) | 51 | get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) |
88 | { | 52 | { |
89 | (Some(name), ReferenceKind::StructLiteral) | 53 | ( |
54 | Some(name), | ||
55 | Some(|name_ref| is_record_lit_name_ref(name_ref) || is_call_expr_name_ref(name_ref)), | ||
56 | ) | ||
90 | } else if let Some(name) = get_enum_def_name_for_struct_literal_search(&sema, &syntax, position) | 57 | } else if let Some(name) = get_enum_def_name_for_struct_literal_search(&sema, &syntax, position) |
91 | { | 58 | { |
92 | (Some(name), ReferenceKind::EnumLiteral) | 59 | (Some(name), Some(is_enum_lit_name_ref)) |
93 | } else { | 60 | } else { |
94 | ( | 61 | (sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset), None) |
95 | sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset), | ||
96 | ReferenceKind::Other, | ||
97 | ) | ||
98 | }; | 62 | }; |
99 | 63 | ||
100 | let def = find_name(&sema, &syntax, position, opt_name)?; | 64 | let def = find_def(&sema, &syntax, position, opt_name)?; |
101 | 65 | ||
102 | let mut usages = def.usages(sema).set_scope(search_scope).all(); | 66 | let mut usages = def.usages(sema).set_scope(search_scope).all(); |
103 | usages | 67 | if let Some(ctor_filter) = ctor_filter { |
104 | .references | 68 | // filter for constructor-literals |
105 | .values_mut() | 69 | usages.references.values_mut().for_each(|it| { |
106 | .for_each(|it| it.retain(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)); | 70 | it.retain(|reference| reference.name.as_name_ref().map_or(false, ctor_filter)); |
107 | usages.references.retain(|_, it| !it.is_empty()); | 71 | }); |
108 | 72 | usages.references.retain(|_, it| !it.is_empty()); | |
73 | } | ||
109 | let nav = def.try_to_nav(sema.db)?; | 74 | let nav = def.try_to_nav(sema.db)?; |
110 | let decl_range = nav.focus_or_full_range(); | 75 | let decl_range = nav.focus_or_full_range(); |
111 | 76 | ||
112 | let mut kind = ReferenceKind::Other; | 77 | let declaration = Declaration { nav, access: decl_access(&def, &syntax, decl_range) }; |
113 | if let Definition::Local(local) = def { | 78 | let references = usages |
114 | match local.source(sema.db).value { | 79 | .into_iter() |
115 | Either::Left(pat) => { | 80 | .map(|(file_id, refs)| { |
116 | if matches!( | 81 | (file_id, refs.into_iter().map(|file_ref| (file_ref.range, file_ref.access)).collect()) |
117 | pat.syntax().parent().and_then(ast::RecordPatField::cast), | 82 | }) |
118 | Some(pat_field) if pat_field.name_ref().is_none() | 83 | .collect(); |
119 | ) { | ||
120 | kind = ReferenceKind::FieldShorthandForLocal; | ||
121 | } | ||
122 | } | ||
123 | Either::Right(_) => kind = ReferenceKind::SelfParam, | ||
124 | } | ||
125 | } else if matches!( | ||
126 | def, | ||
127 | Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) | ||
128 | ) { | ||
129 | kind = ReferenceKind::Lifetime; | ||
130 | }; | ||
131 | |||
132 | let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; | ||
133 | 84 | ||
134 | Some(ReferenceSearchResult { declaration, references: usages }) | 85 | Some(ReferenceSearchResult { declaration, references }) |
135 | } | 86 | } |
136 | 87 | ||
137 | fn find_name( | 88 | fn find_def( |
138 | sema: &Semantics<RootDatabase>, | 89 | sema: &Semantics<RootDatabase>, |
139 | syntax: &SyntaxNode, | 90 | syntax: &SyntaxNode, |
140 | position: FilePosition, | 91 | position: FilePosition, |
141 | opt_name: Option<ast::Name>, | 92 | opt_name: Option<ast::Name>, |
142 | ) -> Option<Definition> { | 93 | ) -> Option<Definition> { |
143 | let def = if let Some(name) = opt_name { | 94 | if let Some(name) = opt_name { |
144 | NameClass::classify(sema, &name)?.referenced_or_defined(sema.db) | 95 | let class = NameClass::classify(sema, &name)?; |
96 | Some(class.referenced_or_defined(sema.db)) | ||
145 | } else if let Some(lifetime) = | 97 | } else if let Some(lifetime) = |
146 | sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) | 98 | sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) |
147 | { | 99 | { |
148 | if let Some(def) = | 100 | let def = if let Some(def) = |
149 | NameRefClass::classify_lifetime(sema, &lifetime).map(|class| class.referenced(sema.db)) | 101 | NameRefClass::classify_lifetime(sema, &lifetime).map(|class| class.referenced(sema.db)) |
150 | { | 102 | { |
151 | def | 103 | def |
152 | } else { | 104 | } else { |
153 | NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db) | 105 | NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db) |
154 | } | 106 | }; |
107 | Some(def) | ||
155 | } else if let Some(name_ref) = | 108 | } else if let Some(name_ref) = |
156 | sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset) | 109 | sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset) |
157 | { | 110 | { |
158 | NameRefClass::classify(sema, &name_ref)?.referenced(sema.db) | 111 | let class = NameRefClass::classify(sema, &name_ref)?; |
112 | Some(class.referenced(sema.db)) | ||
159 | } else { | 113 | } else { |
160 | return None; | 114 | None |
161 | }; | 115 | } |
162 | Some(def) | ||
163 | } | 116 | } |
164 | 117 | ||
165 | fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { | 118 | fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { |
@@ -235,6 +188,43 @@ fn get_enum_def_name_for_struct_literal_search( | |||
235 | None | 188 | None |
236 | } | 189 | } |
237 | 190 | ||
191 | fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool { | ||
192 | name_ref | ||
193 | .syntax() | ||
194 | .ancestors() | ||
195 | .find_map(ast::CallExpr::cast) | ||
196 | .and_then(|c| match c.expr()? { | ||
197 | ast::Expr::PathExpr(p) => { | ||
198 | Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref)) | ||
199 | } | ||
200 | _ => None, | ||
201 | }) | ||
202 | .unwrap_or(false) | ||
203 | } | ||
204 | |||
205 | fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool { | ||
206 | name_ref | ||
207 | .syntax() | ||
208 | .ancestors() | ||
209 | .find_map(ast::RecordExpr::cast) | ||
210 | .and_then(|l| l.path()) | ||
211 | .and_then(|p| p.segment()) | ||
212 | .map(|p| p.name_ref().as_ref() == Some(name_ref)) | ||
213 | .unwrap_or(false) | ||
214 | } | ||
215 | |||
216 | fn is_enum_lit_name_ref(name_ref: &ast::NameRef) -> bool { | ||
217 | name_ref | ||
218 | .syntax() | ||
219 | .ancestors() | ||
220 | .find_map(ast::PathExpr::cast) | ||
221 | .and_then(|p| p.path()) | ||
222 | .and_then(|p| p.qualifier()) | ||
223 | .and_then(|p| p.segment()) | ||
224 | .map(|p| p.name_ref().as_ref() == Some(name_ref)) | ||
225 | .unwrap_or(false) | ||
226 | } | ||
227 | |||
238 | #[cfg(test)] | 228 | #[cfg(test)] |
239 | mod tests { | 229 | mod tests { |
240 | use expect_test::{expect, Expect}; | 230 | use expect_test::{expect, Expect}; |
@@ -259,9 +249,9 @@ fn main() { | |||
259 | } | 249 | } |
260 | "#, | 250 | "#, |
261 | expect![[r#" | 251 | expect![[r#" |
262 | Foo Struct FileId(0) 0..26 7..10 Other | 252 | Foo Struct FileId(0) 0..26 7..10 |
263 | 253 | ||
264 | FileId(0) 101..104 StructLiteral | 254 | FileId(0) 101..104 |
265 | "#]], | 255 | "#]], |
266 | ); | 256 | ); |
267 | } | 257 | } |
@@ -277,10 +267,10 @@ struct Foo$0 {} | |||
277 | } | 267 | } |
278 | "#, | 268 | "#, |
279 | expect![[r#" | 269 | expect![[r#" |
280 | Foo Struct FileId(0) 0..13 7..10 Other | 270 | Foo Struct FileId(0) 0..13 7..10 |
281 | 271 | ||
282 | FileId(0) 41..44 Other | 272 | FileId(0) 41..44 |
283 | FileId(0) 54..57 StructLiteral | 273 | FileId(0) 54..57 |
284 | "#]], | 274 | "#]], |
285 | ); | 275 | ); |
286 | } | 276 | } |
@@ -296,9 +286,9 @@ struct Foo<T> $0{} | |||
296 | } | 286 | } |
297 | "#, | 287 | "#, |
298 | expect![[r#" | 288 | expect![[r#" |
299 | Foo Struct FileId(0) 0..16 7..10 Other | 289 | Foo Struct FileId(0) 0..16 7..10 |
300 | 290 | ||
301 | FileId(0) 64..67 StructLiteral | 291 | FileId(0) 64..67 |
302 | "#]], | 292 | "#]], |
303 | ); | 293 | ); |
304 | } | 294 | } |
@@ -315,9 +305,9 @@ fn main() { | |||
315 | } | 305 | } |
316 | "#, | 306 | "#, |
317 | expect![[r#" | 307 | expect![[r#" |
318 | Foo Struct FileId(0) 0..16 7..10 Other | 308 | Foo Struct FileId(0) 0..16 7..10 |
319 | 309 | ||
320 | FileId(0) 54..57 StructLiteral | 310 | FileId(0) 54..57 |
321 | "#]], | 311 | "#]], |
322 | ); | 312 | ); |
323 | } | 313 | } |
@@ -336,9 +326,9 @@ fn main() { | |||
336 | } | 326 | } |
337 | "#, | 327 | "#, |
338 | expect![[r#" | 328 | expect![[r#" |
339 | Foo Enum FileId(0) 0..26 5..8 Other | 329 | Foo Enum FileId(0) 0..26 5..8 |
340 | 330 | ||
341 | FileId(0) 63..66 EnumLiteral | 331 | FileId(0) 63..66 |
342 | "#]], | 332 | "#]], |
343 | ); | 333 | ); |
344 | } | 334 | } |
@@ -357,10 +347,10 @@ fn main() { | |||
357 | } | 347 | } |
358 | "#, | 348 | "#, |
359 | expect![[r#" | 349 | expect![[r#" |
360 | Foo Enum FileId(0) 0..26 5..8 Other | 350 | Foo Enum FileId(0) 0..26 5..8 |
361 | 351 | ||
362 | FileId(0) 50..53 Other | 352 | FileId(0) 50..53 |
363 | FileId(0) 63..66 EnumLiteral | 353 | FileId(0) 63..66 |
364 | "#]], | 354 | "#]], |
365 | ); | 355 | ); |
366 | } | 356 | } |
@@ -379,9 +369,9 @@ fn main() { | |||
379 | } | 369 | } |
380 | "#, | 370 | "#, |
381 | expect![[r#" | 371 | expect![[r#" |
382 | Foo Enum FileId(0) 0..32 5..8 Other | 372 | Foo Enum FileId(0) 0..32 5..8 |
383 | 373 | ||
384 | FileId(0) 73..76 EnumLiteral | 374 | FileId(0) 73..76 |
385 | "#]], | 375 | "#]], |
386 | ); | 376 | ); |
387 | } | 377 | } |
@@ -400,9 +390,9 @@ fn main() { | |||
400 | } | 390 | } |
401 | "#, | 391 | "#, |
402 | expect![[r#" | 392 | expect![[r#" |
403 | Foo Enum FileId(0) 0..33 5..8 Other | 393 | Foo Enum FileId(0) 0..33 5..8 |
404 | 394 | ||
405 | FileId(0) 70..73 EnumLiteral | 395 | FileId(0) 70..73 |
406 | "#]], | 396 | "#]], |
407 | ); | 397 | ); |
408 | } | 398 | } |
@@ -423,12 +413,12 @@ fn main() { | |||
423 | i = 5; | 413 | i = 5; |
424 | }"#, | 414 | }"#, |
425 | expect![[r#" | 415 | expect![[r#" |
426 | i Local FileId(0) 20..25 24..25 Other Write | 416 | i Local FileId(0) 20..25 24..25 Write |
427 | 417 | ||
428 | FileId(0) 50..51 Other Write | 418 | FileId(0) 50..51 Write |
429 | FileId(0) 54..55 Other Read | 419 | FileId(0) 54..55 Read |
430 | FileId(0) 76..77 Other Write | 420 | FileId(0) 76..77 Write |
431 | FileId(0) 94..95 Other Write | 421 | FileId(0) 94..95 Write |
432 | "#]], | 422 | "#]], |
433 | ); | 423 | ); |
434 | } | 424 | } |
@@ -447,10 +437,10 @@ fn bar() { | |||
447 | } | 437 | } |
448 | "#, | 438 | "#, |
449 | expect![[r#" | 439 | expect![[r#" |
450 | spam Local FileId(0) 19..23 19..23 Other | 440 | spam Local FileId(0) 19..23 19..23 |
451 | 441 | ||
452 | FileId(0) 34..38 Other Read | 442 | FileId(0) 34..38 Read |
453 | FileId(0) 41..45 Other Read | 443 | FileId(0) 41..45 Read |
454 | "#]], | 444 | "#]], |
455 | ); | 445 | ); |
456 | } | 446 | } |
@@ -462,9 +452,9 @@ fn bar() { | |||
462 | fn foo(i : u32) -> u32 { i$0 } | 452 | fn foo(i : u32) -> u32 { i$0 } |
463 | "#, | 453 | "#, |
464 | expect![[r#" | 454 | expect![[r#" |
465 | i ValueParam FileId(0) 7..8 7..8 Other | 455 | i ValueParam FileId(0) 7..8 7..8 |
466 | 456 | ||
467 | FileId(0) 25..26 Other Read | 457 | FileId(0) 25..26 Read |
468 | "#]], | 458 | "#]], |
469 | ); | 459 | ); |
470 | } | 460 | } |
@@ -476,9 +466,9 @@ fn foo(i : u32) -> u32 { i$0 } | |||
476 | fn foo(i$0 : u32) -> u32 { i } | 466 | fn foo(i$0 : u32) -> u32 { i } |
477 | "#, | 467 | "#, |
478 | expect![[r#" | 468 | expect![[r#" |
479 | i ValueParam FileId(0) 7..8 7..8 Other | 469 | i ValueParam FileId(0) 7..8 7..8 |
480 | 470 | ||
481 | FileId(0) 25..26 Other Read | 471 | FileId(0) 25..26 Read |
482 | "#]], | 472 | "#]], |
483 | ); | 473 | ); |
484 | } | 474 | } |
@@ -497,9 +487,9 @@ fn main(s: Foo) { | |||
497 | } | 487 | } |
498 | "#, | 488 | "#, |
499 | expect![[r#" | 489 | expect![[r#" |
500 | spam Field FileId(0) 17..30 21..25 Other | 490 | spam Field FileId(0) 17..30 21..25 |
501 | 491 | ||
502 | FileId(0) 67..71 Other Read | 492 | FileId(0) 67..71 Read |
503 | "#]], | 493 | "#]], |
504 | ); | 494 | ); |
505 | } | 495 | } |
@@ -514,7 +504,7 @@ impl Foo { | |||
514 | } | 504 | } |
515 | "#, | 505 | "#, |
516 | expect![[r#" | 506 | expect![[r#" |
517 | f Function FileId(0) 27..43 30..31 Other | 507 | f Function FileId(0) 27..43 30..31 |
518 | 508 | ||
519 | "#]], | 509 | "#]], |
520 | ); | 510 | ); |
@@ -531,7 +521,7 @@ enum Foo { | |||
531 | } | 521 | } |
532 | "#, | 522 | "#, |
533 | expect![[r#" | 523 | expect![[r#" |
534 | B Variant FileId(0) 22..23 22..23 Other | 524 | B Variant FileId(0) 22..23 22..23 |
535 | 525 | ||
536 | "#]], | 526 | "#]], |
537 | ); | 527 | ); |
@@ -548,7 +538,7 @@ enum Foo { | |||
548 | } | 538 | } |
549 | "#, | 539 | "#, |
550 | expect![[r#" | 540 | expect![[r#" |
551 | field Field FileId(0) 26..35 26..31 Other | 541 | field Field FileId(0) 26..35 26..31 |
552 | 542 | ||
553 | "#]], | 543 | "#]], |
554 | ); | 544 | ); |
@@ -589,10 +579,10 @@ fn f() { | |||
589 | } | 579 | } |
590 | "#, | 580 | "#, |
591 | expect![[r#" | 581 | expect![[r#" |
592 | Foo Struct FileId(1) 17..51 28..31 Other | 582 | Foo Struct FileId(1) 17..51 28..31 |
593 | 583 | ||
594 | FileId(0) 53..56 StructLiteral | 584 | FileId(0) 53..56 |
595 | FileId(2) 79..82 StructLiteral | 585 | FileId(2) 79..82 |
596 | "#]], | 586 | "#]], |
597 | ); | 587 | ); |
598 | } | 588 | } |
@@ -619,9 +609,9 @@ pub struct Foo { | |||
619 | } | 609 | } |
620 | "#, | 610 | "#, |
621 | expect![[r#" | 611 | expect![[r#" |
622 | foo Module FileId(1) 0..35 Other | 612 | foo Module FileId(1) 0..35 |
623 | 613 | ||
624 | FileId(0) 14..17 Other | 614 | FileId(0) 14..17 |
625 | "#]], | 615 | "#]], |
626 | ); | 616 | ); |
627 | } | 617 | } |
@@ -647,10 +637,10 @@ pub(super) struct Foo$0 { | |||
647 | } | 637 | } |
648 | "#, | 638 | "#, |
649 | expect![[r#" | 639 | expect![[r#" |
650 | Foo Struct FileId(2) 0..41 18..21 Other | 640 | Foo Struct FileId(2) 0..41 18..21 |
651 | 641 | ||
652 | FileId(1) 20..23 Other | 642 | FileId(1) 20..23 |
653 | FileId(1) 47..50 StructLiteral | 643 | FileId(1) 47..50 |
654 | "#]], | 644 | "#]], |
655 | ); | 645 | ); |
656 | } | 646 | } |
@@ -675,10 +665,10 @@ pub(super) struct Foo$0 { | |||
675 | code, | 665 | code, |
676 | None, | 666 | None, |
677 | expect![[r#" | 667 | expect![[r#" |
678 | quux Function FileId(0) 19..35 26..30 Other | 668 | quux Function FileId(0) 19..35 26..30 |
679 | 669 | ||
680 | FileId(1) 16..20 StructLiteral | 670 | FileId(1) 16..20 |
681 | FileId(2) 16..20 StructLiteral | 671 | FileId(2) 16..20 |
682 | "#]], | 672 | "#]], |
683 | ); | 673 | ); |
684 | 674 | ||
@@ -686,9 +676,9 @@ pub(super) struct Foo$0 { | |||
686 | code, | 676 | code, |
687 | Some(SearchScope::single_file(FileId(2))), | 677 | Some(SearchScope::single_file(FileId(2))), |
688 | expect![[r#" | 678 | expect![[r#" |
689 | quux Function FileId(0) 19..35 26..30 Other | 679 | quux Function FileId(0) 19..35 26..30 |
690 | 680 | ||
691 | FileId(2) 16..20 StructLiteral | 681 | FileId(2) 16..20 |
692 | "#]], | 682 | "#]], |
693 | ); | 683 | ); |
694 | } | 684 | } |
@@ -706,10 +696,10 @@ fn foo() { | |||
706 | } | 696 | } |
707 | "#, | 697 | "#, |
708 | expect![[r#" | 698 | expect![[r#" |
709 | m1 Macro FileId(0) 0..46 29..31 Other | 699 | m1 Macro FileId(0) 0..46 29..31 |
710 | 700 | ||
711 | FileId(0) 63..65 StructLiteral | 701 | FileId(0) 63..65 |
712 | FileId(0) 73..75 StructLiteral | 702 | FileId(0) 73..75 |
713 | "#]], | 703 | "#]], |
714 | ); | 704 | ); |
715 | } | 705 | } |
@@ -724,10 +714,10 @@ fn foo() { | |||
724 | } | 714 | } |
725 | "#, | 715 | "#, |
726 | expect![[r#" | 716 | expect![[r#" |
727 | i Local FileId(0) 19..24 23..24 Other Write | 717 | i Local FileId(0) 19..24 23..24 Write |
728 | 718 | ||
729 | FileId(0) 34..35 Other Write | 719 | FileId(0) 34..35 Write |
730 | FileId(0) 38..39 Other Read | 720 | FileId(0) 38..39 Read |
731 | "#]], | 721 | "#]], |
732 | ); | 722 | ); |
733 | } | 723 | } |
@@ -746,10 +736,10 @@ fn foo() { | |||
746 | } | 736 | } |
747 | "#, | 737 | "#, |
748 | expect![[r#" | 738 | expect![[r#" |
749 | f Field FileId(0) 15..21 15..16 Other | 739 | f Field FileId(0) 15..21 15..16 |
750 | 740 | ||
751 | FileId(0) 55..56 RecordFieldExprOrPat Read | 741 | FileId(0) 55..56 Read |
752 | FileId(0) 68..69 Other Write | 742 | FileId(0) 68..69 Write |
753 | "#]], | 743 | "#]], |
754 | ); | 744 | ); |
755 | } | 745 | } |
@@ -764,9 +754,9 @@ fn foo() { | |||
764 | } | 754 | } |
765 | "#, | 755 | "#, |
766 | expect![[r#" | 756 | expect![[r#" |
767 | i Local FileId(0) 19..20 19..20 Other | 757 | i Local FileId(0) 19..20 19..20 |
768 | 758 | ||
769 | FileId(0) 26..27 Other Write | 759 | FileId(0) 26..27 Write |
770 | "#]], | 760 | "#]], |
771 | ); | 761 | ); |
772 | } | 762 | } |
@@ -788,9 +778,9 @@ fn main() { | |||
788 | } | 778 | } |
789 | "#, | 779 | "#, |
790 | expect![[r#" | 780 | expect![[r#" |
791 | new Function FileId(0) 54..81 61..64 Other | 781 | new Function FileId(0) 54..81 61..64 |
792 | 782 | ||
793 | FileId(0) 126..129 StructLiteral | 783 | FileId(0) 126..129 |
794 | "#]], | 784 | "#]], |
795 | ); | 785 | ); |
796 | } | 786 | } |
@@ -810,10 +800,10 @@ use crate::f; | |||
810 | fn g() { f(); } | 800 | fn g() { f(); } |
811 | "#, | 801 | "#, |
812 | expect![[r#" | 802 | expect![[r#" |
813 | f Function FileId(0) 22..31 25..26 Other | 803 | f Function FileId(0) 22..31 25..26 |
814 | 804 | ||
815 | FileId(1) 11..12 Other | 805 | FileId(1) 11..12 |
816 | FileId(1) 24..25 StructLiteral | 806 | FileId(1) 24..25 |
817 | "#]], | 807 | "#]], |
818 | ); | 808 | ); |
819 | } | 809 | } |
@@ -833,9 +823,9 @@ fn f(s: S) { | |||
833 | } | 823 | } |
834 | "#, | 824 | "#, |
835 | expect![[r#" | 825 | expect![[r#" |
836 | field Field FileId(0) 15..24 15..20 Other | 826 | field Field FileId(0) 15..24 15..20 |
837 | 827 | ||
838 | FileId(0) 68..73 FieldShorthandForField Read | 828 | FileId(0) 68..73 Read |
839 | "#]], | 829 | "#]], |
840 | ); | 830 | ); |
841 | } | 831 | } |
@@ -857,9 +847,9 @@ fn f(e: En) { | |||
857 | } | 847 | } |
858 | "#, | 848 | "#, |
859 | expect![[r#" | 849 | expect![[r#" |
860 | field Field FileId(0) 32..41 32..37 Other | 850 | field Field FileId(0) 32..41 32..37 |
861 | 851 | ||
862 | FileId(0) 102..107 FieldShorthandForField Read | 852 | FileId(0) 102..107 Read |
863 | "#]], | 853 | "#]], |
864 | ); | 854 | ); |
865 | } | 855 | } |
@@ -881,9 +871,9 @@ fn f() -> m::En { | |||
881 | } | 871 | } |
882 | "#, | 872 | "#, |
883 | expect![[r#" | 873 | expect![[r#" |
884 | field Field FileId(0) 56..65 56..61 Other | 874 | field Field FileId(0) 56..65 56..61 |
885 | 875 | ||
886 | FileId(0) 125..130 RecordFieldExprOrPat Read | 876 | FileId(0) 125..130 Read |
887 | "#]], | 877 | "#]], |
888 | ); | 878 | ); |
889 | } | 879 | } |
@@ -906,10 +896,10 @@ impl Foo { | |||
906 | } | 896 | } |
907 | "#, | 897 | "#, |
908 | expect![[r#" | 898 | expect![[r#" |
909 | self SelfParam FileId(0) 47..51 47..51 SelfParam | 899 | self SelfParam FileId(0) 47..51 47..51 |
910 | 900 | ||
911 | FileId(0) 71..75 Other Read | 901 | FileId(0) 71..75 Read |
912 | FileId(0) 152..156 Other Read | 902 | FileId(0) 152..156 Read |
913 | "#]], | 903 | "#]], |
914 | ); | 904 | ); |
915 | } | 905 | } |
@@ -927,9 +917,9 @@ impl Foo { | |||
927 | } | 917 | } |
928 | "#, | 918 | "#, |
929 | expect![[r#" | 919 | expect![[r#" |
930 | self SelfParam FileId(0) 47..51 47..51 SelfParam | 920 | self SelfParam FileId(0) 47..51 47..51 |
931 | 921 | ||
932 | FileId(0) 63..67 Other Read | 922 | FileId(0) 63..67 Read |
933 | "#]], | 923 | "#]], |
934 | ); | 924 | ); |
935 | } | 925 | } |
@@ -945,7 +935,7 @@ impl Foo { | |||
945 | let mut actual = String::new(); | 935 | let mut actual = String::new(); |
946 | { | 936 | { |
947 | let decl = refs.declaration; | 937 | let decl = refs.declaration; |
948 | format_to!(actual, "{} {:?}", decl.nav.debug_render(), decl.kind); | 938 | format_to!(actual, "{}", decl.nav.debug_render()); |
949 | if let Some(access) = decl.access { | 939 | if let Some(access) = decl.access { |
950 | format_to!(actual, " {:?}", access) | 940 | format_to!(actual, " {:?}", access) |
951 | } | 941 | } |
@@ -953,9 +943,9 @@ impl Foo { | |||
953 | } | 943 | } |
954 | 944 | ||
955 | for (file_id, references) in refs.references { | 945 | for (file_id, references) in refs.references { |
956 | for r in references { | 946 | for (range, access) in references { |
957 | format_to!(actual, "{:?} {:?} {:?}", file_id, r.range, r.kind); | 947 | format_to!(actual, "{:?} {:?}", file_id, range); |
958 | if let Some(access) = r.access { | 948 | if let Some(access) = access { |
959 | format_to!(actual, " {:?}", access); | 949 | format_to!(actual, " {:?}", access); |
960 | } | 950 | } |
961 | actual += "\n"; | 951 | actual += "\n"; |
@@ -976,13 +966,13 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> { | |||
976 | } | 966 | } |
977 | "#, | 967 | "#, |
978 | expect![[r#" | 968 | expect![[r#" |
979 | 'a LifetimeParam FileId(0) 55..57 55..57 Lifetime | 969 | 'a LifetimeParam FileId(0) 55..57 55..57 |
980 | 970 | ||
981 | FileId(0) 63..65 Lifetime | 971 | FileId(0) 63..65 |
982 | FileId(0) 71..73 Lifetime | 972 | FileId(0) 71..73 |
983 | FileId(0) 82..84 Lifetime | 973 | FileId(0) 82..84 |
984 | FileId(0) 95..97 Lifetime | 974 | FileId(0) 95..97 |
985 | FileId(0) 106..108 Lifetime | 975 | FileId(0) 106..108 |
986 | "#]], | 976 | "#]], |
987 | ); | 977 | ); |
988 | } | 978 | } |
@@ -994,10 +984,10 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> { | |||
994 | type Foo<'a, T> where T: 'a$0 = &'a T; | 984 | type Foo<'a, T> where T: 'a$0 = &'a T; |
995 | "#, | 985 | "#, |
996 | expect![[r#" | 986 | expect![[r#" |
997 | 'a LifetimeParam FileId(0) 9..11 9..11 Lifetime | 987 | 'a LifetimeParam FileId(0) 9..11 9..11 |
998 | 988 | ||
999 | FileId(0) 25..27 Lifetime | 989 | FileId(0) 25..27 |
1000 | FileId(0) 31..33 Lifetime | 990 | FileId(0) 31..33 |
1001 | "#]], | 991 | "#]], |
1002 | ); | 992 | ); |
1003 | } | 993 | } |
@@ -1016,11 +1006,11 @@ impl<'a> Foo<'a> for &'a () { | |||
1016 | } | 1006 | } |
1017 | "#, | 1007 | "#, |
1018 | expect![[r#" | 1008 | expect![[r#" |
1019 | 'a LifetimeParam FileId(0) 47..49 47..49 Lifetime | 1009 | 'a LifetimeParam FileId(0) 47..49 47..49 |
1020 | 1010 | ||
1021 | FileId(0) 55..57 Lifetime | 1011 | FileId(0) 55..57 |
1022 | FileId(0) 64..66 Lifetime | 1012 | FileId(0) 64..66 |
1023 | FileId(0) 89..91 Lifetime | 1013 | FileId(0) 89..91 |
1024 | "#]], | 1014 | "#]], |
1025 | ); | 1015 | ); |
1026 | } | 1016 | } |
@@ -1036,9 +1026,9 @@ fn main() { | |||
1036 | } | 1026 | } |
1037 | "#, | 1027 | "#, |
1038 | expect![[r#" | 1028 | expect![[r#" |
1039 | a Local FileId(0) 59..60 59..60 Other | 1029 | a Local FileId(0) 59..60 59..60 |
1040 | 1030 | ||
1041 | FileId(0) 80..81 Other Read | 1031 | FileId(0) 80..81 Read |
1042 | "#]], | 1032 | "#]], |
1043 | ); | 1033 | ); |
1044 | } | 1034 | } |
@@ -1054,9 +1044,9 @@ fn main() { | |||
1054 | } | 1044 | } |
1055 | "#, | 1045 | "#, |
1056 | expect![[r#" | 1046 | expect![[r#" |
1057 | a Local FileId(0) 59..60 59..60 Other | 1047 | a Local FileId(0) 59..60 59..60 |
1058 | 1048 | ||
1059 | FileId(0) 80..81 Other Read | 1049 | FileId(0) 80..81 Read |
1060 | "#]], | 1050 | "#]], |
1061 | ); | 1051 | ); |
1062 | } | 1052 | } |
@@ -1075,10 +1065,10 @@ fn foo<'a>() -> &'a () { | |||
1075 | } | 1065 | } |
1076 | "#, | 1066 | "#, |
1077 | expect![[r#" | 1067 | expect![[r#" |
1078 | 'a Label FileId(0) 29..32 29..31 Lifetime | 1068 | 'a Label FileId(0) 29..32 29..31 |
1079 | 1069 | ||
1080 | FileId(0) 80..82 Lifetime | 1070 | FileId(0) 80..82 |
1081 | FileId(0) 108..110 Lifetime | 1071 | FileId(0) 108..110 |
1082 | "#]], | 1072 | "#]], |
1083 | ); | 1073 | ); |
1084 | } | 1074 | } |
@@ -1092,9 +1082,9 @@ fn foo<const FOO$0: usize>() -> usize { | |||
1092 | } | 1082 | } |
1093 | "#, | 1083 | "#, |
1094 | expect![[r#" | 1084 | expect![[r#" |
1095 | FOO ConstParam FileId(0) 7..23 13..16 Other | 1085 | FOO ConstParam FileId(0) 7..23 13..16 |
1096 | 1086 | ||
1097 | FileId(0) 42..45 Other | 1087 | FileId(0) 42..45 |
1098 | "#]], | 1088 | "#]], |
1099 | ); | 1089 | ); |
1100 | } | 1090 | } |
@@ -1108,9 +1098,9 @@ trait Foo { | |||
1108 | } | 1098 | } |
1109 | "#, | 1099 | "#, |
1110 | expect![[r#" | 1100 | expect![[r#" |
1111 | Self TypeParam FileId(0) 6..9 6..9 Other | 1101 | Self TypeParam FileId(0) 6..9 6..9 |
1112 | 1102 | ||
1113 | FileId(0) 26..30 Other | 1103 | FileId(0) 26..30 |
1114 | "#]], | 1104 | "#]], |
1115 | ); | 1105 | ); |
1116 | } | 1106 | } |
@@ -1131,9 +1121,9 @@ impl Foo { | |||
1131 | 1121 | ||
1132 | "#, | 1122 | "#, |
1133 | expect![[r#" | 1123 | expect![[r#" |
1134 | Bar Variant FileId(0) 11..16 11..14 Other | 1124 | Bar Variant FileId(0) 11..16 11..14 |
1135 | 1125 | ||
1136 | FileId(0) 89..92 Other | 1126 | FileId(0) 89..92 |
1137 | "#]], | 1127 | "#]], |
1138 | ); | 1128 | ); |
1139 | } | 1129 | } |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index ebb1ce7dd..b04214291 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -4,9 +4,9 @@ use std::fmt::{self, Display}; | |||
4 | use either::Either; | 4 | use either::Either; |
5 | use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics}; | 5 | use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics}; |
6 | use ide_db::{ | 6 | use ide_db::{ |
7 | base_db::{AnchoredPathBuf, FileId, FileRange}, | 7 | base_db::{AnchoredPathBuf, FileId}, |
8 | defs::{Definition, NameClass, NameRefClass}, | 8 | defs::{Definition, NameClass, NameRefClass}, |
9 | search::FileReference, | 9 | search::{FileReference, NameLike}, |
10 | RootDatabase, | 10 | RootDatabase, |
11 | }; | 11 | }; |
12 | use stdx::never; | 12 | use stdx::never; |
@@ -17,10 +17,7 @@ use syntax::{ | |||
17 | use test_utils::mark; | 17 | use test_utils::mark; |
18 | use text_edit::TextEdit; | 18 | use text_edit::TextEdit; |
19 | 19 | ||
20 | use crate::{ | 20 | use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; |
21 | display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, SourceChange, | ||
22 | TextRange, | ||
23 | }; | ||
24 | 21 | ||
25 | type RenameResult<T> = Result<T, RenameError>; | 22 | type RenameResult<T> = Result<T, RenameError>; |
26 | #[derive(Debug)] | 23 | #[derive(Debug)] |
@@ -41,6 +38,8 @@ macro_rules! bail { | |||
41 | ($($tokens:tt)*) => {return Err(format_err!($($tokens)*))} | 38 | ($($tokens:tt)*) => {return Err(format_err!($($tokens)*))} |
42 | } | 39 | } |
43 | 40 | ||
41 | /// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is | ||
42 | /// being targeted for a rename. | ||
44 | pub(crate) fn prepare_rename( | 43 | pub(crate) fn prepare_rename( |
45 | db: &RootDatabase, | 44 | db: &RootDatabase, |
46 | position: FilePosition, | 45 | position: FilePosition, |
@@ -123,12 +122,6 @@ fn check_identifier(new_name: &str) -> RenameResult<IdentifierKind> { | |||
123 | } | 122 | } |
124 | } | 123 | } |
125 | 124 | ||
126 | enum NameLike { | ||
127 | Name(ast::Name), | ||
128 | NameRef(ast::NameRef), | ||
129 | Lifetime(ast::Lifetime), | ||
130 | } | ||
131 | |||
132 | fn find_name_like( | 125 | fn find_name_like( |
133 | sema: &Semantics<RootDatabase>, | 126 | sema: &Semantics<RootDatabase>, |
134 | syntax: &SyntaxNode, | 127 | syntax: &SyntaxNode, |
@@ -171,72 +164,97 @@ fn find_definition( | |||
171 | } | 164 | } |
172 | 165 | ||
173 | fn source_edit_from_references( | 166 | fn source_edit_from_references( |
174 | sema: &Semantics<RootDatabase>, | 167 | _sema: &Semantics<RootDatabase>, |
175 | file_id: FileId, | 168 | file_id: FileId, |
176 | references: &[FileReference], | 169 | references: &[FileReference], |
170 | def: Definition, | ||
177 | new_name: &str, | 171 | new_name: &str, |
178 | ) -> (FileId, TextEdit) { | 172 | ) -> (FileId, TextEdit) { |
179 | let mut edit = TextEdit::builder(); | 173 | let mut edit = TextEdit::builder(); |
180 | for reference in references { | 174 | for reference in references { |
181 | let mut replacement_text = String::new(); | 175 | let (range, replacement) = match &reference.name { |
182 | let range = match reference.kind { | 176 | NameLike::Name(_) => (None, format!("{}", new_name)), |
183 | ReferenceKind::FieldShorthandForField => { | 177 | NameLike::NameRef(name_ref) => source_edit_from_name_ref(name_ref, new_name, def), |
184 | mark::hit!(test_rename_struct_field_for_shorthand); | 178 | NameLike::Lifetime(_) => (None, format!("{}", new_name)), |
185 | replacement_text.push_str(new_name); | ||
186 | replacement_text.push_str(": "); | ||
187 | TextRange::new(reference.range.start(), reference.range.start()) | ||
188 | } | ||
189 | ReferenceKind::FieldShorthandForLocal => { | ||
190 | mark::hit!(test_rename_local_for_field_shorthand); | ||
191 | replacement_text.push_str(": "); | ||
192 | replacement_text.push_str(new_name); | ||
193 | TextRange::new(reference.range.end(), reference.range.end()) | ||
194 | } | ||
195 | ReferenceKind::RecordFieldExprOrPat => { | ||
196 | mark::hit!(test_rename_field_expr_pat); | ||
197 | replacement_text.push_str(new_name); | ||
198 | edit_text_range_for_record_field_expr_or_pat( | ||
199 | sema, | ||
200 | FileRange { file_id, range: reference.range }, | ||
201 | new_name, | ||
202 | ) | ||
203 | } | ||
204 | _ => { | ||
205 | replacement_text.push_str(new_name); | ||
206 | reference.range | ||
207 | } | ||
208 | }; | 179 | }; |
209 | edit.replace(range, replacement_text); | 180 | // FIXME: Some(range) will be incorrect when we are inside macros |
181 | edit.replace(range.unwrap_or(reference.range), replacement); | ||
210 | } | 182 | } |
211 | (file_id, edit.finish()) | 183 | (file_id, edit.finish()) |
212 | } | 184 | } |
213 | 185 | ||
214 | fn edit_text_range_for_record_field_expr_or_pat( | 186 | fn source_edit_from_name_ref( |
215 | sema: &Semantics<RootDatabase>, | 187 | name_ref: &ast::NameRef, |
216 | file_range: FileRange, | ||
217 | new_name: &str, | 188 | new_name: &str, |
218 | ) -> TextRange { | 189 | def: Definition, |
219 | let source_file = sema.parse(file_range.file_id); | 190 | ) -> (Option<TextRange>, String) { |
220 | let file_syntax = source_file.syntax(); | 191 | if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) { |
221 | let original_range = file_range.range; | 192 | let rcf_name_ref = record_field.name_ref(); |
222 | 193 | let rcf_expr = record_field.expr(); | |
223 | syntax::algo::find_node_at_range::<ast::RecordExprField>(file_syntax, original_range) | 194 | match (rcf_name_ref, rcf_expr.and_then(|it| it.name_ref())) { |
224 | .and_then(|field_expr| match field_expr.expr().and_then(|e| e.name_ref()) { | 195 | // field: init-expr, check if we can use a field init shorthand |
225 | Some(name) if &name.to_string() == new_name => Some(field_expr.syntax().text_range()), | 196 | (Some(field_name), Some(init)) => { |
226 | _ => None, | 197 | if field_name == *name_ref { |
227 | }) | 198 | if init.text() == new_name { |
228 | .or_else(|| { | 199 | mark::hit!(test_rename_field_put_init_shorthand); |
229 | syntax::algo::find_node_at_range::<ast::RecordPatField>(file_syntax, original_range) | 200 | // same names, we can use a shorthand here instead |
230 | .and_then(|field_pat| match field_pat.pat() { | 201 | // we do not want to erase attributes hence this range start |
231 | Some(ast::Pat::IdentPat(pat)) | 202 | let s = field_name.syntax().text_range().start(); |
232 | if pat.name().map(|n| n.to_string()).as_deref() == Some(new_name) => | 203 | let e = record_field.syntax().text_range().end(); |
233 | { | 204 | return (Some(TextRange::new(s, e)), format!("{}", new_name)); |
234 | Some(field_pat.syntax().text_range()) | ||
235 | } | 205 | } |
236 | _ => None, | 206 | } else if init == *name_ref { |
237 | }) | 207 | if field_name.text() == new_name { |
238 | }) | 208 | mark::hit!(test_rename_local_put_init_shorthand); |
239 | .unwrap_or(original_range) | 209 | // same names, we can use a shorthand here instead |
210 | // we do not want to erase attributes hence this range start | ||
211 | let s = field_name.syntax().text_range().start(); | ||
212 | let e = record_field.syntax().text_range().end(); | ||
213 | return (Some(TextRange::new(s, e)), format!("{}", new_name)); | ||
214 | } | ||
215 | } | ||
216 | } | ||
217 | // 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 | } | ||
234 | } | ||
235 | _ => {} | ||
236 | } | ||
237 | } | ||
238 | if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) { | ||
239 | let rcf_name_ref = record_field.name_ref(); | ||
240 | let rcf_pat = record_field.pat(); | ||
241 | match (rcf_name_ref, rcf_pat) { | ||
242 | // field: rename | ||
243 | (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { | ||
244 | // field name is being renamed | ||
245 | if pat.name().map_or(false, |it| it.text() == new_name) { | ||
246 | mark::hit!(test_rename_field_put_init_shorthand_pat); | ||
247 | // same names, we can use a shorthand here instead | ||
248 | // we do not want to erase attributes hence this range start | ||
249 | let s = field_name.syntax().text_range().start(); | ||
250 | let e = record_field.syntax().text_range().end(); | ||
251 | return (Some(TextRange::new(s, e)), format!("{}", new_name)); | ||
252 | } | ||
253 | } | ||
254 | _ => {} | ||
255 | } | ||
256 | } | ||
257 | (None, format!("{}", new_name)) | ||
240 | } | 258 | } |
241 | 259 | ||
242 | fn rename_mod( | 260 | fn rename_mod( |
@@ -277,7 +295,7 @@ fn rename_mod( | |||
277 | let def = Definition::ModuleDef(ModuleDef::Module(module)); | 295 | let def = Definition::ModuleDef(ModuleDef::Module(module)); |
278 | let usages = def.usages(sema).all(); | 296 | let usages = def.usages(sema).all(); |
279 | let ref_edits = usages.iter().map(|(&file_id, references)| { | 297 | let ref_edits = usages.iter().map(|(&file_id, references)| { |
280 | source_edit_from_references(sema, file_id, references, new_name) | 298 | source_edit_from_references(sema, file_id, references, def, new_name) |
281 | }); | 299 | }); |
282 | source_change.extend(ref_edits); | 300 | source_change.extend(ref_edits); |
283 | 301 | ||
@@ -346,7 +364,7 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe | |||
346 | let usages = def.usages(sema).all(); | 364 | let usages = def.usages(sema).all(); |
347 | let mut source_change = SourceChange::default(); | 365 | let mut source_change = SourceChange::default(); |
348 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 366 | source_change.extend(usages.iter().map(|(&file_id, references)| { |
349 | source_edit_from_references(sema, file_id, references, "self") | 367 | source_edit_from_references(sema, file_id, references, def, "self") |
350 | })); | 368 | })); |
351 | source_change.insert_source_edit( | 369 | source_change.insert_source_edit( |
352 | file_id.original_file(sema.db), | 370 | file_id.original_file(sema.db), |
@@ -403,7 +421,7 @@ fn rename_self_to_param( | |||
403 | let mut source_change = SourceChange::default(); | 421 | let mut source_change = SourceChange::default(); |
404 | source_change.insert_source_edit(file_id.original_file(sema.db), edit); | 422 | source_change.insert_source_edit(file_id.original_file(sema.db), edit); |
405 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 423 | source_change.extend(usages.iter().map(|(&file_id, references)| { |
406 | source_edit_from_references(sema, file_id, &references, new_name) | 424 | source_edit_from_references(sema, file_id, &references, def, new_name) |
407 | })); | 425 | })); |
408 | Ok(source_change) | 426 | Ok(source_change) |
409 | } | 427 | } |
@@ -457,7 +475,7 @@ fn rename_reference( | |||
457 | } | 475 | } |
458 | let mut source_change = SourceChange::default(); | 476 | let mut source_change = SourceChange::default(); |
459 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 477 | source_change.extend(usages.iter().map(|(&file_id, references)| { |
460 | source_edit_from_references(sema, file_id, &references, new_name) | 478 | source_edit_from_references(sema, file_id, &references, def, new_name) |
461 | })); | 479 | })); |
462 | 480 | ||
463 | let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; | 481 | let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; |
@@ -545,10 +563,8 @@ mod tests { | |||
545 | 563 | ||
546 | fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) { | 564 | fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) { |
547 | let (analysis, position) = fixture::position(ra_fixture); | 565 | let (analysis, position) = fixture::position(ra_fixture); |
548 | let source_change = analysis | 566 | let source_change = |
549 | .rename(position, new_name) | 567 | analysis.rename(position, new_name).unwrap().expect("Expect returned a RenameError"); |
550 | .unwrap() | ||
551 | .expect("Expect returned RangeInfo to be Some, but was None"); | ||
552 | expect.assert_debug_eq(&source_change) | 568 | expect.assert_debug_eq(&source_change) |
553 | } | 569 | } |
554 | 570 | ||
@@ -792,8 +808,8 @@ impl Foo { | |||
792 | } | 808 | } |
793 | 809 | ||
794 | #[test] | 810 | #[test] |
795 | fn test_rename_struct_field_for_shorthand() { | 811 | fn test_rename_field_in_field_shorthand() { |
796 | mark::check!(test_rename_struct_field_for_shorthand); | 812 | mark::check!(test_rename_field_in_field_shorthand); |
797 | check( | 813 | check( |
798 | "j", | 814 | "j", |
799 | r#" | 815 | r#" |
@@ -818,8 +834,8 @@ impl Foo { | |||
818 | } | 834 | } |
819 | 835 | ||
820 | #[test] | 836 | #[test] |
821 | fn test_rename_local_for_field_shorthand() { | 837 | fn test_rename_local_in_field_shorthand() { |
822 | mark::check!(test_rename_local_for_field_shorthand); | 838 | mark::check!(test_rename_local_in_field_shorthand); |
823 | check( | 839 | check( |
824 | "j", | 840 | "j", |
825 | r#" | 841 | r#" |
@@ -1417,8 +1433,8 @@ impl Foo { | |||
1417 | } | 1433 | } |
1418 | 1434 | ||
1419 | #[test] | 1435 | #[test] |
1420 | fn test_initializer_use_field_init_shorthand() { | 1436 | fn test_rename_field_put_init_shorthand() { |
1421 | mark::check!(test_rename_field_expr_pat); | 1437 | mark::check!(test_rename_field_put_init_shorthand); |
1422 | check( | 1438 | check( |
1423 | "bar", | 1439 | "bar", |
1424 | r#" | 1440 | r#" |
@@ -1439,7 +1455,30 @@ fn foo(bar: i32) -> Foo { | |||
1439 | } | 1455 | } |
1440 | 1456 | ||
1441 | #[test] | 1457 | #[test] |
1458 | fn test_rename_local_put_init_shorthand() { | ||
1459 | mark::check!(test_rename_local_put_init_shorthand); | ||
1460 | check( | ||
1461 | "i", | ||
1462 | r#" | ||
1463 | struct Foo { i: i32 } | ||
1464 | |||
1465 | fn foo(bar$0: i32) -> Foo { | ||
1466 | Foo { i: bar } | ||
1467 | } | ||
1468 | "#, | ||
1469 | r#" | ||
1470 | struct Foo { i: i32 } | ||
1471 | |||
1472 | fn foo(i: i32) -> Foo { | ||
1473 | Foo { i } | ||
1474 | } | ||
1475 | "#, | ||
1476 | ); | ||
1477 | } | ||
1478 | |||
1479 | #[test] | ||
1442 | fn test_struct_field_destructure_into_shorthand() { | 1480 | fn test_struct_field_destructure_into_shorthand() { |
1481 | mark::check!(test_rename_field_put_init_shorthand_pat); | ||
1443 | check( | 1482 | check( |
1444 | "baz", | 1483 | "baz", |
1445 | r#" | 1484 | r#" |