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