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