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