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