aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-02-12 18:01:36 +0000
committerGitHub <[email protected]>2021-02-12 18:01:36 +0000
commit88253907f4bc3beaa7b8f2e58cb652f653f92d56 (patch)
tree1669ff0e6ab614c679f245f84d0f9a8202366e0e /crates/ide/src
parent4d51b5644458c7dcb97a4d445f1b379cd2548a78 (diff)
parentfd6cf4d566174dbdb50259bbbfdaf5a12f81544d (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/src')
-rw-r--r--crates/ide/src/call_hierarchy.rs8
-rw-r--r--crates/ide/src/lib.rs4
-rw-r--r--crates/ide/src/references.rs370
-rw-r--r--crates/ide/src/references/rename.rs197
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
12pub(crate) mod rename; 12pub(crate) mod rename;
13 13
14use either::Either;
15use hir::Semantics; 14use hir::Semantics;
16use ide_db::{ 15use 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};
21use rustc_hash::FxHashMap;
22use syntax::{ 22use 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)]
31pub struct ReferenceSearchResult { 31pub 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)]
37pub struct Declaration { 37pub 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
43impl 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
69impl 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
78pub(crate) fn find_all_refs( 42pub(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
137fn find_name( 88fn 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
165fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { 118fn 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
191fn 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
205fn 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
216fn 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)]
239mod tests { 229mod 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() {
462fn foo(i : u32) -> u32 { i$0 } 452fn 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 }
476fn foo(i$0 : u32) -> u32 { i } 466fn 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;
810fn g() { f(); } 800fn 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> {
994type Foo<'a, T> where T: 'a$0 = &'a T; 984type 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};
4use either::Either; 4use either::Either;
5use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics}; 5use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics};
6use ide_db::{ 6use 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};
12use stdx::never; 12use stdx::never;
@@ -17,10 +17,7 @@ use syntax::{
17use test_utils::mark; 17use test_utils::mark;
18use text_edit::TextEdit; 18use text_edit::TextEdit;
19 19
20use crate::{ 20use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange};
21 display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, SourceChange,
22 TextRange,
23};
24 21
25type RenameResult<T> = Result<T, RenameError>; 22type 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.
44pub(crate) fn prepare_rename( 43pub(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
126enum NameLike {
127 Name(ast::Name),
128 NameRef(ast::NameRef),
129 Lifetime(ast::Lifetime),
130}
131
132fn find_name_like( 125fn 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
173fn source_edit_from_references( 166fn 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
214fn edit_text_range_for_record_field_expr_or_pat( 186fn 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
242fn rename_mod( 260fn 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#"
1463struct Foo { i: i32 }
1464
1465fn foo(bar$0: i32) -> Foo {
1466 Foo { i: bar }
1467}
1468"#,
1469 r#"
1470struct Foo { i: i32 }
1471
1472fn 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#"