]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/references/rename.rs
Properly handle shorthands in destructure patterns when renaming
[rust.git] / crates / ide / src / references / rename.rs
1 //! FIXME: write short doc here
2
3 use hir::{Module, ModuleDef, ModuleSource, Semantics};
4 use ide_db::base_db::SourceDatabaseExt;
5 use ide_db::{
6     defs::{Definition, NameClass, NameRefClass},
7     RootDatabase,
8 };
9
10 use std::{
11     convert::TryInto,
12     error::Error,
13     fmt::{self, Display},
14 };
15 use syntax::{
16     algo::find_node_at_offset,
17     ast::{self, NameOwner},
18     lex_single_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken,
19 };
20 use test_utils::mark;
21 use text_edit::TextEdit;
22
23 use crate::{
24     references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind,
25     SourceChange, SourceFileEdit, TextRange, TextSize,
26 };
27
28 #[derive(Debug)]
29 pub struct RenameError(pub(crate) String);
30
31 impl fmt::Display for RenameError {
32     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33         Display::fmt(&self.0, f)
34     }
35 }
36
37 impl Error for RenameError {}
38
39 pub(crate) fn rename(
40     db: &RootDatabase,
41     position: FilePosition,
42     new_name: &str,
43 ) -> Result<RangeInfo<SourceChange>, RenameError> {
44     let sema = Semantics::new(db);
45     rename_with_semantics(&sema, position, new_name)
46 }
47
48 pub(crate) fn rename_with_semantics(
49     sema: &Semantics<RootDatabase>,
50     position: FilePosition,
51     new_name: &str,
52 ) -> Result<RangeInfo<SourceChange>, RenameError> {
53     match lex_single_syntax_kind(new_name) {
54         Some(res) => match res {
55             (SyntaxKind::IDENT, _) => (),
56             (SyntaxKind::UNDERSCORE, _) => (),
57             (SyntaxKind::SELF_KW, _) => return rename_to_self(&sema, position),
58             (_, Some(syntax_error)) => {
59                 return Err(RenameError(format!("Invalid name `{}`: {}", new_name, syntax_error)))
60             }
61             (_, None) => {
62                 return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name)))
63             }
64         },
65         None => return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))),
66     }
67
68     let source_file = sema.parse(position.file_id);
69     let syntax = source_file.syntax();
70     if let Some(module) = find_module_at_offset(&sema, position, syntax) {
71         rename_mod(&sema, position, module, new_name)
72     } else if let Some(self_token) =
73         syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
74     {
75         rename_self_to_param(&sema, position, self_token, new_name)
76     } else {
77         rename_reference(&sema, position, new_name)
78     }
79 }
80
81 fn find_module_at_offset(
82     sema: &Semantics<RootDatabase>,
83     position: FilePosition,
84     syntax: &SyntaxNode,
85 ) -> Option<Module> {
86     let ident = syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::IDENT)?;
87
88     let module = match_ast! {
89         match (ident.parent()) {
90             ast::NameRef(name_ref) => {
91                 match NameRefClass::classify(sema, &name_ref)? {
92                     NameRefClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module,
93                     _ => return None,
94                 }
95             },
96             ast::Name(name) => {
97                 match NameClass::classify(&sema, &name)? {
98                     NameClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module,
99                     _ => return None,
100                 }
101             },
102             _ => return None,
103         }
104     };
105
106     Some(module)
107 }
108
109 fn source_edit_from_reference(
110     sema: &Semantics<RootDatabase>,
111     reference: Reference,
112     new_name: &str,
113 ) -> SourceFileEdit {
114     let mut replacement_text = String::new();
115     let file_id = reference.file_range.file_id;
116     let range = match reference.kind {
117         ReferenceKind::FieldShorthandForField => {
118             mark::hit!(test_rename_struct_field_for_shorthand);
119             replacement_text.push_str(new_name);
120             replacement_text.push_str(": ");
121             TextRange::new(reference.file_range.range.start(), reference.file_range.range.start())
122         }
123         ReferenceKind::FieldShorthandForLocal => {
124             mark::hit!(test_rename_local_for_field_shorthand);
125             replacement_text.push_str(": ");
126             replacement_text.push_str(new_name);
127             TextRange::new(reference.file_range.range.end(), reference.file_range.range.end())
128         }
129         ReferenceKind::RecordExprField => {
130             replacement_text.push_str(new_name);
131             let mut range = reference.file_range.range;
132             if let Some(field_expr) = syntax::algo::find_node_at_range::<ast::RecordExprField>(
133                 sema.parse(file_id).syntax(),
134                 reference.file_range.range,
135             ) {
136                 // use shorthand initializer if we were to write foo: foo
137                 if let Some(name) = field_expr.expr().and_then(|e| e.name_ref()) {
138                     if &name.to_string() == new_name {
139                         range = field_expr.syntax().text_range();
140                     }
141                 }
142             }
143             range
144         }
145         _ => {
146             replacement_text.push_str(new_name);
147             reference.file_range.range
148         }
149     };
150     SourceFileEdit { file_id, edit: TextEdit::replace(range, replacement_text) }
151 }
152
153 fn rename_mod(
154     sema: &Semantics<RootDatabase>,
155     position: FilePosition,
156     module: Module,
157     new_name: &str,
158 ) -> Result<RangeInfo<SourceChange>, RenameError> {
159     let mut source_file_edits = Vec::new();
160     let mut file_system_edits = Vec::new();
161
162     let src = module.definition_source(sema.db);
163     let file_id = src.file_id.original_file(sema.db);
164     match src.value {
165         ModuleSource::SourceFile(..) => {
166             // mod is defined in path/to/dir/mod.rs
167             let dst = if module.is_mod_rs(sema.db) {
168                 format!("../{}/mod.rs", new_name)
169             } else {
170                 format!("{}.rs", new_name)
171             };
172             let move_file = FileSystemEdit::MoveFile { src: file_id, anchor: file_id, dst };
173             file_system_edits.push(move_file);
174         }
175         ModuleSource::Module(..) => {}
176     }
177
178     if let Some(src) = module.declaration_source(sema.db) {
179         let file_id = src.file_id.original_file(sema.db);
180         let name = src.value.name().unwrap();
181         let edit = SourceFileEdit {
182             file_id,
183             edit: TextEdit::replace(name.syntax().text_range(), new_name.into()),
184         };
185         source_file_edits.push(edit);
186     }
187
188     let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)
189         .ok_or_else(|| RenameError("No references found at position".to_string()))?;
190     let ref_edits = refs
191         .references
192         .into_iter()
193         .map(|reference| source_edit_from_reference(sema, reference, new_name));
194     source_file_edits.extend(ref_edits);
195
196     Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
197 }
198
199 fn rename_to_self(
200     sema: &Semantics<RootDatabase>,
201     position: FilePosition,
202 ) -> Result<RangeInfo<SourceChange>, RenameError> {
203     let source_file = sema.parse(position.file_id);
204     let syn = source_file.syntax();
205
206     let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset)
207         .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?;
208     let params =
209         fn_def.param_list().ok_or_else(|| RenameError("Method has no parameters".to_string()))?;
210     if params.self_param().is_some() {
211         return Err(RenameError("Method already has a self parameter".to_string()));
212     }
213     let first_param =
214         params.params().next().ok_or_else(|| RenameError("Method has no parameters".into()))?;
215     let mutable = match first_param.ty() {
216         Some(ast::Type::RefType(rt)) => rt.mut_token().is_some(),
217         _ => return Err(RenameError("Not renaming other types".to_string())),
218     };
219
220     let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)
221         .ok_or_else(|| RenameError("No reference found at position".to_string()))?;
222
223     let param_range = first_param.syntax().text_range();
224     let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs
225         .into_iter()
226         .partition(|reference| param_range.intersect(reference.file_range.range).is_some());
227
228     if param_ref.is_empty() {
229         return Err(RenameError("Parameter to rename not found".to_string()));
230     }
231
232     let mut edits = usages
233         .into_iter()
234         .map(|reference| source_edit_from_reference(sema, reference, "self"))
235         .collect::<Vec<_>>();
236
237     edits.push(SourceFileEdit {
238         file_id: position.file_id,
239         edit: TextEdit::replace(
240             param_range,
241             String::from(if mutable { "&mut self" } else { "&self" }),
242         ),
243     });
244
245     Ok(RangeInfo::new(range, SourceChange::from(edits)))
246 }
247
248 fn text_edit_from_self_param(
249     syn: &SyntaxNode,
250     self_param: &ast::SelfParam,
251     new_name: &str,
252 ) -> Option<TextEdit> {
253     fn target_type_name(impl_def: &ast::Impl) -> Option<String> {
254         if let Some(ast::Type::PathType(p)) = impl_def.self_ty() {
255             return Some(p.path()?.segment()?.name_ref()?.text().to_string());
256         }
257         None
258     }
259
260     let impl_def = find_node_at_offset::<ast::Impl>(syn, self_param.syntax().text_range().start())?;
261     let type_name = target_type_name(&impl_def)?;
262
263     let mut replacement_text = String::from(new_name);
264     replacement_text.push_str(": ");
265     replacement_text.push_str(self_param.mut_token().map_or("&", |_| "&mut "));
266     replacement_text.push_str(type_name.as_str());
267
268     Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text))
269 }
270
271 fn rename_self_to_param(
272     sema: &Semantics<RootDatabase>,
273     position: FilePosition,
274     self_token: SyntaxToken,
275     new_name: &str,
276 ) -> Result<RangeInfo<SourceChange>, RenameError> {
277     let source_file = sema.parse(position.file_id);
278     let syn = source_file.syntax();
279
280     let text = sema.db.file_text(position.file_id);
281     let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset)
282         .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?;
283     let search_range = fn_def.syntax().text_range();
284
285     let mut edits: Vec<SourceFileEdit> = vec![];
286
287     for (idx, _) in text.match_indices("self") {
288         let offset: TextSize = idx.try_into().unwrap();
289         if !search_range.contains_inclusive(offset) {
290             continue;
291         }
292         if let Some(ref usage) =
293             syn.token_at_offset(offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
294         {
295             let edit = if let Some(ref self_param) = ast::SelfParam::cast(usage.parent()) {
296                 text_edit_from_self_param(syn, self_param, new_name)
297                     .ok_or_else(|| RenameError("No target type found".to_string()))?
298             } else {
299                 TextEdit::replace(usage.text_range(), String::from(new_name))
300             };
301             edits.push(SourceFileEdit { file_id: position.file_id, edit });
302         }
303     }
304
305     let range = ast::SelfParam::cast(self_token.parent())
306         .map_or(self_token.text_range(), |p| p.syntax().text_range());
307
308     Ok(RangeInfo::new(range, SourceChange::from(edits)))
309 }
310
311 fn rename_reference(
312     sema: &Semantics<RootDatabase>,
313     position: FilePosition,
314     new_name: &str,
315 ) -> Result<RangeInfo<SourceChange>, RenameError> {
316     let RangeInfo { range, info: refs } = match find_all_refs(sema, position, None) {
317         Some(range_info) => range_info,
318         None => return Err(RenameError("No references found at position".to_string())),
319     };
320
321     let edit = refs
322         .into_iter()
323         .map(|reference| source_edit_from_reference(sema, reference, new_name))
324         .collect::<Vec<_>>();
325
326     if edit.is_empty() {
327         return Err(RenameError("No references found at position".to_string()));
328     }
329
330     Ok(RangeInfo::new(range, SourceChange::from(edit)))
331 }
332
333 #[cfg(test)]
334 mod tests {
335     use expect_test::{expect, Expect};
336     use stdx::trim_indent;
337     use test_utils::{assert_eq_text, mark};
338     use text_edit::TextEdit;
339
340     use crate::{fixture, FileId};
341
342     fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
343         let ra_fixture_after = &trim_indent(ra_fixture_after);
344         let (analysis, position) = fixture::position(ra_fixture_before);
345         let rename_result = analysis
346             .rename(position, new_name)
347             .unwrap_or_else(|err| panic!("Rename to '{}' was cancelled: {}", new_name, err));
348         match rename_result {
349             Ok(source_change) => {
350                 let mut text_edit_builder = TextEdit::builder();
351                 let mut file_id: Option<FileId> = None;
352                 for edit in source_change.info.source_file_edits {
353                     file_id = Some(edit.file_id);
354                     for indel in edit.edit.into_iter() {
355                         text_edit_builder.replace(indel.delete, indel.insert);
356                     }
357                 }
358                 let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string();
359                 text_edit_builder.finish().apply(&mut result);
360                 assert_eq_text!(ra_fixture_after, &*result);
361             }
362             Err(err) => {
363                 if ra_fixture_after.starts_with("error:") {
364                     let error_message = ra_fixture_after
365                         .chars()
366                         .into_iter()
367                         .skip("error:".len())
368                         .collect::<String>();
369                     assert_eq!(error_message.trim(), err.to_string());
370                     return;
371                 } else {
372                     panic!("Rename to '{}' failed unexpectedly: {}", new_name, err)
373                 }
374             }
375         };
376     }
377
378     fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) {
379         let (analysis, position) = fixture::position(ra_fixture);
380         let source_change = analysis
381             .rename(position, new_name)
382             .unwrap()
383             .expect("Expect returned RangeInfo to be Some, but was None");
384         expect.assert_debug_eq(&source_change)
385     }
386
387     #[test]
388     fn test_rename_to_underscore() {
389         check("_", r#"fn main() { let i<|> = 1; }"#, r#"fn main() { let _ = 1; }"#);
390     }
391
392     #[test]
393     fn test_rename_to_raw_identifier() {
394         check("r#fn", r#"fn main() { let i<|> = 1; }"#, r#"fn main() { let r#fn = 1; }"#);
395     }
396
397     #[test]
398     fn test_rename_to_invalid_identifier1() {
399         check(
400             "invalid!",
401             r#"fn main() { let i<|> = 1; }"#,
402             "error: Invalid name `invalid!`: not an identifier",
403         );
404     }
405
406     #[test]
407     fn test_rename_to_invalid_identifier2() {
408         check(
409             "multiple tokens",
410             r#"fn main() { let i<|> = 1; }"#,
411             "error: Invalid name `multiple tokens`: not an identifier",
412         );
413     }
414
415     #[test]
416     fn test_rename_to_invalid_identifier3() {
417         check(
418             "let",
419             r#"fn main() { let i<|> = 1; }"#,
420             "error: Invalid name `let`: not an identifier",
421         );
422     }
423
424     #[test]
425     fn test_rename_for_local() {
426         check(
427             "k",
428             r#"
429 fn main() {
430     let mut i = 1;
431     let j = 1;
432     i = i<|> + j;
433
434     { i = 0; }
435
436     i = 5;
437 }
438 "#,
439             r#"
440 fn main() {
441     let mut k = 1;
442     let j = 1;
443     k = k + j;
444
445     { k = 0; }
446
447     k = 5;
448 }
449 "#,
450         );
451     }
452
453     #[test]
454     fn test_rename_unresolved_reference() {
455         check(
456             "new_name",
457             r#"fn main() { let _ = unresolved_ref<|>; }"#,
458             "error: No references found at position",
459         );
460     }
461
462     #[test]
463     fn test_rename_for_macro_args() {
464         check(
465             "b",
466             r#"
467 macro_rules! foo {($i:ident) => {$i} }
468 fn main() {
469     let a<|> = "test";
470     foo!(a);
471 }
472 "#,
473             r#"
474 macro_rules! foo {($i:ident) => {$i} }
475 fn main() {
476     let b = "test";
477     foo!(b);
478 }
479 "#,
480         );
481     }
482
483     #[test]
484     fn test_rename_for_macro_args_rev() {
485         check(
486             "b",
487             r#"
488 macro_rules! foo {($i:ident) => {$i} }
489 fn main() {
490     let a = "test";
491     foo!(a<|>);
492 }
493 "#,
494             r#"
495 macro_rules! foo {($i:ident) => {$i} }
496 fn main() {
497     let b = "test";
498     foo!(b);
499 }
500 "#,
501         );
502     }
503
504     #[test]
505     fn test_rename_for_macro_define_fn() {
506         check(
507             "bar",
508             r#"
509 macro_rules! define_fn {($id:ident) => { fn $id{} }}
510 define_fn!(foo);
511 fn main() {
512     fo<|>o();
513 }
514 "#,
515             r#"
516 macro_rules! define_fn {($id:ident) => { fn $id{} }}
517 define_fn!(bar);
518 fn main() {
519     bar();
520 }
521 "#,
522         );
523     }
524
525     #[test]
526     fn test_rename_for_macro_define_fn_rev() {
527         check(
528             "bar",
529             r#"
530 macro_rules! define_fn {($id:ident) => { fn $id{} }}
531 define_fn!(fo<|>o);
532 fn main() {
533     foo();
534 }
535 "#,
536             r#"
537 macro_rules! define_fn {($id:ident) => { fn $id{} }}
538 define_fn!(bar);
539 fn main() {
540     bar();
541 }
542 "#,
543         );
544     }
545
546     #[test]
547     fn test_rename_for_param_inside() {
548         check("j", r#"fn foo(i : u32) -> u32 { i<|> }"#, r#"fn foo(j : u32) -> u32 { j }"#);
549     }
550
551     #[test]
552     fn test_rename_refs_for_fn_param() {
553         check("j", r#"fn foo(i<|> : u32) -> u32 { i }"#, r#"fn foo(j : u32) -> u32 { j }"#);
554     }
555
556     #[test]
557     fn test_rename_for_mut_param() {
558         check("j", r#"fn foo(mut i<|> : u32) -> u32 { i }"#, r#"fn foo(mut j : u32) -> u32 { j }"#);
559     }
560
561     #[test]
562     fn test_rename_struct_field() {
563         check(
564             "j",
565             r#"
566 struct Foo { i<|>: i32 }
567
568 impl Foo {
569     fn new(i: i32) -> Self {
570         Self { i: i }
571     }
572 }
573 "#,
574             r#"
575 struct Foo { j: i32 }
576
577 impl Foo {
578     fn new(i: i32) -> Self {
579         Self { j: i }
580     }
581 }
582 "#,
583         );
584     }
585
586     #[test]
587     fn test_rename_struct_field_for_shorthand() {
588         mark::check!(test_rename_struct_field_for_shorthand);
589         check(
590             "j",
591             r#"
592 struct Foo { i<|>: i32 }
593
594 impl Foo {
595     fn new(i: i32) -> Self {
596         Self { i }
597     }
598 }
599 "#,
600             r#"
601 struct Foo { j: i32 }
602
603 impl Foo {
604     fn new(i: i32) -> Self {
605         Self { j: i }
606     }
607 }
608 "#,
609         );
610     }
611
612     #[test]
613     fn test_rename_local_for_field_shorthand() {
614         mark::check!(test_rename_local_for_field_shorthand);
615         check(
616             "j",
617             r#"
618 struct Foo { i: i32 }
619
620 impl Foo {
621     fn new(i<|>: i32) -> Self {
622         Self { i }
623     }
624 }
625 "#,
626             r#"
627 struct Foo { i: i32 }
628
629 impl Foo {
630     fn new(j: i32) -> Self {
631         Self { i: j }
632     }
633 }
634 "#,
635         );
636     }
637
638     #[test]
639     fn test_field_shorthand_correct_struct() {
640         check(
641             "j",
642             r#"
643 struct Foo { i<|>: i32 }
644 struct Bar { i: i32 }
645
646 impl Bar {
647     fn new(i: i32) -> Self {
648         Self { i }
649     }
650 }
651 "#,
652             r#"
653 struct Foo { j: i32 }
654 struct Bar { i: i32 }
655
656 impl Bar {
657     fn new(i: i32) -> Self {
658         Self { i }
659     }
660 }
661 "#,
662         );
663     }
664
665     #[test]
666     fn test_shadow_local_for_struct_shorthand() {
667         check(
668             "j",
669             r#"
670 struct Foo { i: i32 }
671
672 fn baz(i<|>: i32) -> Self {
673      let x = Foo { i };
674      {
675          let i = 0;
676          Foo { i }
677      }
678 }
679 "#,
680             r#"
681 struct Foo { i: i32 }
682
683 fn baz(j: i32) -> Self {
684      let x = Foo { i: j };
685      {
686          let i = 0;
687          Foo { i }
688      }
689 }
690 "#,
691         );
692     }
693
694     #[test]
695     fn test_rename_mod() {
696         check_expect(
697             "foo2",
698             r#"
699 //- /lib.rs
700 mod bar;
701
702 //- /bar.rs
703 mod foo<|>;
704
705 //- /bar/foo.rs
706 // empty
707 "#,
708             expect![[r#"
709                 RangeInfo {
710                     range: 4..7,
711                     info: SourceChange {
712                         source_file_edits: [
713                             SourceFileEdit {
714                                 file_id: FileId(
715                                     1,
716                                 ),
717                                 edit: TextEdit {
718                                     indels: [
719                                         Indel {
720                                             insert: "foo2",
721                                             delete: 4..7,
722                                         },
723                                     ],
724                                 },
725                             },
726                         ],
727                         file_system_edits: [
728                             MoveFile {
729                                 src: FileId(
730                                     2,
731                                 ),
732                                 anchor: FileId(
733                                     2,
734                                 ),
735                                 dst: "foo2.rs",
736                             },
737                         ],
738                         is_snippet: false,
739                     },
740                 }
741             "#]],
742         );
743     }
744
745     #[test]
746     fn test_rename_mod_in_use_tree() {
747         check_expect(
748             "quux",
749             r#"
750 //- /main.rs
751 pub mod foo;
752 pub mod bar;
753 fn main() {}
754
755 //- /foo.rs
756 pub struct FooContent;
757
758 //- /bar.rs
759 use crate::foo<|>::FooContent;
760 "#,
761             expect![[r#"
762                 RangeInfo {
763                     range: 11..14,
764                     info: SourceChange {
765                         source_file_edits: [
766                             SourceFileEdit {
767                                 file_id: FileId(
768                                     0,
769                                 ),
770                                 edit: TextEdit {
771                                     indels: [
772                                         Indel {
773                                             insert: "quux",
774                                             delete: 8..11,
775                                         },
776                                     ],
777                                 },
778                             },
779                             SourceFileEdit {
780                                 file_id: FileId(
781                                     2,
782                                 ),
783                                 edit: TextEdit {
784                                     indels: [
785                                         Indel {
786                                             insert: "quux",
787                                             delete: 11..14,
788                                         },
789                                     ],
790                                 },
791                             },
792                         ],
793                         file_system_edits: [
794                             MoveFile {
795                                 src: FileId(
796                                     1,
797                                 ),
798                                 anchor: FileId(
799                                     1,
800                                 ),
801                                 dst: "quux.rs",
802                             },
803                         ],
804                         is_snippet: false,
805                     },
806                 }
807             "#]],
808         );
809     }
810
811     #[test]
812     fn test_rename_mod_in_dir() {
813         check_expect(
814             "foo2",
815             r#"
816 //- /lib.rs
817 mod fo<|>o;
818 //- /foo/mod.rs
819 // emtpy
820 "#,
821             expect![[r#"
822                 RangeInfo {
823                     range: 4..7,
824                     info: SourceChange {
825                         source_file_edits: [
826                             SourceFileEdit {
827                                 file_id: FileId(
828                                     0,
829                                 ),
830                                 edit: TextEdit {
831                                     indels: [
832                                         Indel {
833                                             insert: "foo2",
834                                             delete: 4..7,
835                                         },
836                                     ],
837                                 },
838                             },
839                         ],
840                         file_system_edits: [
841                             MoveFile {
842                                 src: FileId(
843                                     1,
844                                 ),
845                                 anchor: FileId(
846                                     1,
847                                 ),
848                                 dst: "../foo2/mod.rs",
849                             },
850                         ],
851                         is_snippet: false,
852                     },
853                 }
854             "#]],
855         );
856     }
857
858     #[test]
859     fn test_rename_unusually_nested_mod() {
860         check_expect(
861             "bar",
862             r#"
863 //- /lib.rs
864 mod outer { mod fo<|>o; }
865
866 //- /outer/foo.rs
867 // emtpy
868 "#,
869             expect![[r#"
870                 RangeInfo {
871                     range: 16..19,
872                     info: SourceChange {
873                         source_file_edits: [
874                             SourceFileEdit {
875                                 file_id: FileId(
876                                     0,
877                                 ),
878                                 edit: TextEdit {
879                                     indels: [
880                                         Indel {
881                                             insert: "bar",
882                                             delete: 16..19,
883                                         },
884                                     ],
885                                 },
886                             },
887                         ],
888                         file_system_edits: [
889                             MoveFile {
890                                 src: FileId(
891                                     1,
892                                 ),
893                                 anchor: FileId(
894                                     1,
895                                 ),
896                                 dst: "bar.rs",
897                             },
898                         ],
899                         is_snippet: false,
900                     },
901                 }
902             "#]],
903         );
904     }
905
906     #[test]
907     fn test_module_rename_in_path() {
908         check(
909             "baz",
910             r#"
911 mod <|>foo { pub fn bar() {} }
912
913 fn main() { foo::bar(); }
914 "#,
915             r#"
916 mod baz { pub fn bar() {} }
917
918 fn main() { baz::bar(); }
919 "#,
920         );
921     }
922
923     #[test]
924     fn test_rename_mod_filename_and_path() {
925         check_expect(
926             "foo2",
927             r#"
928 //- /lib.rs
929 mod bar;
930 fn f() {
931     bar::foo::fun()
932 }
933
934 //- /bar.rs
935 pub mod foo<|>;
936
937 //- /bar/foo.rs
938 // pub fn fun() {}
939 "#,
940             expect![[r#"
941                 RangeInfo {
942                     range: 8..11,
943                     info: SourceChange {
944                         source_file_edits: [
945                             SourceFileEdit {
946                                 file_id: FileId(
947                                     1,
948                                 ),
949                                 edit: TextEdit {
950                                     indels: [
951                                         Indel {
952                                             insert: "foo2",
953                                             delete: 8..11,
954                                         },
955                                     ],
956                                 },
957                             },
958                             SourceFileEdit {
959                                 file_id: FileId(
960                                     0,
961                                 ),
962                                 edit: TextEdit {
963                                     indels: [
964                                         Indel {
965                                             insert: "foo2",
966                                             delete: 27..30,
967                                         },
968                                     ],
969                                 },
970                             },
971                         ],
972                         file_system_edits: [
973                             MoveFile {
974                                 src: FileId(
975                                     2,
976                                 ),
977                                 anchor: FileId(
978                                     2,
979                                 ),
980                                 dst: "foo2.rs",
981                             },
982                         ],
983                         is_snippet: false,
984                     },
985                 }
986             "#]],
987         );
988     }
989
990     #[test]
991     fn test_enum_variant_from_module_1() {
992         check(
993             "Baz",
994             r#"
995 mod foo {
996     pub enum Foo { Bar<|> }
997 }
998
999 fn func(f: foo::Foo) {
1000     match f {
1001         foo::Foo::Bar => {}
1002     }
1003 }
1004 "#,
1005             r#"
1006 mod foo {
1007     pub enum Foo { Baz }
1008 }
1009
1010 fn func(f: foo::Foo) {
1011     match f {
1012         foo::Foo::Baz => {}
1013     }
1014 }
1015 "#,
1016         );
1017     }
1018
1019     #[test]
1020     fn test_enum_variant_from_module_2() {
1021         check(
1022             "baz",
1023             r#"
1024 mod foo {
1025     pub struct Foo { pub bar<|>: uint }
1026 }
1027
1028 fn foo(f: foo::Foo) {
1029     let _ = f.bar;
1030 }
1031 "#,
1032             r#"
1033 mod foo {
1034     pub struct Foo { pub baz: uint }
1035 }
1036
1037 fn foo(f: foo::Foo) {
1038     let _ = f.baz;
1039 }
1040 "#,
1041         );
1042     }
1043
1044     #[test]
1045     fn test_parameter_to_self() {
1046         check(
1047             "self",
1048             r#"
1049 struct Foo { i: i32 }
1050
1051 impl Foo {
1052     fn f(foo<|>: &mut Foo) -> i32 {
1053         foo.i
1054     }
1055 }
1056 "#,
1057             r#"
1058 struct Foo { i: i32 }
1059
1060 impl Foo {
1061     fn f(&mut self) -> i32 {
1062         self.i
1063     }
1064 }
1065 "#,
1066         );
1067     }
1068
1069     #[test]
1070     fn test_self_to_parameter() {
1071         check(
1072             "foo",
1073             r#"
1074 struct Foo { i: i32 }
1075
1076 impl Foo {
1077     fn f(&mut <|>self) -> i32 {
1078         self.i
1079     }
1080 }
1081 "#,
1082             r#"
1083 struct Foo { i: i32 }
1084
1085 impl Foo {
1086     fn f(foo: &mut Foo) -> i32 {
1087         foo.i
1088     }
1089 }
1090 "#,
1091         );
1092     }
1093
1094     #[test]
1095     fn test_self_in_path_to_parameter() {
1096         check(
1097             "foo",
1098             r#"
1099 struct Foo { i: i32 }
1100
1101 impl Foo {
1102     fn f(&self) -> i32 {
1103         let self_var = 1;
1104         self<|>.i
1105     }
1106 }
1107 "#,
1108             r#"
1109 struct Foo { i: i32 }
1110
1111 impl Foo {
1112     fn f(foo: &Foo) -> i32 {
1113         let self_var = 1;
1114         foo.i
1115     }
1116 }
1117 "#,
1118         );
1119     }
1120
1121     #[test]
1122     fn test_initializer_use_field_init_shorthand() {
1123         check(
1124             "bar",
1125             r#"
1126 struct Foo { i<|>: i32 }
1127
1128 fn foo(bar: i32) -> Foo {
1129     Foo { i: bar }
1130 }
1131 "#,
1132             r#"
1133 struct Foo { bar: i32 }
1134
1135 fn foo(bar: i32) -> Foo {
1136     Foo { bar }
1137 }
1138 "#,
1139         );
1140     }
1141
1142     #[test]
1143     fn test_rename_binding_in_destructure_pat_shorthand() {
1144         check(
1145             "bar",
1146             r#"
1147 struct Foo {
1148     i: i32,
1149 }
1150
1151 fn foo(foo: Foo) {
1152     let Foo { i } = foo;
1153     let _ = i<|>;
1154 }
1155 "#,
1156             r#"
1157 struct Foo {
1158     i: i32,
1159 }
1160
1161 fn foo(foo: Foo) {
1162     let Foo { i: bar } = foo;
1163     let _ = bar;
1164 }
1165 "#,
1166         );
1167     }
1168
1169     #[test]
1170     fn test_rename_binding_in_destructure_pat() {
1171         check(
1172             "bar",
1173             r#"
1174 struct Foo {
1175     i: i32,
1176 }
1177
1178 fn foo(foo: Foo) {
1179     let Foo { i: b } = foo;
1180     let _ = b<|>;
1181 }
1182 "#,
1183             r#"
1184 struct Foo {
1185     i: i32,
1186 }
1187
1188 fn foo(foo: Foo) {
1189     let Foo { i: bar } = foo;
1190     let _ = bar;
1191 }
1192 "#,
1193         );
1194     }
1195 }