aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
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.rs357
-rw-r--r--crates/ide/src/references/rename.rs197
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
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::{
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};
21use rustc_hash::FxHashMap;
21use syntax::{ 22use 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)]
30pub struct ReferenceSearchResult { 31pub 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)]
36pub struct Declaration { 37pub struct Declaration {
37 nav: NavigationTarget, 38 pub nav: NavigationTarget,
38 kind: ReferenceKind, 39 pub access: Option<ReferenceAccess>,
39 access: Option<ReferenceAccess>,
40}
41
42impl 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
59pub(crate) fn find_all_refs( 42pub(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
118fn find_name( 92fn 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
146fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { 122fn 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
195fn 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
209fn 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
220fn 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)]
220mod tests { 233mod 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() {
443fn foo(i : u32) -> u32 { i$0 } 456fn 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 }
457fn foo(i$0 : u32) -> u32 { i } 470fn 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;
791fn g() { f(); } 804fn 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> {
975type Foo<'a, T> where T: 'a$0 = &'a T; 988type 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};
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,
@@ -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
214fn edit_text_range_for_record_field_expr_or_pat( 188fn 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
242fn rename_mod( 262fn 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#"
1465struct Foo { i: i32 }
1466
1467fn foo(bar$0: i32) -> Foo {
1468 Foo { i: bar }
1469}
1470"#,
1471 r#"
1472struct Foo { i: i32 }
1473
1474fn 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#"