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