]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/extract_variable.rs
b498d86fb461ab3cde9f05d98d3df582fe1ca977
[rust.git] / crates / ide_assists / src / handlers / extract_variable.rs
1 use stdx::format_to;
2 use syntax::{
3     ast::{self, AstNode},
4     NodeOrToken,
5     SyntaxKind::{
6         BLOCK_EXPR, BREAK_EXPR, CLOSURE_EXPR, COMMENT, LOOP_EXPR, MATCH_ARM, MATCH_GUARD,
7         PATH_EXPR, RETURN_EXPR,
8     },
9     SyntaxNode,
10 };
11
12 use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
13
14 // Assist: extract_variable
15 //
16 // Extracts subexpression into a variable.
17 //
18 // ```
19 // fn main() {
20 //     $0(1 + 2)$0 * 4;
21 // }
22 // ```
23 // ->
24 // ```
25 // fn main() {
26 //     let $0var_name = (1 + 2);
27 //     var_name * 4;
28 // }
29 // ```
30 pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
31     if ctx.has_empty_selection() {
32         return None;
33     }
34
35     let node = match ctx.covering_element() {
36         NodeOrToken::Node(it) => it,
37         NodeOrToken::Token(it) if it.kind() == COMMENT => {
38             cov_mark::hit!(extract_var_in_comment_is_not_applicable);
39             return None;
40         }
41         NodeOrToken::Token(it) => it.parent()?,
42     };
43     let node = node.ancestors().take_while(|anc| anc.text_range() == node.text_range()).last()?;
44     let to_extract = node
45         .descendants()
46         .take_while(|it| ctx.selection_trimmed().contains_range(it.text_range()))
47         .find_map(valid_target_expr)?;
48
49     if let Some(ty_info) = ctx.sema.type_of_expr(&to_extract) {
50         if ty_info.adjusted().is_unit() {
51             return None;
52         }
53     }
54
55     let ref_kind: RefKind = if let Some(receiver_type) = get_receiver_type(&ctx, &to_extract) {
56         if receiver_type.is_mutable_reference() {
57             RefKind::MutRef
58         } else if receiver_type.is_reference() {
59             RefKind::Ref
60         } else {
61             RefKind::None
62         }
63     } else {
64         RefKind::None
65     };
66
67     let anchor = Anchor::from(&to_extract)?;
68     let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone();
69     let target = to_extract.syntax().text_range();
70     acc.add(
71         AssistId("extract_variable", AssistKind::RefactorExtract),
72         "Extract into variable",
73         target,
74         move |edit| {
75             let field_shorthand =
76                 match to_extract.syntax().parent().and_then(ast::RecordExprField::cast) {
77                     Some(field) => field.name_ref(),
78                     None => None,
79                 };
80
81             let mut buf = String::new();
82
83             let var_name = match &field_shorthand {
84                 Some(it) => it.to_string(),
85                 None => suggest_name::for_variable(&to_extract, &ctx.sema),
86             };
87             let expr_range = match &field_shorthand {
88                 Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()),
89                 None => to_extract.syntax().text_range(),
90             };
91
92             let reference_modifier = match ref_kind {
93                 RefKind::MutRef => "&mut ",
94                 RefKind::Ref => "&",
95                 RefKind::None => "",
96             };
97
98             match anchor {
99                 Anchor::Before(_) | Anchor::Replace(_) => {
100                     format_to!(buf, "let {} = {}", var_name, reference_modifier)
101                 }
102                 Anchor::WrapInBlock(_) => {
103                     format_to!(buf, "{{ let {} = {}", var_name, reference_modifier)
104                 }
105             };
106             format_to!(buf, "{}", to_extract.syntax());
107
108             if let Anchor::Replace(stmt) = anchor {
109                 cov_mark::hit!(test_extract_var_expr_stmt);
110                 if stmt.semicolon_token().is_none() {
111                     buf.push(';');
112                 }
113                 match ctx.config.snippet_cap {
114                     Some(cap) => {
115                         let snip = buf
116                             .replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
117                         edit.replace_snippet(cap, expr_range, snip)
118                     }
119                     None => edit.replace(expr_range, buf),
120                 }
121                 return;
122             }
123
124             buf.push(';');
125
126             // We want to maintain the indent level,
127             // but we do not want to duplicate possible
128             // extra newlines in the indent block
129             let text = indent.text();
130             if text.starts_with('\n') {
131                 buf.push('\n');
132                 buf.push_str(text.trim_start_matches('\n'));
133             } else {
134                 buf.push_str(text);
135             }
136
137             edit.replace(expr_range, var_name.clone());
138             let offset = anchor.syntax().text_range().start();
139             match ctx.config.snippet_cap {
140                 Some(cap) => {
141                     let snip =
142                         buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
143                     edit.insert_snippet(cap, offset, snip)
144                 }
145                 None => edit.insert(offset, buf),
146             }
147
148             if let Anchor::WrapInBlock(_) = anchor {
149                 edit.insert(anchor.syntax().text_range().end(), " }");
150             }
151         },
152     )
153 }
154
155 /// Check whether the node is a valid expression which can be extracted to a variable.
156 /// In general that's true for any expression, but in some cases that would produce invalid code.
157 fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
158     match node.kind() {
159         PATH_EXPR | LOOP_EXPR => None,
160         BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()),
161         RETURN_EXPR => ast::ReturnExpr::cast(node).and_then(|e| e.expr()),
162         BLOCK_EXPR => {
163             ast::BlockExpr::cast(node).filter(|it| it.is_standalone()).map(ast::Expr::from)
164         }
165         _ => ast::Expr::cast(node),
166     }
167 }
168
169 fn get_receiver_type(ctx: &AssistContext, expression: &ast::Expr) -> Option<hir::Type> {
170     let receiver = get_receiver(expression.to_owned())?;
171     Some(ctx.sema.type_of_expr(&receiver)?.original())
172 }
173
174 fn get_receiver(expression: ast::Expr) -> Option<ast::Expr> {
175     match expression {
176         ast::Expr::FieldExpr(field) if field.expr().is_some() => {
177             let nested_expression = &field.expr()?;
178             get_receiver(nested_expression.to_owned())
179         }
180         ast::Expr::PathExpr(_) => Some(expression),
181         _ => None,
182     }
183 }
184
185 #[derive(Debug)]
186 enum RefKind {
187     Ref,
188     MutRef,
189     None,
190 }
191
192 #[derive(Debug)]
193 enum Anchor {
194     Before(SyntaxNode),
195     Replace(ast::ExprStmt),
196     WrapInBlock(SyntaxNode),
197 }
198
199 impl Anchor {
200     fn from(to_extract: &ast::Expr) -> Option<Anchor> {
201         to_extract
202             .syntax()
203             .ancestors()
204             .take_while(|it| !ast::Item::can_cast(it.kind()) || ast::MacroCall::can_cast(it.kind()))
205             .find_map(|node| {
206                 if ast::MacroCall::can_cast(node.kind()) {
207                     return None;
208                 }
209                 if let Some(expr) =
210                     node.parent().and_then(ast::StmtList::cast).and_then(|it| it.tail_expr())
211                 {
212                     if expr.syntax() == &node {
213                         cov_mark::hit!(test_extract_var_last_expr);
214                         return Some(Anchor::Before(node));
215                     }
216                 }
217
218                 if let Some(parent) = node.parent() {
219                     if parent.kind() == CLOSURE_EXPR {
220                         cov_mark::hit!(test_extract_var_in_closure_no_block);
221                         return Some(Anchor::WrapInBlock(node));
222                     }
223                     if parent.kind() == MATCH_ARM {
224                         if node.kind() == MATCH_GUARD {
225                             cov_mark::hit!(test_extract_var_in_match_guard);
226                         } else {
227                             cov_mark::hit!(test_extract_var_in_match_arm_no_block);
228                             return Some(Anchor::WrapInBlock(node));
229                         }
230                     }
231                 }
232
233                 if let Some(stmt) = ast::Stmt::cast(node.clone()) {
234                     if let ast::Stmt::ExprStmt(stmt) = stmt {
235                         if stmt.expr().as_ref() == Some(to_extract) {
236                             return Some(Anchor::Replace(stmt));
237                         }
238                     }
239                     return Some(Anchor::Before(node));
240                 }
241                 None
242             })
243     }
244
245     fn syntax(&self) -> &SyntaxNode {
246         match self {
247             Anchor::Before(it) | Anchor::WrapInBlock(it) => it,
248             Anchor::Replace(stmt) => stmt.syntax(),
249         }
250     }
251 }
252
253 #[cfg(test)]
254 mod tests {
255     use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
256
257     use super::*;
258
259     #[test]
260     fn test_extract_var_simple() {
261         check_assist(
262             extract_variable,
263             r#"
264 fn foo() {
265     foo($01 + 1$0);
266 }"#,
267             r#"
268 fn foo() {
269     let $0var_name = 1 + 1;
270     foo(var_name);
271 }"#,
272         );
273     }
274
275     #[test]
276     fn extract_var_in_comment_is_not_applicable() {
277         cov_mark::check!(extract_var_in_comment_is_not_applicable);
278         check_assist_not_applicable(extract_variable, "fn main() { 1 + /* $0comment$0 */ 1; }");
279     }
280
281     #[test]
282     fn test_extract_var_expr_stmt() {
283         cov_mark::check!(test_extract_var_expr_stmt);
284         check_assist(
285             extract_variable,
286             r#"
287 fn foo() {
288   $0  1 + 1$0;
289 }"#,
290             r#"
291 fn foo() {
292     let $0var_name = 1 + 1;
293 }"#,
294         );
295         check_assist(
296             extract_variable,
297             r"
298 fn foo() {
299     $0{ let x = 0; x }$0
300     something_else();
301 }",
302             r"
303 fn foo() {
304     let $0var_name = { let x = 0; x };
305     something_else();
306 }",
307         );
308     }
309
310     #[test]
311     fn test_extract_var_part_of_expr_stmt() {
312         check_assist(
313             extract_variable,
314             r"
315 fn foo() {
316     $01$0 + 1;
317 }",
318             r"
319 fn foo() {
320     let $0var_name = 1;
321     var_name + 1;
322 }",
323         );
324     }
325
326     #[test]
327     fn test_extract_var_last_expr() {
328         cov_mark::check!(test_extract_var_last_expr);
329         check_assist(
330             extract_variable,
331             r#"
332 fn foo() {
333     bar($01 + 1$0)
334 }
335 "#,
336             r#"
337 fn foo() {
338     let $0var_name = 1 + 1;
339     bar(var_name)
340 }
341 "#,
342         );
343         check_assist(
344             extract_variable,
345             r#"
346 fn foo() -> i32 {
347     $0bar(1 + 1)$0
348 }
349
350 fn bar(i: i32) -> i32 {
351     i
352 }
353 "#,
354             r#"
355 fn foo() -> i32 {
356     let $0bar = bar(1 + 1);
357     bar
358 }
359
360 fn bar(i: i32) -> i32 {
361     i
362 }
363 "#,
364         )
365     }
366
367     #[test]
368     fn test_extract_var_in_match_arm_no_block() {
369         cov_mark::check!(test_extract_var_in_match_arm_no_block);
370         check_assist(
371             extract_variable,
372             r#"
373 fn main() {
374     let x = true;
375     let tuple = match x {
376         true => ($02 + 2$0, true)
377         _ => (0, false)
378     };
379 }
380 "#,
381             r#"
382 fn main() {
383     let x = true;
384     let tuple = match x {
385         true => { let $0var_name = 2 + 2; (var_name, true) }
386         _ => (0, false)
387     };
388 }
389 "#,
390         );
391     }
392
393     #[test]
394     fn test_extract_var_in_match_arm_with_block() {
395         check_assist(
396             extract_variable,
397             r#"
398 fn main() {
399     let x = true;
400     let tuple = match x {
401         true => {
402             let y = 1;
403             ($02 + y$0, true)
404         }
405         _ => (0, false)
406     };
407 }
408 "#,
409             r#"
410 fn main() {
411     let x = true;
412     let tuple = match x {
413         true => {
414             let y = 1;
415             let $0var_name = 2 + y;
416             (var_name, true)
417         }
418         _ => (0, false)
419     };
420 }
421 "#,
422         );
423     }
424
425     #[test]
426     fn test_extract_var_in_match_guard() {
427         cov_mark::check!(test_extract_var_in_match_guard);
428         check_assist(
429             extract_variable,
430             r#"
431 fn main() {
432     match () {
433         () if $010 > 0$0 => 1
434         _ => 2
435     };
436 }
437 "#,
438             r#"
439 fn main() {
440     let $0var_name = 10 > 0;
441     match () {
442         () if var_name => 1
443         _ => 2
444     };
445 }
446 "#,
447         );
448     }
449
450     #[test]
451     fn test_extract_var_in_closure_no_block() {
452         cov_mark::check!(test_extract_var_in_closure_no_block);
453         check_assist(
454             extract_variable,
455             r#"
456 fn main() {
457     let lambda = |x: u32| $0x * 2$0;
458 }
459 "#,
460             r#"
461 fn main() {
462     let lambda = |x: u32| { let $0var_name = x * 2; var_name };
463 }
464 "#,
465         );
466     }
467
468     #[test]
469     fn test_extract_var_in_closure_with_block() {
470         check_assist(
471             extract_variable,
472             r#"
473 fn main() {
474     let lambda = |x: u32| { $0x * 2$0 };
475 }
476 "#,
477             r#"
478 fn main() {
479     let lambda = |x: u32| { let $0var_name = x * 2; var_name };
480 }
481 "#,
482         );
483     }
484
485     #[test]
486     fn test_extract_var_path_simple() {
487         check_assist(
488             extract_variable,
489             "
490 fn main() {
491     let o = $0Some(true)$0;
492 }
493 ",
494             "
495 fn main() {
496     let $0var_name = Some(true);
497     let o = var_name;
498 }
499 ",
500         );
501     }
502
503     #[test]
504     fn test_extract_var_path_method() {
505         check_assist(
506             extract_variable,
507             "
508 fn main() {
509     let v = $0bar.foo()$0;
510 }
511 ",
512             "
513 fn main() {
514     let $0foo = bar.foo();
515     let v = foo;
516 }
517 ",
518         );
519     }
520
521     #[test]
522     fn test_extract_var_return() {
523         check_assist(
524             extract_variable,
525             "
526 fn foo() -> u32 {
527     $0return 2 + 2$0;
528 }
529 ",
530             "
531 fn foo() -> u32 {
532     let $0var_name = 2 + 2;
533     return var_name;
534 }
535 ",
536         );
537     }
538
539     #[test]
540     fn test_extract_var_does_not_add_extra_whitespace() {
541         check_assist(
542             extract_variable,
543             "
544 fn foo() -> u32 {
545
546
547     $0return 2 + 2$0;
548 }
549 ",
550             "
551 fn foo() -> u32 {
552
553
554     let $0var_name = 2 + 2;
555     return var_name;
556 }
557 ",
558         );
559
560         check_assist(
561             extract_variable,
562             "
563 fn foo() -> u32 {
564
565         $0return 2 + 2$0;
566 }
567 ",
568             "
569 fn foo() -> u32 {
570
571         let $0var_name = 2 + 2;
572         return var_name;
573 }
574 ",
575         );
576
577         check_assist(
578             extract_variable,
579             "
580 fn foo() -> u32 {
581     let foo = 1;
582
583     // bar
584
585
586     $0return 2 + 2$0;
587 }
588 ",
589             "
590 fn foo() -> u32 {
591     let foo = 1;
592
593     // bar
594
595
596     let $0var_name = 2 + 2;
597     return var_name;
598 }
599 ",
600         );
601     }
602
603     #[test]
604     fn test_extract_var_break() {
605         check_assist(
606             extract_variable,
607             "
608 fn main() {
609     let result = loop {
610         $0break 2 + 2$0;
611     };
612 }
613 ",
614             "
615 fn main() {
616     let result = loop {
617         let $0var_name = 2 + 2;
618         break var_name;
619     };
620 }
621 ",
622         );
623     }
624
625     #[test]
626     fn test_extract_var_for_cast() {
627         check_assist(
628             extract_variable,
629             "
630 fn main() {
631     let v = $00f32 as u32$0;
632 }
633 ",
634             "
635 fn main() {
636     let $0var_name = 0f32 as u32;
637     let v = var_name;
638 }
639 ",
640         );
641     }
642
643     #[test]
644     fn extract_var_field_shorthand() {
645         check_assist(
646             extract_variable,
647             r#"
648 struct S {
649     foo: i32
650 }
651
652 fn main() {
653     S { foo: $01 + 1$0 }
654 }
655 "#,
656             r#"
657 struct S {
658     foo: i32
659 }
660
661 fn main() {
662     let $0foo = 1 + 1;
663     S { foo }
664 }
665 "#,
666         )
667     }
668
669     #[test]
670     fn extract_var_name_from_type() {
671         check_assist(
672             extract_variable,
673             r#"
674 struct Test(i32);
675
676 fn foo() -> Test {
677     $0{ Test(10) }$0
678 }
679 "#,
680             r#"
681 struct Test(i32);
682
683 fn foo() -> Test {
684     let $0test = { Test(10) };
685     test
686 }
687 "#,
688         )
689     }
690
691     #[test]
692     fn extract_var_name_from_parameter() {
693         check_assist(
694             extract_variable,
695             r#"
696 fn bar(test: u32, size: u32)
697
698 fn foo() {
699     bar(1, $01+1$0);
700 }
701 "#,
702             r#"
703 fn bar(test: u32, size: u32)
704
705 fn foo() {
706     let $0size = 1+1;
707     bar(1, size);
708 }
709 "#,
710         )
711     }
712
713     #[test]
714     fn extract_var_parameter_name_has_precedence_over_type() {
715         check_assist(
716             extract_variable,
717             r#"
718 struct TextSize(u32);
719 fn bar(test: u32, size: TextSize)
720
721 fn foo() {
722     bar(1, $0{ TextSize(1+1) }$0);
723 }
724 "#,
725             r#"
726 struct TextSize(u32);
727 fn bar(test: u32, size: TextSize)
728
729 fn foo() {
730     let $0size = { TextSize(1+1) };
731     bar(1, size);
732 }
733 "#,
734         )
735     }
736
737     #[test]
738     fn extract_var_name_from_function() {
739         check_assist(
740             extract_variable,
741             r#"
742 fn is_required(test: u32, size: u32) -> bool
743
744 fn foo() -> bool {
745     $0is_required(1, 2)$0
746 }
747 "#,
748             r#"
749 fn is_required(test: u32, size: u32) -> bool
750
751 fn foo() -> bool {
752     let $0is_required = is_required(1, 2);
753     is_required
754 }
755 "#,
756         )
757     }
758
759     #[test]
760     fn extract_var_name_from_method() {
761         check_assist(
762             extract_variable,
763             r#"
764 struct S;
765 impl S {
766     fn bar(&self, n: u32) -> u32 { n }
767 }
768
769 fn foo() -> u32 {
770     $0S.bar(1)$0
771 }
772 "#,
773             r#"
774 struct S;
775 impl S {
776     fn bar(&self, n: u32) -> u32 { n }
777 }
778
779 fn foo() -> u32 {
780     let $0bar = S.bar(1);
781     bar
782 }
783 "#,
784         )
785     }
786
787     #[test]
788     fn extract_var_name_from_method_param() {
789         check_assist(
790             extract_variable,
791             r#"
792 struct S;
793 impl S {
794     fn bar(&self, n: u32, size: u32) { n }
795 }
796
797 fn foo() {
798     S.bar($01 + 1$0, 2)
799 }
800 "#,
801             r#"
802 struct S;
803 impl S {
804     fn bar(&self, n: u32, size: u32) { n }
805 }
806
807 fn foo() {
808     let $0n = 1 + 1;
809     S.bar(n, 2)
810 }
811 "#,
812         )
813     }
814
815     #[test]
816     fn extract_var_name_from_ufcs_method_param() {
817         check_assist(
818             extract_variable,
819             r#"
820 struct S;
821 impl S {
822     fn bar(&self, n: u32, size: u32) { n }
823 }
824
825 fn foo() {
826     S::bar(&S, $01 + 1$0, 2)
827 }
828 "#,
829             r#"
830 struct S;
831 impl S {
832     fn bar(&self, n: u32, size: u32) { n }
833 }
834
835 fn foo() {
836     let $0n = 1 + 1;
837     S::bar(&S, n, 2)
838 }
839 "#,
840         )
841     }
842
843     #[test]
844     fn extract_var_parameter_name_has_precedence_over_function() {
845         check_assist(
846             extract_variable,
847             r#"
848 fn bar(test: u32, size: u32)
849
850 fn foo() {
851     bar(1, $0symbol_size(1, 2)$0);
852 }
853 "#,
854             r#"
855 fn bar(test: u32, size: u32)
856
857 fn foo() {
858     let $0size = symbol_size(1, 2);
859     bar(1, size);
860 }
861 "#,
862         )
863     }
864
865     #[test]
866     fn extract_macro_call() {
867         check_assist(
868             extract_variable,
869             r"
870 struct Vec;
871 macro_rules! vec {
872     () => {Vec}
873 }
874 fn main() {
875     let _ = $0vec![]$0;
876 }
877 ",
878             r"
879 struct Vec;
880 macro_rules! vec {
881     () => {Vec}
882 }
883 fn main() {
884     let $0vec = vec![];
885     let _ = vec;
886 }
887 ",
888         );
889     }
890
891     #[test]
892     fn test_extract_var_for_return_not_applicable() {
893         check_assist_not_applicable(extract_variable, "fn foo() { $0return$0; } ");
894     }
895
896     #[test]
897     fn test_extract_var_for_break_not_applicable() {
898         check_assist_not_applicable(extract_variable, "fn main() { loop { $0break$0; }; }");
899     }
900
901     #[test]
902     fn test_extract_var_unit_expr_not_applicable() {
903         check_assist_not_applicable(
904             extract_variable,
905             r#"
906 fn foo() {
907     let mut i = 3;
908     $0if i >= 0 {
909         i += 1;
910     } else {
911         i -= 1;
912     }$0
913 }"#,
914         );
915     }
916
917     // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic
918     #[test]
919     fn extract_var_target() {
920         check_assist_target(extract_variable, "fn foo() -> u32 { $0return 2 + 2$0; }", "2 + 2");
921
922         check_assist_target(
923             extract_variable,
924             "
925 fn main() {
926     let x = true;
927     let tuple = match x {
928         true => ($02 + 2$0, true)
929         _ => (0, false)
930     };
931 }
932 ",
933             "2 + 2",
934         );
935     }
936
937     #[test]
938     fn extract_var_no_block_body() {
939         check_assist_not_applicable(
940             extract_variable,
941             r"
942 const X: usize = $0100$0;
943 ",
944         );
945     }
946
947     #[test]
948     fn test_extract_var_mutable_reference_parameter() {
949         check_assist(
950             extract_variable,
951             r#"
952 struct S {
953     vec: Vec<u8>
954 }
955
956 fn foo(s: &mut S) {
957     $0s.vec$0.push(0);
958 }"#,
959             r#"
960 struct S {
961     vec: Vec<u8>
962 }
963
964 fn foo(s: &mut S) {
965     let $0var_name = &mut s.vec;
966     var_name.push(0);
967 }"#,
968         );
969     }
970
971     #[test]
972     fn test_extract_var_mutable_reference_parameter_deep_nesting() {
973         check_assist(
974             extract_variable,
975             r#"
976 struct Y {
977     field: X
978 }
979 struct X {
980     field: S
981 }
982 struct S {
983     vec: Vec<u8>
984 }
985
986 fn foo(f: &mut Y) {
987     $0f.field.field.vec$0.push(0);
988 }"#,
989             r#"
990 struct Y {
991     field: X
992 }
993 struct X {
994     field: S
995 }
996 struct S {
997     vec: Vec<u8>
998 }
999
1000 fn foo(f: &mut Y) {
1001     let $0var_name = &mut f.field.field.vec;
1002     var_name.push(0);
1003 }"#,
1004         );
1005     }
1006
1007     #[test]
1008     fn test_extract_var_reference_parameter() {
1009         check_assist(
1010             extract_variable,
1011             r#"
1012 struct X;
1013
1014 impl X {
1015     fn do_thing(&self) {
1016
1017     }
1018 }
1019
1020 struct S {
1021     sub: X
1022 }
1023
1024 fn foo(s: &S) {
1025     $0s.sub$0.do_thing();
1026 }"#,
1027             r#"
1028 struct X;
1029
1030 impl X {
1031     fn do_thing(&self) {
1032
1033     }
1034 }
1035
1036 struct S {
1037     sub: X
1038 }
1039
1040 fn foo(s: &S) {
1041     let $0x = &s.sub;
1042     x.do_thing();
1043 }"#,
1044         );
1045     }
1046
1047     #[test]
1048     fn test_extract_var_reference_parameter_deep_nesting() {
1049         check_assist(
1050             extract_variable,
1051             r#"
1052 struct Z;
1053 impl Z {
1054     fn do_thing(&self) {
1055
1056     }
1057 }
1058
1059 struct Y {
1060     field: Z
1061 }
1062
1063 struct X {
1064     field: Y
1065 }
1066
1067 struct S {
1068     sub: X
1069 }
1070
1071 fn foo(s: &S) {
1072     $0s.sub.field.field$0.do_thing();
1073 }"#,
1074             r#"
1075 struct Z;
1076 impl Z {
1077     fn do_thing(&self) {
1078
1079     }
1080 }
1081
1082 struct Y {
1083     field: Z
1084 }
1085
1086 struct X {
1087     field: Y
1088 }
1089
1090 struct S {
1091     sub: X
1092 }
1093
1094 fn foo(s: &S) {
1095     let $0z = &s.sub.field.field;
1096     z.do_thing();
1097 }"#,
1098         );
1099     }
1100
1101     #[test]
1102     fn test_extract_var_regular_parameter() {
1103         check_assist(
1104             extract_variable,
1105             r#"
1106 struct X;
1107
1108 impl X {
1109     fn do_thing(&self) {
1110
1111     }
1112 }
1113
1114 struct S {
1115     sub: X
1116 }
1117
1118 fn foo(s: S) {
1119     $0s.sub$0.do_thing();
1120 }"#,
1121             r#"
1122 struct X;
1123
1124 impl X {
1125     fn do_thing(&self) {
1126
1127     }
1128 }
1129
1130 struct S {
1131     sub: X
1132 }
1133
1134 fn foo(s: S) {
1135     let $0x = s.sub;
1136     x.do_thing();
1137 }"#,
1138         );
1139     }
1140
1141     #[test]
1142     fn test_extract_var_mutable_reference_local() {
1143         check_assist(
1144             extract_variable,
1145             r#"
1146 struct X;
1147
1148 struct S {
1149     sub: X
1150 }
1151
1152 impl S {
1153     fn new() -> S {
1154         S { 
1155             sub: X::new()
1156         }
1157     }
1158 }
1159
1160 impl X {
1161     fn new() -> X {
1162         X { }
1163     }
1164     fn do_thing(&self) {
1165
1166     }
1167 }
1168
1169
1170 fn foo() {
1171     let local = &mut S::new();
1172     $0local.sub$0.do_thing();
1173 }"#,
1174             r#"
1175 struct X;
1176
1177 struct S {
1178     sub: X
1179 }
1180
1181 impl S {
1182     fn new() -> S {
1183         S { 
1184             sub: X::new()
1185         }
1186     }
1187 }
1188
1189 impl X {
1190     fn new() -> X {
1191         X { }
1192     }
1193     fn do_thing(&self) {
1194
1195     }
1196 }
1197
1198
1199 fn foo() {
1200     let local = &mut S::new();
1201     let $0x = &mut local.sub;
1202     x.do_thing();
1203 }"#,
1204         );
1205     }
1206
1207     #[test]
1208     fn test_extract_var_reference_local() {
1209         check_assist(
1210             extract_variable,
1211             r#"
1212 struct X;
1213
1214 struct S {
1215     sub: X
1216 }
1217
1218 impl S {
1219     fn new() -> S {
1220         S { 
1221             sub: X::new()
1222         }
1223     }
1224 }
1225
1226 impl X {
1227     fn new() -> X {
1228         X { }
1229     }
1230     fn do_thing(&self) {
1231
1232     }
1233 }
1234
1235
1236 fn foo() {
1237     let local = &S::new();
1238     $0local.sub$0.do_thing();
1239 }"#,
1240             r#"
1241 struct X;
1242
1243 struct S {
1244     sub: X
1245 }
1246
1247 impl S {
1248     fn new() -> S {
1249         S { 
1250             sub: X::new()
1251         }
1252     }
1253 }
1254
1255 impl X {
1256     fn new() -> X {
1257         X { }
1258     }
1259     fn do_thing(&self) {
1260
1261     }
1262 }
1263
1264
1265 fn foo() {
1266     let local = &S::new();
1267     let $0x = &local.sub;
1268     x.do_thing();
1269 }"#,
1270         );
1271     }
1272 }