diff options
author | Lukas Wirth <[email protected]> | 2021-02-07 17:38:12 +0000 |
---|---|---|
committer | Lukas Wirth <[email protected]> | 2021-02-12 17:58:28 +0000 |
commit | d644728d82df10b034d0ea736590c781afa2ba15 (patch) | |
tree | 4591c44955de42387165e7a59789d7e636284bc6 /crates/ide/src | |
parent | 43ccbf4360271d5da2fd3688a04b34c66357e0b6 (diff) |
Refactor reference searching to work with the ast
Diffstat (limited to 'crates/ide/src')
-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 | 357 | ||||
-rw-r--r-- | crates/ide/src/references/rename.rs | 197 |
4 files changed, 310 insertions, 256 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..a245d9341 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::{FileReference, 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 e8737dcfa..f96fac9c1 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -11,13 +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::{ |
16 | base_db::FileId, | ||
17 | defs::{Definition, NameClass, NameRefClass}, | 17 | defs::{Definition, NameClass, NameRefClass}, |
18 | search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope, UsageSearchResult}, | 18 | search::{ReferenceAccess, SearchScope}, |
19 | RootDatabase, | 19 | RootDatabase, |
20 | }; | 20 | }; |
21 | use rustc_hash::FxHashMap; | ||
21 | use syntax::{ | 22 | use syntax::{ |
22 | algo::find_node_at_offset, | 23 | algo::find_node_at_offset, |
23 | ast::{self, NameOwner}, | 24 | ast::{self, NameOwner}, |
@@ -28,32 +29,14 @@ use crate::{display::TryToNav, FilePosition, NavigationTarget}; | |||
28 | 29 | ||
29 | #[derive(Debug, Clone)] | 30 | #[derive(Debug, Clone)] |
30 | pub struct ReferenceSearchResult { | 31 | pub struct ReferenceSearchResult { |
31 | declaration: Declaration, | 32 | pub declaration: Declaration, |
32 | references: UsageSearchResult, | 33 | pub references: FxHashMap<FileId, Vec<(TextRange, Option<ReferenceAccess>)>>, |
33 | } | 34 | } |
34 | 35 | ||
35 | #[derive(Debug, Clone)] | 36 | #[derive(Debug, Clone)] |
36 | pub struct Declaration { | 37 | pub struct Declaration { |
37 | nav: NavigationTarget, | 38 | pub nav: NavigationTarget, |
38 | kind: ReferenceKind, | 39 | pub access: Option<ReferenceAccess>, |
39 | access: Option<ReferenceAccess>, | ||
40 | } | ||
41 | |||
42 | impl ReferenceSearchResult { | ||
43 | pub fn references(self) -> UsageSearchResult { | ||
44 | self.references | ||
45 | } | ||
46 | |||
47 | pub fn references_with_declaration(mut self) -> UsageSearchResult { | ||
48 | let decl_ref = FileReference { | ||
49 | range: self.declaration.nav.focus_or_full_range(), | ||
50 | kind: self.declaration.kind, | ||
51 | access: self.declaration.access, | ||
52 | }; | ||
53 | let file_id = self.declaration.nav.file_id; | ||
54 | self.references.references.entry(file_id).or_default().push(decl_ref); | ||
55 | self.references | ||
56 | } | ||
57 | } | 40 | } |
58 | 41 | ||
59 | pub(crate) fn find_all_refs( | 42 | pub(crate) fn find_all_refs( |
@@ -64,83 +47,76 @@ pub(crate) fn find_all_refs( | |||
64 | let _p = profile::span("find_all_refs"); | 47 | let _p = profile::span("find_all_refs"); |
65 | let syntax = sema.parse(position.file_id).syntax().clone(); | 48 | let syntax = sema.parse(position.file_id).syntax().clone(); |
66 | 49 | ||
67 | let (opt_name, search_kind) = if let Some(name) = | 50 | let (opt_name, ctor_filter): (_, Option<fn(&_) -> bool>) = if let Some(name) = |
68 | get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) | 51 | get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) |
69 | { | 52 | { |
70 | (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 | ) | ||
71 | } 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) |
72 | { | 58 | { |
73 | (Some(name), ReferenceKind::EnumLiteral) | 59 | (Some(name), Some(is_enum_lit_name_ref)) |
74 | } else { | 60 | } else { |
75 | ( | 61 | (sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset), None) |
76 | sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset), | ||
77 | ReferenceKind::Other, | ||
78 | ) | ||
79 | }; | 62 | }; |
80 | 63 | ||
81 | let def = find_name(&sema, &syntax, position, opt_name)?; | 64 | let def = find_def(&sema, &syntax, position, opt_name)?; |
82 | 65 | ||
83 | let mut usages = def.usages(sema).set_scope(search_scope).all(); | 66 | let mut usages = def.usages(sema).set_scope(search_scope).all(); |
84 | usages | 67 | if let Some(ctor_filter) = ctor_filter { |
85 | .references | 68 | // filter for constructor-literals |
86 | .values_mut() | 69 | usages.references.iter_mut().for_each(|(&file_id, it)| { |
87 | .for_each(|it| it.retain(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)); | 70 | let root = sema.parse(file_id); |
88 | usages.references.retain(|_, it| !it.is_empty()); | 71 | let root = root.syntax(); |
89 | 72 | it.retain(|reference| { | |
73 | reference.as_name_ref(root).map_or(false, |name_ref| ctor_filter(&name_ref)) | ||
74 | }) | ||
75 | }); | ||
76 | usages.references.retain(|_, it| !it.is_empty()); | ||
77 | } | ||
90 | let nav = def.try_to_nav(sema.db)?; | 78 | let nav = def.try_to_nav(sema.db)?; |
91 | let decl_range = nav.focus_or_full_range(); | 79 | let decl_range = nav.focus_or_full_range(); |
92 | 80 | ||
93 | let mut kind = ReferenceKind::Other; | 81 | let declaration = Declaration { nav, access: decl_access(&def, &syntax, decl_range) }; |
94 | if let Definition::Local(local) = def { | 82 | let references = usages |
95 | match local.source(sema.db).value { | 83 | .into_iter() |
96 | Either::Left(pat) => { | 84 | .map(|(file_id, refs)| { |
97 | if matches!( | 85 | (file_id, refs.into_iter().map(|file_ref| (file_ref.range, file_ref.access)).collect()) |
98 | pat.syntax().parent().and_then(ast::RecordPatField::cast), | 86 | }) |
99 | Some(pat_field) if pat_field.name_ref().is_none() | 87 | .collect(); |
100 | ) { | ||
101 | kind = ReferenceKind::FieldShorthandForLocal; | ||
102 | } | ||
103 | } | ||
104 | Either::Right(_) => kind = ReferenceKind::SelfParam, | ||
105 | } | ||
106 | } else if matches!( | ||
107 | def, | ||
108 | Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) | ||
109 | ) { | ||
110 | kind = ReferenceKind::Lifetime; | ||
111 | }; | ||
112 | |||
113 | let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; | ||
114 | 88 | ||
115 | Some(ReferenceSearchResult { declaration, references: usages }) | 89 | Some(ReferenceSearchResult { declaration, references }) |
116 | } | 90 | } |
117 | 91 | ||
118 | fn find_name( | 92 | fn find_def( |
119 | sema: &Semantics<RootDatabase>, | 93 | sema: &Semantics<RootDatabase>, |
120 | syntax: &SyntaxNode, | 94 | syntax: &SyntaxNode, |
121 | position: FilePosition, | 95 | position: FilePosition, |
122 | opt_name: Option<ast::Name>, | 96 | opt_name: Option<ast::Name>, |
123 | ) -> Option<Definition> { | 97 | ) -> Option<Definition> { |
124 | let def = if let Some(name) = opt_name { | 98 | if let Some(name) = opt_name { |
125 | NameClass::classify(sema, &name)?.referenced_or_defined(sema.db) | 99 | let class = NameClass::classify(sema, &name)?; |
100 | Some(class.referenced_or_defined(sema.db)) | ||
126 | } else if let Some(lifetime) = | 101 | } else if let Some(lifetime) = |
127 | sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) | 102 | sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) |
128 | { | 103 | { |
129 | if let Some(def) = | 104 | let def = if let Some(def) = |
130 | NameRefClass::classify_lifetime(sema, &lifetime).map(|class| class.referenced(sema.db)) | 105 | NameRefClass::classify_lifetime(sema, &lifetime).map(|class| class.referenced(sema.db)) |
131 | { | 106 | { |
132 | def | 107 | def |
133 | } else { | 108 | } else { |
134 | NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db) | 109 | NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db) |
135 | } | 110 | }; |
111 | Some(def) | ||
136 | } else if let Some(name_ref) = | 112 | } else if let Some(name_ref) = |
137 | sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset) | 113 | sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset) |
138 | { | 114 | { |
139 | NameRefClass::classify(sema, &name_ref)?.referenced(sema.db) | 115 | let class = NameRefClass::classify(sema, &name_ref)?; |
116 | Some(class.referenced(sema.db)) | ||
140 | } else { | 117 | } else { |
141 | return None; | 118 | None |
142 | }; | 119 | } |
143 | Some(def) | ||
144 | } | 120 | } |
145 | 121 | ||
146 | fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { | 122 | fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { |
@@ -216,6 +192,43 @@ fn get_enum_def_name_for_struct_literal_search( | |||
216 | None | 192 | None |
217 | } | 193 | } |
218 | 194 | ||
195 | fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool { | ||
196 | name_ref | ||
197 | .syntax() | ||
198 | .ancestors() | ||
199 | .find_map(ast::CallExpr::cast) | ||
200 | .and_then(|c| match c.expr()? { | ||
201 | ast::Expr::PathExpr(p) => { | ||
202 | Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref)) | ||
203 | } | ||
204 | _ => None, | ||
205 | }) | ||
206 | .unwrap_or(false) | ||
207 | } | ||
208 | |||
209 | fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool { | ||
210 | name_ref | ||
211 | .syntax() | ||
212 | .ancestors() | ||
213 | .find_map(ast::RecordExpr::cast) | ||
214 | .and_then(|l| l.path()) | ||
215 | .and_then(|p| p.segment()) | ||
216 | .map(|p| p.name_ref().as_ref() == Some(name_ref)) | ||
217 | .unwrap_or(false) | ||
218 | } | ||
219 | |||
220 | fn is_enum_lit_name_ref(name_ref: &ast::NameRef) -> bool { | ||
221 | name_ref | ||
222 | .syntax() | ||
223 | .ancestors() | ||
224 | .find_map(ast::PathExpr::cast) | ||
225 | .and_then(|p| p.path()) | ||
226 | .and_then(|p| p.qualifier()) | ||
227 | .and_then(|p| p.segment()) | ||
228 | .map(|p| p.name_ref().as_ref() == Some(name_ref)) | ||
229 | .unwrap_or(false) | ||
230 | } | ||
231 | |||
219 | #[cfg(test)] | 232 | #[cfg(test)] |
220 | mod tests { | 233 | mod tests { |
221 | use expect_test::{expect, Expect}; | 234 | use expect_test::{expect, Expect}; |
@@ -240,9 +253,9 @@ fn main() { | |||
240 | } | 253 | } |
241 | "#, | 254 | "#, |
242 | expect![[r#" | 255 | expect![[r#" |
243 | Foo Struct FileId(0) 0..26 7..10 Other | 256 | Foo Struct FileId(0) 0..26 7..10 |
244 | 257 | ||
245 | FileId(0) 101..104 StructLiteral | 258 | FileId(0) 101..104 |
246 | "#]], | 259 | "#]], |
247 | ); | 260 | ); |
248 | } | 261 | } |
@@ -258,10 +271,10 @@ struct Foo$0 {} | |||
258 | } | 271 | } |
259 | "#, | 272 | "#, |
260 | expect![[r#" | 273 | expect![[r#" |
261 | Foo Struct FileId(0) 0..13 7..10 Other | 274 | Foo Struct FileId(0) 0..13 7..10 |
262 | 275 | ||
263 | FileId(0) 41..44 Other | 276 | FileId(0) 41..44 |
264 | FileId(0) 54..57 StructLiteral | 277 | FileId(0) 54..57 |
265 | "#]], | 278 | "#]], |
266 | ); | 279 | ); |
267 | } | 280 | } |
@@ -277,9 +290,9 @@ struct Foo<T> $0{} | |||
277 | } | 290 | } |
278 | "#, | 291 | "#, |
279 | expect![[r#" | 292 | expect![[r#" |
280 | Foo Struct FileId(0) 0..16 7..10 Other | 293 | Foo Struct FileId(0) 0..16 7..10 |
281 | 294 | ||
282 | FileId(0) 64..67 StructLiteral | 295 | FileId(0) 64..67 |
283 | "#]], | 296 | "#]], |
284 | ); | 297 | ); |
285 | } | 298 | } |
@@ -296,9 +309,9 @@ fn main() { | |||
296 | } | 309 | } |
297 | "#, | 310 | "#, |
298 | expect![[r#" | 311 | expect![[r#" |
299 | Foo Struct FileId(0) 0..16 7..10 Other | 312 | Foo Struct FileId(0) 0..16 7..10 |
300 | 313 | ||
301 | FileId(0) 54..57 StructLiteral | 314 | FileId(0) 54..57 |
302 | "#]], | 315 | "#]], |
303 | ); | 316 | ); |
304 | } | 317 | } |
@@ -317,9 +330,9 @@ fn main() { | |||
317 | } | 330 | } |
318 | "#, | 331 | "#, |
319 | expect![[r#" | 332 | expect![[r#" |
320 | Foo Enum FileId(0) 0..26 5..8 Other | 333 | Foo Enum FileId(0) 0..26 5..8 |
321 | 334 | ||
322 | FileId(0) 63..66 EnumLiteral | 335 | FileId(0) 63..66 |
323 | "#]], | 336 | "#]], |
324 | ); | 337 | ); |
325 | } | 338 | } |
@@ -338,10 +351,10 @@ fn main() { | |||
338 | } | 351 | } |
339 | "#, | 352 | "#, |
340 | expect![[r#" | 353 | expect![[r#" |
341 | Foo Enum FileId(0) 0..26 5..8 Other | 354 | Foo Enum FileId(0) 0..26 5..8 |
342 | 355 | ||
343 | FileId(0) 50..53 Other | 356 | FileId(0) 50..53 |
344 | FileId(0) 63..66 EnumLiteral | 357 | FileId(0) 63..66 |
345 | "#]], | 358 | "#]], |
346 | ); | 359 | ); |
347 | } | 360 | } |
@@ -360,9 +373,9 @@ fn main() { | |||
360 | } | 373 | } |
361 | "#, | 374 | "#, |
362 | expect![[r#" | 375 | expect![[r#" |
363 | Foo Enum FileId(0) 0..32 5..8 Other | 376 | Foo Enum FileId(0) 0..32 5..8 |
364 | 377 | ||
365 | FileId(0) 73..76 EnumLiteral | 378 | FileId(0) 73..76 |
366 | "#]], | 379 | "#]], |
367 | ); | 380 | ); |
368 | } | 381 | } |
@@ -381,9 +394,9 @@ fn main() { | |||
381 | } | 394 | } |
382 | "#, | 395 | "#, |
383 | expect![[r#" | 396 | expect![[r#" |
384 | Foo Enum FileId(0) 0..33 5..8 Other | 397 | Foo Enum FileId(0) 0..33 5..8 |
385 | 398 | ||
386 | FileId(0) 70..73 EnumLiteral | 399 | FileId(0) 70..73 |
387 | "#]], | 400 | "#]], |
388 | ); | 401 | ); |
389 | } | 402 | } |
@@ -404,12 +417,12 @@ fn main() { | |||
404 | i = 5; | 417 | i = 5; |
405 | }"#, | 418 | }"#, |
406 | expect![[r#" | 419 | expect![[r#" |
407 | i Local FileId(0) 20..25 24..25 Other Write | 420 | i Local FileId(0) 20..25 24..25 Write |
408 | 421 | ||
409 | FileId(0) 50..51 Other Write | 422 | FileId(0) 50..51 Write |
410 | FileId(0) 54..55 Other Read | 423 | FileId(0) 54..55 Read |
411 | FileId(0) 76..77 Other Write | 424 | FileId(0) 76..77 Write |
412 | FileId(0) 94..95 Other Write | 425 | FileId(0) 94..95 Write |
413 | "#]], | 426 | "#]], |
414 | ); | 427 | ); |
415 | } | 428 | } |
@@ -428,10 +441,10 @@ fn bar() { | |||
428 | } | 441 | } |
429 | "#, | 442 | "#, |
430 | expect![[r#" | 443 | expect![[r#" |
431 | spam Local FileId(0) 19..23 19..23 Other | 444 | spam Local FileId(0) 19..23 19..23 |
432 | 445 | ||
433 | FileId(0) 34..38 Other Read | 446 | FileId(0) 34..38 Read |
434 | FileId(0) 41..45 Other Read | 447 | FileId(0) 41..45 Read |
435 | "#]], | 448 | "#]], |
436 | ); | 449 | ); |
437 | } | 450 | } |
@@ -443,9 +456,9 @@ fn bar() { | |||
443 | fn foo(i : u32) -> u32 { i$0 } | 456 | fn foo(i : u32) -> u32 { i$0 } |
444 | "#, | 457 | "#, |
445 | expect![[r#" | 458 | expect![[r#" |
446 | i ValueParam FileId(0) 7..8 7..8 Other | 459 | i ValueParam FileId(0) 7..8 7..8 |
447 | 460 | ||
448 | FileId(0) 25..26 Other Read | 461 | FileId(0) 25..26 Read |
449 | "#]], | 462 | "#]], |
450 | ); | 463 | ); |
451 | } | 464 | } |
@@ -457,9 +470,9 @@ fn foo(i : u32) -> u32 { i$0 } | |||
457 | fn foo(i$0 : u32) -> u32 { i } | 470 | fn foo(i$0 : u32) -> u32 { i } |
458 | "#, | 471 | "#, |
459 | expect![[r#" | 472 | expect![[r#" |
460 | i ValueParam FileId(0) 7..8 7..8 Other | 473 | i ValueParam FileId(0) 7..8 7..8 |
461 | 474 | ||
462 | FileId(0) 25..26 Other Read | 475 | FileId(0) 25..26 Read |
463 | "#]], | 476 | "#]], |
464 | ); | 477 | ); |
465 | } | 478 | } |
@@ -478,9 +491,9 @@ fn main(s: Foo) { | |||
478 | } | 491 | } |
479 | "#, | 492 | "#, |
480 | expect![[r#" | 493 | expect![[r#" |
481 | spam Field FileId(0) 17..30 21..25 Other | 494 | spam Field FileId(0) 17..30 21..25 |
482 | 495 | ||
483 | FileId(0) 67..71 Other Read | 496 | FileId(0) 67..71 Read |
484 | "#]], | 497 | "#]], |
485 | ); | 498 | ); |
486 | } | 499 | } |
@@ -495,7 +508,7 @@ impl Foo { | |||
495 | } | 508 | } |
496 | "#, | 509 | "#, |
497 | expect![[r#" | 510 | expect![[r#" |
498 | f Function FileId(0) 27..43 30..31 Other | 511 | f Function FileId(0) 27..43 30..31 |
499 | 512 | ||
500 | "#]], | 513 | "#]], |
501 | ); | 514 | ); |
@@ -512,7 +525,7 @@ enum Foo { | |||
512 | } | 525 | } |
513 | "#, | 526 | "#, |
514 | expect![[r#" | 527 | expect![[r#" |
515 | B Variant FileId(0) 22..23 22..23 Other | 528 | B Variant FileId(0) 22..23 22..23 |
516 | 529 | ||
517 | "#]], | 530 | "#]], |
518 | ); | 531 | ); |
@@ -529,7 +542,7 @@ enum Foo { | |||
529 | } | 542 | } |
530 | "#, | 543 | "#, |
531 | expect![[r#" | 544 | expect![[r#" |
532 | field Field FileId(0) 26..35 26..31 Other | 545 | field Field FileId(0) 26..35 26..31 |
533 | 546 | ||
534 | "#]], | 547 | "#]], |
535 | ); | 548 | ); |
@@ -570,10 +583,10 @@ fn f() { | |||
570 | } | 583 | } |
571 | "#, | 584 | "#, |
572 | expect![[r#" | 585 | expect![[r#" |
573 | Foo Struct FileId(1) 17..51 28..31 Other | 586 | Foo Struct FileId(1) 17..51 28..31 |
574 | 587 | ||
575 | FileId(0) 53..56 StructLiteral | 588 | FileId(0) 53..56 |
576 | FileId(2) 79..82 StructLiteral | 589 | FileId(2) 79..82 |
577 | "#]], | 590 | "#]], |
578 | ); | 591 | ); |
579 | } | 592 | } |
@@ -600,9 +613,9 @@ pub struct Foo { | |||
600 | } | 613 | } |
601 | "#, | 614 | "#, |
602 | expect![[r#" | 615 | expect![[r#" |
603 | foo Module FileId(1) 0..35 Other | 616 | foo Module FileId(1) 0..35 |
604 | 617 | ||
605 | FileId(0) 14..17 Other | 618 | FileId(0) 14..17 |
606 | "#]], | 619 | "#]], |
607 | ); | 620 | ); |
608 | } | 621 | } |
@@ -628,10 +641,10 @@ pub(super) struct Foo$0 { | |||
628 | } | 641 | } |
629 | "#, | 642 | "#, |
630 | expect![[r#" | 643 | expect![[r#" |
631 | Foo Struct FileId(2) 0..41 18..21 Other | 644 | Foo Struct FileId(2) 0..41 18..21 |
632 | 645 | ||
633 | FileId(1) 20..23 Other | 646 | FileId(1) 20..23 |
634 | FileId(1) 47..50 StructLiteral | 647 | FileId(1) 47..50 |
635 | "#]], | 648 | "#]], |
636 | ); | 649 | ); |
637 | } | 650 | } |
@@ -656,10 +669,10 @@ pub(super) struct Foo$0 { | |||
656 | code, | 669 | code, |
657 | None, | 670 | None, |
658 | expect![[r#" | 671 | expect![[r#" |
659 | quux Function FileId(0) 19..35 26..30 Other | 672 | quux Function FileId(0) 19..35 26..30 |
660 | 673 | ||
661 | FileId(1) 16..20 StructLiteral | 674 | FileId(1) 16..20 |
662 | FileId(2) 16..20 StructLiteral | 675 | FileId(2) 16..20 |
663 | "#]], | 676 | "#]], |
664 | ); | 677 | ); |
665 | 678 | ||
@@ -667,9 +680,9 @@ pub(super) struct Foo$0 { | |||
667 | code, | 680 | code, |
668 | Some(SearchScope::single_file(FileId(2))), | 681 | Some(SearchScope::single_file(FileId(2))), |
669 | expect![[r#" | 682 | expect![[r#" |
670 | quux Function FileId(0) 19..35 26..30 Other | 683 | quux Function FileId(0) 19..35 26..30 |
671 | 684 | ||
672 | FileId(2) 16..20 StructLiteral | 685 | FileId(2) 16..20 |
673 | "#]], | 686 | "#]], |
674 | ); | 687 | ); |
675 | } | 688 | } |
@@ -687,10 +700,10 @@ fn foo() { | |||
687 | } | 700 | } |
688 | "#, | 701 | "#, |
689 | expect![[r#" | 702 | expect![[r#" |
690 | m1 Macro FileId(0) 0..46 29..31 Other | 703 | m1 Macro FileId(0) 0..46 29..31 |
691 | 704 | ||
692 | FileId(0) 63..65 StructLiteral | 705 | FileId(0) 63..65 |
693 | FileId(0) 73..75 StructLiteral | 706 | FileId(0) 73..75 |
694 | "#]], | 707 | "#]], |
695 | ); | 708 | ); |
696 | } | 709 | } |
@@ -705,10 +718,10 @@ fn foo() { | |||
705 | } | 718 | } |
706 | "#, | 719 | "#, |
707 | expect![[r#" | 720 | expect![[r#" |
708 | i Local FileId(0) 19..24 23..24 Other Write | 721 | i Local FileId(0) 19..24 23..24 Write |
709 | 722 | ||
710 | FileId(0) 34..35 Other Write | 723 | FileId(0) 34..35 Write |
711 | FileId(0) 38..39 Other Read | 724 | FileId(0) 38..39 Read |
712 | "#]], | 725 | "#]], |
713 | ); | 726 | ); |
714 | } | 727 | } |
@@ -727,10 +740,10 @@ fn foo() { | |||
727 | } | 740 | } |
728 | "#, | 741 | "#, |
729 | expect![[r#" | 742 | expect![[r#" |
730 | f Field FileId(0) 15..21 15..16 Other | 743 | f Field FileId(0) 15..21 15..16 |
731 | 744 | ||
732 | FileId(0) 55..56 RecordFieldExprOrPat Read | 745 | FileId(0) 55..56 Read |
733 | FileId(0) 68..69 Other Write | 746 | FileId(0) 68..69 Write |
734 | "#]], | 747 | "#]], |
735 | ); | 748 | ); |
736 | } | 749 | } |
@@ -745,9 +758,9 @@ fn foo() { | |||
745 | } | 758 | } |
746 | "#, | 759 | "#, |
747 | expect![[r#" | 760 | expect![[r#" |
748 | i Local FileId(0) 19..20 19..20 Other | 761 | i Local FileId(0) 19..20 19..20 |
749 | 762 | ||
750 | FileId(0) 26..27 Other Write | 763 | FileId(0) 26..27 Write |
751 | "#]], | 764 | "#]], |
752 | ); | 765 | ); |
753 | } | 766 | } |
@@ -769,9 +782,9 @@ fn main() { | |||
769 | } | 782 | } |
770 | "#, | 783 | "#, |
771 | expect![[r#" | 784 | expect![[r#" |
772 | new Function FileId(0) 54..81 61..64 Other | 785 | new Function FileId(0) 54..81 61..64 |
773 | 786 | ||
774 | FileId(0) 126..129 StructLiteral | 787 | FileId(0) 126..129 |
775 | "#]], | 788 | "#]], |
776 | ); | 789 | ); |
777 | } | 790 | } |
@@ -791,10 +804,10 @@ use crate::f; | |||
791 | fn g() { f(); } | 804 | fn g() { f(); } |
792 | "#, | 805 | "#, |
793 | expect![[r#" | 806 | expect![[r#" |
794 | f Function FileId(0) 22..31 25..26 Other | 807 | f Function FileId(0) 22..31 25..26 |
795 | 808 | ||
796 | FileId(1) 11..12 Other | 809 | FileId(1) 11..12 |
797 | FileId(1) 24..25 StructLiteral | 810 | FileId(1) 24..25 |
798 | "#]], | 811 | "#]], |
799 | ); | 812 | ); |
800 | } | 813 | } |
@@ -814,9 +827,9 @@ fn f(s: S) { | |||
814 | } | 827 | } |
815 | "#, | 828 | "#, |
816 | expect![[r#" | 829 | expect![[r#" |
817 | field Field FileId(0) 15..24 15..20 Other | 830 | field Field FileId(0) 15..24 15..20 |
818 | 831 | ||
819 | FileId(0) 68..73 FieldShorthandForField Read | 832 | FileId(0) 68..73 Read |
820 | "#]], | 833 | "#]], |
821 | ); | 834 | ); |
822 | } | 835 | } |
@@ -838,9 +851,9 @@ fn f(e: En) { | |||
838 | } | 851 | } |
839 | "#, | 852 | "#, |
840 | expect![[r#" | 853 | expect![[r#" |
841 | field Field FileId(0) 32..41 32..37 Other | 854 | field Field FileId(0) 32..41 32..37 |
842 | 855 | ||
843 | FileId(0) 102..107 FieldShorthandForField Read | 856 | FileId(0) 102..107 Read |
844 | "#]], | 857 | "#]], |
845 | ); | 858 | ); |
846 | } | 859 | } |
@@ -862,9 +875,9 @@ fn f() -> m::En { | |||
862 | } | 875 | } |
863 | "#, | 876 | "#, |
864 | expect![[r#" | 877 | expect![[r#" |
865 | field Field FileId(0) 56..65 56..61 Other | 878 | field Field FileId(0) 56..65 56..61 |
866 | 879 | ||
867 | FileId(0) 125..130 RecordFieldExprOrPat Read | 880 | FileId(0) 125..130 Read |
868 | "#]], | 881 | "#]], |
869 | ); | 882 | ); |
870 | } | 883 | } |
@@ -887,10 +900,10 @@ impl Foo { | |||
887 | } | 900 | } |
888 | "#, | 901 | "#, |
889 | expect![[r#" | 902 | expect![[r#" |
890 | self SelfParam FileId(0) 47..51 47..51 SelfParam | 903 | self SelfParam FileId(0) 47..51 47..51 |
891 | 904 | ||
892 | FileId(0) 71..75 Other Read | 905 | FileId(0) 71..75 Read |
893 | FileId(0) 152..156 Other Read | 906 | FileId(0) 152..156 Read |
894 | "#]], | 907 | "#]], |
895 | ); | 908 | ); |
896 | } | 909 | } |
@@ -908,9 +921,9 @@ impl Foo { | |||
908 | } | 921 | } |
909 | "#, | 922 | "#, |
910 | expect![[r#" | 923 | expect![[r#" |
911 | self SelfParam FileId(0) 47..51 47..51 SelfParam | 924 | self SelfParam FileId(0) 47..51 47..51 |
912 | 925 | ||
913 | FileId(0) 63..67 Other Read | 926 | FileId(0) 63..67 Read |
914 | "#]], | 927 | "#]], |
915 | ); | 928 | ); |
916 | } | 929 | } |
@@ -926,7 +939,7 @@ impl Foo { | |||
926 | let mut actual = String::new(); | 939 | let mut actual = String::new(); |
927 | { | 940 | { |
928 | let decl = refs.declaration; | 941 | let decl = refs.declaration; |
929 | format_to!(actual, "{} {:?}", decl.nav.debug_render(), decl.kind); | 942 | format_to!(actual, "{}", decl.nav.debug_render()); |
930 | if let Some(access) = decl.access { | 943 | if let Some(access) = decl.access { |
931 | format_to!(actual, " {:?}", access) | 944 | format_to!(actual, " {:?}", access) |
932 | } | 945 | } |
@@ -934,9 +947,9 @@ impl Foo { | |||
934 | } | 947 | } |
935 | 948 | ||
936 | for (file_id, references) in refs.references { | 949 | for (file_id, references) in refs.references { |
937 | for r in references { | 950 | for (range, access) in references { |
938 | format_to!(actual, "{:?} {:?} {:?}", file_id, r.range, r.kind); | 951 | format_to!(actual, "{:?} {:?}", file_id, range); |
939 | if let Some(access) = r.access { | 952 | if let Some(access) = access { |
940 | format_to!(actual, " {:?}", access); | 953 | format_to!(actual, " {:?}", access); |
941 | } | 954 | } |
942 | actual += "\n"; | 955 | actual += "\n"; |
@@ -957,13 +970,13 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> { | |||
957 | } | 970 | } |
958 | "#, | 971 | "#, |
959 | expect![[r#" | 972 | expect![[r#" |
960 | 'a LifetimeParam FileId(0) 55..57 55..57 Lifetime | 973 | 'a LifetimeParam FileId(0) 55..57 55..57 |
961 | 974 | ||
962 | FileId(0) 63..65 Lifetime | 975 | FileId(0) 63..65 |
963 | FileId(0) 71..73 Lifetime | 976 | FileId(0) 71..73 |
964 | FileId(0) 82..84 Lifetime | 977 | FileId(0) 82..84 |
965 | FileId(0) 95..97 Lifetime | 978 | FileId(0) 95..97 |
966 | FileId(0) 106..108 Lifetime | 979 | FileId(0) 106..108 |
967 | "#]], | 980 | "#]], |
968 | ); | 981 | ); |
969 | } | 982 | } |
@@ -975,10 +988,10 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> { | |||
975 | type Foo<'a, T> where T: 'a$0 = &'a T; | 988 | type Foo<'a, T> where T: 'a$0 = &'a T; |
976 | "#, | 989 | "#, |
977 | expect![[r#" | 990 | expect![[r#" |
978 | 'a LifetimeParam FileId(0) 9..11 9..11 Lifetime | 991 | 'a LifetimeParam FileId(0) 9..11 9..11 |
979 | 992 | ||
980 | FileId(0) 25..27 Lifetime | 993 | FileId(0) 25..27 |
981 | FileId(0) 31..33 Lifetime | 994 | FileId(0) 31..33 |
982 | "#]], | 995 | "#]], |
983 | ); | 996 | ); |
984 | } | 997 | } |
@@ -997,11 +1010,11 @@ impl<'a> Foo<'a> for &'a () { | |||
997 | } | 1010 | } |
998 | "#, | 1011 | "#, |
999 | expect![[r#" | 1012 | expect![[r#" |
1000 | 'a LifetimeParam FileId(0) 47..49 47..49 Lifetime | 1013 | 'a LifetimeParam FileId(0) 47..49 47..49 |
1001 | 1014 | ||
1002 | FileId(0) 55..57 Lifetime | 1015 | FileId(0) 55..57 |
1003 | FileId(0) 64..66 Lifetime | 1016 | FileId(0) 64..66 |
1004 | FileId(0) 89..91 Lifetime | 1017 | FileId(0) 89..91 |
1005 | "#]], | 1018 | "#]], |
1006 | ); | 1019 | ); |
1007 | } | 1020 | } |
@@ -1017,9 +1030,9 @@ fn main() { | |||
1017 | } | 1030 | } |
1018 | "#, | 1031 | "#, |
1019 | expect![[r#" | 1032 | expect![[r#" |
1020 | a Local FileId(0) 59..60 59..60 Other | 1033 | a Local FileId(0) 59..60 59..60 |
1021 | 1034 | ||
1022 | FileId(0) 80..81 Other Read | 1035 | FileId(0) 80..81 Read |
1023 | "#]], | 1036 | "#]], |
1024 | ); | 1037 | ); |
1025 | } | 1038 | } |
@@ -1035,9 +1048,9 @@ fn main() { | |||
1035 | } | 1048 | } |
1036 | "#, | 1049 | "#, |
1037 | expect![[r#" | 1050 | expect![[r#" |
1038 | a Local FileId(0) 59..60 59..60 Other | 1051 | a Local FileId(0) 59..60 59..60 |
1039 | 1052 | ||
1040 | FileId(0) 80..81 Other Read | 1053 | FileId(0) 80..81 Read |
1041 | "#]], | 1054 | "#]], |
1042 | ); | 1055 | ); |
1043 | } | 1056 | } |
@@ -1056,10 +1069,10 @@ fn foo<'a>() -> &'a () { | |||
1056 | } | 1069 | } |
1057 | "#, | 1070 | "#, |
1058 | expect![[r#" | 1071 | expect![[r#" |
1059 | 'a Label FileId(0) 29..32 29..31 Lifetime | 1072 | 'a Label FileId(0) 29..32 29..31 |
1060 | 1073 | ||
1061 | FileId(0) 80..82 Lifetime | 1074 | FileId(0) 80..82 |
1062 | FileId(0) 108..110 Lifetime | 1075 | FileId(0) 108..110 |
1063 | "#]], | 1076 | "#]], |
1064 | ); | 1077 | ); |
1065 | } | 1078 | } |
@@ -1073,9 +1086,9 @@ fn foo<const FOO$0: usize>() -> usize { | |||
1073 | } | 1086 | } |
1074 | "#, | 1087 | "#, |
1075 | expect![[r#" | 1088 | expect![[r#" |
1076 | FOO ConstParam FileId(0) 7..23 13..16 Other | 1089 | FOO ConstParam FileId(0) 7..23 13..16 |
1077 | 1090 | ||
1078 | FileId(0) 42..45 Other | 1091 | FileId(0) 42..45 |
1079 | "#]], | 1092 | "#]], |
1080 | ); | 1093 | ); |
1081 | } | 1094 | } |
@@ -1089,9 +1102,9 @@ trait Foo { | |||
1089 | } | 1102 | } |
1090 | "#, | 1103 | "#, |
1091 | expect![[r#" | 1104 | expect![[r#" |
1092 | Self TypeParam FileId(0) 6..9 6..9 Other | 1105 | Self TypeParam FileId(0) 6..9 6..9 |
1093 | 1106 | ||
1094 | FileId(0) 26..30 Other | 1107 | FileId(0) 26..30 |
1095 | "#]], | 1108 | "#]], |
1096 | ); | 1109 | ); |
1097 | } | 1110 | } |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index ebb1ce7dd..64992c72d 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, |
@@ -174,69 +167,96 @@ 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) { |
173 | let root = sema.parse(file_id); | ||
179 | let mut edit = TextEdit::builder(); | 174 | let mut edit = TextEdit::builder(); |
180 | for reference in references { | 175 | for reference in references { |
181 | let mut replacement_text = String::new(); | 176 | let (range, replacement) = match &reference.name_from_syntax(root.syntax()) { |
182 | let range = match reference.kind { | 177 | Some(NameLike::Name(_)) => (None, format!("{}", new_name)), |
183 | ReferenceKind::FieldShorthandForField => { | 178 | Some(NameLike::NameRef(name_ref)) => source_edit_from_name_ref(name_ref, new_name, def), |
184 | mark::hit!(test_rename_struct_field_for_shorthand); | 179 | Some(NameLike::Lifetime(_)) => (None, format!("{}", new_name)), |
185 | replacement_text.push_str(new_name); | 180 | None => (None, new_name.to_owned()), |
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 | }; | 181 | }; |
209 | edit.replace(range, replacement_text); | 182 | // FIXME: Some(range) will be incorrect when we are inside macros |
183 | edit.replace(range.unwrap_or(reference.range), replacement); | ||
210 | } | 184 | } |
211 | (file_id, edit.finish()) | 185 | (file_id, edit.finish()) |
212 | } | 186 | } |
213 | 187 | ||
214 | fn edit_text_range_for_record_field_expr_or_pat( | 188 | fn source_edit_from_name_ref( |
215 | sema: &Semantics<RootDatabase>, | 189 | name_ref: &ast::NameRef, |
216 | file_range: FileRange, | ||
217 | new_name: &str, | 190 | new_name: &str, |
218 | ) -> TextRange { | 191 | def: Definition, |
219 | let source_file = sema.parse(file_range.file_id); | 192 | ) -> (Option<TextRange>, String) { |
220 | let file_syntax = source_file.syntax(); | 193 | if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) { |
221 | let original_range = file_range.range; | 194 | let rcf_name_ref = record_field.name_ref(); |
222 | 195 | let rcf_expr = record_field.expr(); | |
223 | syntax::algo::find_node_at_range::<ast::RecordExprField>(file_syntax, original_range) | 196 | 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()) { | 197 | // 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()), | 198 | (Some(field_name), Some(init)) => { |
226 | _ => None, | 199 | if field_name == *name_ref { |
227 | }) | 200 | if init.text() == new_name { |
228 | .or_else(|| { | 201 | mark::hit!(test_rename_field_put_init_shorthand); |
229 | syntax::algo::find_node_at_range::<ast::RecordPatField>(file_syntax, original_range) | 202 | // same names, we can use a shorthand here instead |
230 | .and_then(|field_pat| match field_pat.pat() { | 203 | // we do not want to erase attributes hence this range start |
231 | Some(ast::Pat::IdentPat(pat)) | 204 | let s = field_name.syntax().text_range().start(); |
232 | if pat.name().map(|n| n.to_string()).as_deref() == Some(new_name) => | 205 | let e = record_field.syntax().text_range().end(); |
233 | { | 206 | return (Some(TextRange::new(s, e)), format!("{}", new_name)); |
234 | Some(field_pat.syntax().text_range()) | ||
235 | } | 207 | } |
236 | _ => None, | 208 | } else if init == *name_ref { |
237 | }) | 209 | if field_name.text() == new_name { |
238 | }) | 210 | mark::hit!(test_rename_local_put_init_shorthand); |
239 | .unwrap_or(original_range) | 211 | // same names, we can use a shorthand here instead |
212 | // we do not want to erase attributes hence this range start | ||
213 | let s = field_name.syntax().text_range().start(); | ||
214 | let e = record_field.syntax().text_range().end(); | ||
215 | return (Some(TextRange::new(s, e)), format!("{}", new_name)); | ||
216 | } | ||
217 | } | ||
218 | } | ||
219 | // init shorthand | ||
220 | (None, Some(_)) => { | ||
221 | // FIXME: instead of splitting the shorthand, recursively trigger a rename of the | ||
222 | // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 | ||
223 | match def { | ||
224 | Definition::Field(_) => { | ||
225 | mark::hit!(test_rename_field_in_field_shorthand); | ||
226 | let s = name_ref.syntax().text_range().start(); | ||
227 | return (Some(TextRange::empty(s)), format!("{}: ", new_name)); | ||
228 | } | ||
229 | Definition::Local(_) => { | ||
230 | mark::hit!(test_rename_local_in_field_shorthand); | ||
231 | let s = name_ref.syntax().text_range().end(); | ||
232 | return (Some(TextRange::empty(s)), format!(": {}", new_name)); | ||
233 | } | ||
234 | _ => {} | ||
235 | } | ||
236 | } | ||
237 | _ => {} | ||
238 | } | ||
239 | } | ||
240 | if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) { | ||
241 | let rcf_name_ref = record_field.name_ref(); | ||
242 | let rcf_pat = record_field.pat(); | ||
243 | match (rcf_name_ref, rcf_pat) { | ||
244 | // field: rename | ||
245 | (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { | ||
246 | // field name is being renamed | ||
247 | if pat.name().map_or(false, |it| it.text() == new_name) { | ||
248 | mark::hit!(test_rename_field_put_init_shorthand_pat); | ||
249 | // same names, we can use a shorthand here instead | ||
250 | // we do not want to erase attributes hence this range start | ||
251 | let s = field_name.syntax().text_range().start(); | ||
252 | let e = record_field.syntax().text_range().end(); | ||
253 | return (Some(TextRange::new(s, e)), format!("{}", new_name)); | ||
254 | } | ||
255 | } | ||
256 | _ => {} | ||
257 | } | ||
258 | } | ||
259 | (None, format!("{}", new_name)) | ||
240 | } | 260 | } |
241 | 261 | ||
242 | fn rename_mod( | 262 | fn rename_mod( |
@@ -277,7 +297,7 @@ fn rename_mod( | |||
277 | let def = Definition::ModuleDef(ModuleDef::Module(module)); | 297 | let def = Definition::ModuleDef(ModuleDef::Module(module)); |
278 | let usages = def.usages(sema).all(); | 298 | let usages = def.usages(sema).all(); |
279 | let ref_edits = usages.iter().map(|(&file_id, references)| { | 299 | let ref_edits = usages.iter().map(|(&file_id, references)| { |
280 | source_edit_from_references(sema, file_id, references, new_name) | 300 | source_edit_from_references(sema, file_id, references, def, new_name) |
281 | }); | 301 | }); |
282 | source_change.extend(ref_edits); | 302 | source_change.extend(ref_edits); |
283 | 303 | ||
@@ -346,7 +366,7 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe | |||
346 | let usages = def.usages(sema).all(); | 366 | let usages = def.usages(sema).all(); |
347 | let mut source_change = SourceChange::default(); | 367 | let mut source_change = SourceChange::default(); |
348 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 368 | source_change.extend(usages.iter().map(|(&file_id, references)| { |
349 | source_edit_from_references(sema, file_id, references, "self") | 369 | source_edit_from_references(sema, file_id, references, def, "self") |
350 | })); | 370 | })); |
351 | source_change.insert_source_edit( | 371 | source_change.insert_source_edit( |
352 | file_id.original_file(sema.db), | 372 | file_id.original_file(sema.db), |
@@ -403,7 +423,7 @@ fn rename_self_to_param( | |||
403 | let mut source_change = SourceChange::default(); | 423 | let mut source_change = SourceChange::default(); |
404 | source_change.insert_source_edit(file_id.original_file(sema.db), edit); | 424 | source_change.insert_source_edit(file_id.original_file(sema.db), edit); |
405 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 425 | source_change.extend(usages.iter().map(|(&file_id, references)| { |
406 | source_edit_from_references(sema, file_id, &references, new_name) | 426 | source_edit_from_references(sema, file_id, &references, def, new_name) |
407 | })); | 427 | })); |
408 | Ok(source_change) | 428 | Ok(source_change) |
409 | } | 429 | } |
@@ -457,7 +477,7 @@ fn rename_reference( | |||
457 | } | 477 | } |
458 | let mut source_change = SourceChange::default(); | 478 | let mut source_change = SourceChange::default(); |
459 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 479 | source_change.extend(usages.iter().map(|(&file_id, references)| { |
460 | source_edit_from_references(sema, file_id, &references, new_name) | 480 | source_edit_from_references(sema, file_id, &references, def, new_name) |
461 | })); | 481 | })); |
462 | 482 | ||
463 | let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; | 483 | let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; |
@@ -545,10 +565,8 @@ mod tests { | |||
545 | 565 | ||
546 | fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) { | 566 | fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) { |
547 | let (analysis, position) = fixture::position(ra_fixture); | 567 | let (analysis, position) = fixture::position(ra_fixture); |
548 | let source_change = analysis | 568 | let source_change = |
549 | .rename(position, new_name) | 569 | 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) | 570 | expect.assert_debug_eq(&source_change) |
553 | } | 571 | } |
554 | 572 | ||
@@ -792,8 +810,8 @@ impl Foo { | |||
792 | } | 810 | } |
793 | 811 | ||
794 | #[test] | 812 | #[test] |
795 | fn test_rename_struct_field_for_shorthand() { | 813 | fn test_rename_field_in_field_shorthand() { |
796 | mark::check!(test_rename_struct_field_for_shorthand); | 814 | mark::check!(test_rename_field_in_field_shorthand); |
797 | check( | 815 | check( |
798 | "j", | 816 | "j", |
799 | r#" | 817 | r#" |
@@ -818,8 +836,8 @@ impl Foo { | |||
818 | } | 836 | } |
819 | 837 | ||
820 | #[test] | 838 | #[test] |
821 | fn test_rename_local_for_field_shorthand() { | 839 | fn test_rename_local_in_field_shorthand() { |
822 | mark::check!(test_rename_local_for_field_shorthand); | 840 | mark::check!(test_rename_local_in_field_shorthand); |
823 | check( | 841 | check( |
824 | "j", | 842 | "j", |
825 | r#" | 843 | r#" |
@@ -1417,8 +1435,8 @@ impl Foo { | |||
1417 | } | 1435 | } |
1418 | 1436 | ||
1419 | #[test] | 1437 | #[test] |
1420 | fn test_initializer_use_field_init_shorthand() { | 1438 | fn test_rename_field_put_init_shorthand() { |
1421 | mark::check!(test_rename_field_expr_pat); | 1439 | mark::check!(test_rename_field_put_init_shorthand); |
1422 | check( | 1440 | check( |
1423 | "bar", | 1441 | "bar", |
1424 | r#" | 1442 | r#" |
@@ -1439,7 +1457,30 @@ fn foo(bar: i32) -> Foo { | |||
1439 | } | 1457 | } |
1440 | 1458 | ||
1441 | #[test] | 1459 | #[test] |
1460 | fn test_rename_local_put_init_shorthand() { | ||
1461 | mark::check!(test_rename_local_put_init_shorthand); | ||
1462 | check( | ||
1463 | "i", | ||
1464 | r#" | ||
1465 | struct Foo { i: i32 } | ||
1466 | |||
1467 | fn foo(bar$0: i32) -> Foo { | ||
1468 | Foo { i: bar } | ||
1469 | } | ||
1470 | "#, | ||
1471 | r#" | ||
1472 | struct Foo { i: i32 } | ||
1473 | |||
1474 | fn foo(i: i32) -> Foo { | ||
1475 | Foo { i } | ||
1476 | } | ||
1477 | "#, | ||
1478 | ); | ||
1479 | } | ||
1480 | |||
1481 | #[test] | ||
1442 | fn test_struct_field_destructure_into_shorthand() { | 1482 | fn test_struct_field_destructure_into_shorthand() { |
1483 | mark::check!(test_rename_field_put_init_shorthand_pat); | ||
1443 | check( | 1484 | check( |
1444 | "baz", | 1485 | "baz", |
1445 | r#" | 1486 | r#" |