]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/destructure_tuple_binding.rs
Handle tuple in macro call
[rust.git] / crates / ide_assists / src / handlers / destructure_tuple_binding.rs
1 use ide_db::{
2     assists::{AssistId, AssistKind},
3     defs::Definition,
4     search::{FileReference, SearchScope, UsageSearchResult},
5 };
6 use itertools::Itertools;
7 use syntax::{TextRange, ast::{self, AstNode, IdentPat, NameOwner}};
8
9 use crate::assist_context::{AssistBuilder, AssistContext, Assists};
10
11 // Assist: destructure_tuple_binding
12 //
13 // Destructures a tuple binding in place.
14 //
15 // ```
16 // fn main() {
17 //     let $0t = (1,2);
18 //     let v = t.0;
19 // }
20 // ```
21 // ->
22 // ```
23 // fn main() {
24 //     let (_0, _1) = (1,2);
25 //     let v = _0;
26 // }
27 // ```
28 //
29 //
30 // And (currently disabled):
31 // Assist: destructure_tuple_binding_in_sub_pattern
32 //
33 // Destructures tuple items in sub-pattern (after `@`).
34 //
35 // ```
36 // fn main() {
37 //     let $0t = (1,2);
38 //     let v = t.0;
39 // }
40 // ```
41 // ->
42 // ```
43 // fn main() {
44 //     let t @ (_0, _1) = (1,2);
45 //     let v = _0;
46 // }
47 // ```
48 pub(crate) fn destructure_tuple_binding(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
49     destructure_tuple_binding_impl(acc, ctx, false)
50 }
51
52 pub(crate) fn destructure_tuple_binding_impl(acc: &mut Assists, ctx: &AssistContext, with_sub_pattern: bool) -> Option<()> {
53     let ident_pat = ctx.find_node_at_offset::<ast::IdentPat>()?;
54     let data = collect_data(ident_pat, ctx)?;
55
56     acc.add(
57         AssistId("destructure_tuple", AssistKind::RefactorRewrite),
58         if with_sub_pattern { "Destructure tuple in place" } else { "Destructure tuple" },
59         data.range,
60         |builder| {
61             edit_tuple_assignment(&data, builder, ctx, false);
62             edit_tuple_usages(&data, builder, ctx, false);
63         },
64     );
65
66     if with_sub_pattern {
67         acc.add(
68             AssistId("destructure_tuple_in_sub_pattern", AssistKind::RefactorRewrite),
69             "Destructure tuple in sub-pattern",
70             data.range,
71             |builder| {
72                 edit_tuple_assignment(&data, builder, ctx, true);
73                 edit_tuple_usages(&data, builder, ctx, true);
74             },
75         );
76     }
77
78     Some(())
79 }
80
81 fn collect_data(ident_pat: IdentPat, ctx: &AssistContext) -> Option<TupleData> {
82     if ident_pat.at_token().is_some() {
83         // cannot destructure pattern with sub-pattern:
84         // Only IdentPat can have sub-pattern,
85         // but not TuplePat (`(a,b)`)
86         cov_mark::hit!(destructure_tuple_subpattern);
87         return None;
88     }
89
90     let ty = ctx.sema.type_of_pat(&ident_pat.clone().into())?;
91     // might be reference
92     let ty = ty.strip_references();
93     // must be tuple
94     let field_types = ty.tuple_fields(ctx.db());
95     if field_types.is_empty() {
96         cov_mark::hit!(destructure_tuple_no_tuple);
97         return None;
98     }
99
100     let name = ident_pat.name()?.to_string();
101     let range = ident_pat.syntax().text_range();
102
103     let usages = ctx.sema.to_def(&ident_pat).map(|def| {
104         Definition::Local(def)
105             .usages(&ctx.sema)
106             .in_scope(SearchScope::single_file(ctx.frange.file_id))
107             .all()
108     });
109
110     let field_names = (0..field_types.len())
111         .map(|i| generate_name(i, &name, &ident_pat, &usages, ctx))
112         .collect_vec();
113
114     Some(TupleData { ident_pat, range, field_names, usages })
115 }
116
117 fn generate_name(
118     index: usize,
119     _tuple_name: &str,
120     _ident_pat: &IdentPat,
121     _usages: &Option<UsageSearchResult>,
122     _ctx: &AssistContext,
123 ) -> String {
124     //TODO: detect if name already used
125     format!("_{}", index)
126 }
127
128 struct TupleData {
129     ident_pat: IdentPat,
130     // name: String,
131     range: TextRange,
132     field_names: Vec<String>,
133     // field_types: Vec<Type>,
134     usages: Option<UsageSearchResult>,
135 }
136 fn edit_tuple_assignment(data: &TupleData, builder: &mut AssistBuilder, ctx: &AssistContext, in_sub_pattern: bool) {
137     let tuple_pat = {
138         let original = &data.ident_pat;
139         let is_ref = original.ref_token().is_some();
140         let is_mut = original.mut_token().is_some();
141         let fields =
142             data
143             .field_names
144             .iter()
145             .map(|name| ast::Pat::from(ast::make::ident_pat(is_ref, is_mut, ast::make::name(name))));
146         ast::make::tuple_pat(fields)
147     };
148
149     let add_cursor = |text: &str| {
150         // place cursor on first tuple item
151         let first_tuple = &data.field_names[0];
152         text.replacen(first_tuple, &format!("$0{}", first_tuple), 1)
153     };
154
155     // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)`
156     if in_sub_pattern {
157         let text = format!(" @ {}", tuple_pat.to_string());
158         match ctx.config.snippet_cap {
159             Some(cap) => {
160                 let snip = add_cursor(&text);
161                 builder.insert_snippet(cap, data.range.end(), snip);
162             }
163             None => builder.insert(data.range.end(), text),
164         };
165     } else {
166         let text = tuple_pat.to_string();
167         match ctx.config.snippet_cap {
168             Some(cap) => {
169                 let snip = add_cursor(&text);
170                 builder.replace_snippet(cap, data.range, snip);
171             }
172             None => builder.replace(data.range, text),
173         };
174     }
175 }
176
177 fn edit_tuple_usages(
178     data: &TupleData,
179     builder: &mut AssistBuilder,
180     ctx: &AssistContext,
181     in_sub_pattern: bool,
182 ) {
183     if let Some(usages) = data.usages.as_ref() {
184         for (file_id, refs) in usages.iter() {
185             builder.edit_file(*file_id);
186
187             for r in refs {
188                 edit_tuple_usage(r, data, builder, ctx, in_sub_pattern);
189             }
190         }
191     }
192 }
193 fn edit_tuple_usage(
194     usage: &FileReference,
195     data: &TupleData,
196     builder: &mut AssistBuilder,
197     _ctx: &AssistContext,
198     in_sub_pattern: bool,
199 ) {
200     match detect_tuple_index(usage, data) {
201         Some(index) => {
202             let text = &data.field_names[index.index];
203             builder.replace(index.range, text);
204         }
205         None => {
206             if in_sub_pattern {
207                 cov_mark::hit!(destructure_tuple_call_with_subpattern);
208                 return;
209             }
210
211             // no index access -> make invalid -> requires handling by user
212             // -> put usage in block comment
213             builder.insert(usage.range.start(), "/*");
214             builder.insert(usage.range.end(), "*/");
215         }
216     }
217 }
218
219 struct TupleIndex {
220     index: usize,
221     range: TextRange,
222 }
223
224 fn detect_tuple_index(usage: &FileReference, data: &TupleData) -> Option<TupleIndex> {
225     // usage is IDENT
226     // IDENT
227     //  NAME_REF
228     //   PATH_SEGMENT
229     //    PATH
230     //     PATH_EXPR
231     //      PAREN_EXRP*
232     //       FIELD_EXPR
233
234     let node = 
235         usage.name.syntax()
236         .ancestors()
237         .skip_while(|s| !ast::PathExpr::can_cast(s.kind()))
238         .skip(1) // PATH_EXPR
239         .find(|s| !ast::ParenExpr::can_cast(s.kind()))?;    // skip parentheses
240
241     if let Some(field_expr) = ast::FieldExpr::cast(node) {
242         let idx = field_expr.name_ref()?.as_tuple_field()?;
243         if idx < data.field_names.len() {
244
245             // special case: in macro call -> range of `field_expr` in applied macro, NOT range in actual file!
246             if field_expr.syntax().ancestors().any(|a| ast::MacroStmts::can_cast(a.kind())) {
247                 cov_mark::hit!(destructure_tuple_macro_call);
248
249                 // issue: cannot differentiate between tuple index passed into macro or tuple index as result of macro:
250                 // ```rust
251                 // macro_rules! m {
252                 //     ($t1:expr, $t2:expr) => { $t1; $t2.0 }
253                 // }
254                 // let t = (1,2);
255                 // m!(t.0, t)
256                 // ```
257                 // -> 2 tuple index usages detected!
258                 //
259                 // -> only handle `t`
260                 return None;
261             }
262
263             Some(TupleIndex {
264                             index: idx,
265                             range: field_expr.syntax().text_range(),
266                         })
267         } else {
268             // tuple index out of range
269             None
270         }
271     } else {
272         None
273     }
274 }
275
276 #[cfg(test)]
277 mod tests {
278     use super::*;
279
280     use crate::tests::{check_assist, check_assist_not_applicable};
281
282     // Tests for direct tuple destructure: 
283     // `let $0t = (1,2);` -> `let (_0, _1) = (1,2);`
284
285     fn assist(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
286         destructure_tuple_binding_impl(acc, ctx, false)
287     }
288
289     #[test]
290     fn dont_trigger_on_unit() {
291         cov_mark::check!(destructure_tuple_no_tuple);
292         check_assist_not_applicable(
293             assist,
294             r#"
295 fn main() {
296 let $0v = ();
297 }
298             "#,
299         )
300     }
301     #[test]
302     fn dont_trigger_on_number() {
303         cov_mark::check!(destructure_tuple_no_tuple);
304         check_assist_not_applicable(
305             assist,
306             r#"
307 fn main() {
308 let $0v = 32;
309 }
310             "#,
311         )
312     }
313
314     #[test]
315     fn destructure_3_tuple() {
316         check_assist(
317             assist,
318             r#"
319 fn main() {
320     let $0tup = (1,2,3);
321 }
322             "#,
323             r#"
324 fn main() {
325     let ($0_0, _1, _2) = (1,2,3);
326 }
327             "#,
328         )
329     }
330     #[test]
331     fn destructure_2_tuple() {
332         check_assist(
333             assist,
334             r#"
335 fn main() {
336     let $0tup = (1,2);
337 }
338             "#,
339             r#"
340 fn main() {
341     let ($0_0, _1) = (1,2);
342 }
343             "#,
344         )
345     }
346     #[test]
347     fn replace_indices() {
348         check_assist(
349             assist,
350             r#"
351 fn main() {
352     let $0tup = (1,2,3);
353     let v1 = tup.0;
354     let v2 = tup.1;
355     let v3 = tup.2;
356 }
357             "#,
358             r#"
359 fn main() {
360     let ($0_0, _1, _2) = (1,2,3);
361     let v1 = _0;
362     let v2 = _1;
363     let v3 = _2;
364 }
365             "#,
366         )
367     }
368
369     #[test]
370     fn replace_usage_in_parentheses() {
371         check_assist(
372             assist,
373             r#"
374 fn main() {
375     let $0tup = (1,2,3);
376     let a = (tup).1;
377     let b = ((tup)).1;
378 }
379             "#,
380             r#"
381 fn main() {
382     let ($0_0, _1, _2) = (1,2,3);
383     let a = _1;
384     let b = _1;
385 }
386             "#,
387         )
388     }
389
390     #[test]
391     fn handle_function_call() {
392         check_assist(
393             assist,
394             r#"
395 fn main() {
396     let $0tup = (1,2);
397     let v = tup.into();
398 }
399             "#,
400             r#"
401 fn main() {
402     let ($0_0, _1) = (1,2);
403     let v = /*tup*/.into();
404 }
405             "#,
406         )
407     }
408
409     #[test]
410     fn handle_invalid_index() {
411         check_assist(
412             assist,
413             r#"
414 fn main() {
415     let $0tup = (1,2);
416     let v = tup.3;
417 }
418             "#,
419             r#"
420 fn main() {
421     let ($0_0, _1) = (1,2);
422     let v = /*tup*/.3;
423 }
424             "#,
425         )
426     }
427
428     #[test]
429     fn dont_replace_variable_with_same_name_as_tuple() {
430         check_assist(
431             assist,
432             r#"
433 fn main() {
434     let tup = (1,2);
435     let v = tup.1;
436     let $0tup = (1,2,3);
437     let v = tup.1;
438     let tup = (1,2,3);
439     let v = tup.1;
440 }
441             "#,
442             r#"
443 fn main() {
444     let tup = (1,2);
445     let v = tup.1;
446     let ($0_0, _1, _2) = (1,2,3);
447     let v = _1;
448     let tup = (1,2,3);
449     let v = tup.1;
450 }
451             "#,
452         )
453     }
454
455     #[test]
456     fn keep_function_call_in_tuple_item() {
457         check_assist(
458             assist,
459             r#"
460 fn main() {
461     let $0t = ("3.14", 0);
462     let pi: f32 = t.0.parse().unwrap();
463 }
464             "#,
465             r#"
466 fn main() {
467     let ($0_0, _1) = ("3.14", 0);
468     let pi: f32 = _0.parse().unwrap();
469 }
470             "#,
471         )
472     }
473
474     #[test]
475     fn keep_type() {
476         check_assist(
477             assist,
478             r#"
479 fn main() {
480     let $0t: (usize, i32) = (1,2);
481 }
482             "#,
483             r#"
484 fn main() {
485     let ($0_0, _1): (usize, i32) = (1,2);
486 }
487             "#,
488         )
489     }
490
491     #[test]
492     fn destructure_reference() {
493         //Note: `v` has different types:
494         // * in 1st: `i32`
495         // * in 2nd: `&i32`
496         check_assist(
497             assist,
498             r#"
499 fn main() {
500     let t = (1,2);
501     let $0t = &t;
502     let v = t.0;
503 }
504             "#,
505             r#"
506 fn main() {
507     let t = (1,2);
508     let ($0_0, _1) = &t;
509     let v = _0;
510 }
511             "#,
512         )
513     }
514
515     #[test]
516     fn destructure_multiple_reference() {
517         check_assist(
518             assist,
519             r#"
520 fn main() {
521     let t = (1,2);
522     let $0t = &&t;
523     let v = t.0;
524 }
525             "#,
526             r#"
527 fn main() {
528     let t = (1,2);
529     let ($0_0, _1) = &&t;
530     let v = _0;
531 }
532             "#,
533         )
534     }
535
536     #[test]
537     fn keep_reference() {
538         check_assist(
539             assist,
540             r#"
541 fn foo(t: &(usize, usize)) -> usize {
542     match t {
543         &$0t => t.0
544     }
545 }
546             "#,
547             r#"
548 fn foo(t: &(usize, usize)) -> usize {
549     match t {
550         &($0_0, _1) => _0
551     }
552 }
553             "#,
554         )
555     }
556
557     #[test]
558     fn with_ref() {
559         //Note: `v` has different types:
560         // * in 1st: `i32`
561         // * in 2nd: `&i32`
562         check_assist(
563             assist,
564             r#"
565 fn main() {
566     let ref $0t = (1,2);
567     let v = t.0;
568 }
569             "#,
570             r#"
571 fn main() {
572     let (ref $0_0, ref _1) = (1,2);
573     let v = _0;
574 }
575             "#,
576         )
577     }
578
579     #[test]
580     fn with_mut() {
581         check_assist(
582             assist,
583             r#"
584 fn main() {
585     let mut $0t = (1,2);
586     t.0 = 42;
587     let v = t.0;
588 }
589             "#,
590             r#"
591 fn main() {
592     let (mut $0_0, mut _1) = (1,2);
593     _0 = 42;
594     let v = _0;
595 }
596             "#,
597         )
598     }
599
600     #[test]
601     fn with_ref_mut() {
602         //Note: `v` has different types:
603         // * in 1st: `i32`
604         // * in 2nd: `&mut i32`
605         // Note: 2nd `_0 = 42` isn't valid; requires dereferencing (`*_0`), but isn't handled here!
606         check_assist(
607             assist,
608             r#"
609 fn main() {
610     let ref mut $0t = (1,2);
611     t.0 = 42;
612     let v = t.0;
613 }
614             "#,
615             r#"
616 fn main() {
617     let (ref mut $0_0, ref mut _1) = (1,2);
618     _0 = 42;
619     let v = _0;
620 }
621             "#,
622         )
623     }
624
625     #[test]
626     fn dont_trigger_for_non_tuple_reference() {
627         check_assist_not_applicable(
628             assist,
629             r#"
630 fn main() {
631     let v = 42;
632     let $0v = &42;
633 }
634             "#,
635         )
636     }
637
638     #[test]
639     fn dont_trigger_on_static_tuple() {
640         check_assist_not_applicable(
641             assist,
642             r#"
643 static $0TUP: (usize, usize) = (1,2);
644             "#,
645         )
646     }
647
648     #[test]
649     fn dont_trigger_on_wildcard() {
650         check_assist_not_applicable(
651             assist,
652             r#"
653 fn main() {
654     let $0_ = (1,2);
655 }
656             "#,
657         )
658     }
659
660     #[test]
661     fn dont_trigger_in_struct() {
662         check_assist_not_applicable(
663             assist,
664             r#"
665 struct S {
666     $0tup: (usize, usize),
667 }
668             "#,
669         )
670     }
671
672     #[test]
673     fn dont_trigger_in_struct_creation() {
674         check_assist_not_applicable(
675             assist,
676             r#"
677 struct S {
678     tup: (usize, usize),
679 }
680 fn main() {
681     let s = S {
682         $0tup: (1,2),
683     };
684 }
685             "#,
686         )
687     }
688
689     #[test]
690     fn dont_trigger_on_tuple_struct() {
691         check_assist_not_applicable(
692             assist,
693             r#"
694 struct S(usize, usize);
695 fn main() {
696     let $0s = S(1,2);
697 }
698             "#,
699         )
700     }
701
702     #[test]
703     fn dont_trigger_when_subpattern_exists() {
704         // sub-pattern is only allowed with IdentPat (name), not other patterns (like TuplePat)
705         cov_mark::check!(destructure_tuple_subpattern);
706         check_assist_not_applicable(
707             assist,
708             r#"
709 fn sum(t: (usize, usize)) -> usize {
710     match t {
711         $0t @ (1..=3,1..=3) => t.0 + t.1,
712         _ => 0,
713     }
714 }
715             "#,
716         )
717     }
718
719     #[test]
720     fn in_subpattern() {
721         check_assist(
722             assist,
723             r#"
724 fn main() {
725     let t1 @ (_, $0t2) = (1, (2,3));
726     let v = t1.0 + t2.0 + t2.1;
727 }
728             "#,
729             r#"
730 fn main() {
731     let t1 @ (_, ($0_0, _1)) = (1, (2,3));
732     let v = t1.0 + _0 + _1;
733 }
734             "#,
735         )
736     }
737
738     #[test]
739     fn in_nested_tuple() {
740         check_assist(
741             assist,
742             r#"
743 fn main() {
744     let ($0tup, v) = ((1,2),3);
745 }
746             "#,
747             r#"
748 fn main() {
749     let (($0_0, _1), v) = ((1,2),3);
750 }
751             "#,
752         )
753     }
754
755     #[test]
756     fn in_closure() {
757         check_assist(
758             assist,
759             r#"
760 fn main() {
761     let $0tup = (1,2,3);
762     let f = |v| v + tup.1;
763 }
764             "#,
765             r#"
766 fn main() {
767     let ($0_0, _1, _2) = (1,2,3);
768     let f = |v| v + _1;
769 }
770             "#,
771         )
772     }
773
774     #[test]
775     fn in_closure_args() {
776         check_assist(
777             assist,
778             r#"
779 fn main() {
780     let f = |$0t| t.0 + t.1;
781     let v = f((1,2));
782 }
783             "#,
784             r#"
785 fn main() {
786     let f = |($0_0, _1)| _0 + _1;
787     let v = f((1,2));
788 }
789             "#,
790         )
791     }
792
793     #[test]
794     fn in_function_args() {
795         check_assist(
796             assist,
797             r#"
798 fn f($0t: (usize, usize)) {
799     let v = t.0;
800 }
801             "#,
802             r#"
803 fn f(($0_0, _1): (usize, usize)) {
804     let v = _0;
805 }
806             "#,
807         )
808     }
809
810     #[test]
811     fn in_if_let() {
812         check_assist(
813             assist,
814             r#"
815 fn f(t: (usize, usize)) {
816     if let $0t = t {
817         let v = t.0;
818     }
819 }
820             "#,
821             r#"
822 fn f(t: (usize, usize)) {
823     if let ($0_0, _1) = t {
824         let v = _0;
825     }
826 }
827             "#,
828         )
829     }
830     #[test]
831     fn in_if_let_option() {
832         check_assist(
833             assist,
834             r#"
835 //- minicore: option
836 fn f(o: Option<(usize, usize)>) {
837     if let Some($0t) = o {
838         let v = t.0;
839     }
840 }
841             "#,
842             r#"
843 fn f(o: Option<(usize, usize)>) {
844     if let Some(($0_0, _1)) = o {
845         let v = _0;
846     }
847 }
848             "#,
849         )
850     }
851
852
853     #[test]
854     fn in_match() {
855         check_assist(
856             assist,
857             r#"
858 fn main() {
859     match (1,2) {
860         $0t => t.1,
861     };
862 }
863             "#,
864             r#"
865 fn main() {
866     match (1,2) {
867         ($0_0, _1) => _1,
868     };
869 }
870             "#,
871         )
872     }
873     #[test]
874     fn in_match_option() {
875         check_assist(
876             assist,
877             r#"
878 //- minicore: option
879 fn main() {
880     match Some((1,2)) {
881         Some($0t) => t.1,
882         _ => 0,
883     };
884 }
885             "#,
886             r#"
887 fn main() {
888     match Some((1,2)) {
889         Some(($0_0, _1)) => _1,
890         _ => 0,
891     };
892 }
893             "#,
894         )
895     }
896     #[test]
897     fn in_match_reference_option() {
898         check_assist(
899             assist,
900             r#"
901 //- minicore: option
902 fn main() {
903     let t = (1,2);
904     match Some(&t) {
905         Some($0t) => t.1,
906         _ => 0,
907     };
908 }
909             "#,
910             r#"
911 fn main() {
912     let t = (1,2);
913     match Some(&t) {
914         Some(($0_0, _1)) => _1,
915         _ => 0,
916     };
917 }
918             "#,
919         )
920     }
921
922     #[test]
923     fn in_for() {
924         check_assist(
925             assist,
926             r#"
927 //- minicore: iterators
928 fn main() {
929     for $0t in core::iter::repeat((1,2))  {
930         let v = t.1;
931     }
932 }
933             "#,
934             r#"
935 fn main() {
936     for ($0_0, _1) in core::iter::repeat((1,2))  {
937         let v = _1;
938     }
939 }
940             "#,
941         )
942     }
943     #[test]
944     fn in_for_nested() {
945         check_assist(
946             assist,
947             r#"
948 //- minicore: iterators
949 fn main() {
950     for (a, $0b) in core::iter::repeat((1,(2,3)))  {
951         let v = b.1;
952     }
953 }
954             "#,
955             r#"
956 fn main() {
957     for (a, ($0_0, _1)) in core::iter::repeat((1,(2,3)))  {
958         let v = _1;
959     }
960 }
961             "#,
962         )
963     }
964
965     #[test]
966     fn not_applicable_on_tuple_usage() {
967         //Improvement: might be reasonable to allow & implement
968         check_assist_not_applicable(
969             assist,
970             r#"
971 fn main() {
972     let t = (1,2);
973     let v = $0t.0;
974 }
975             "#,
976         )
977     }
978
979     #[test]
980     fn replace_all() {
981         check_assist(
982             assist,
983             r#"
984 fn main() {
985     let $0t = (1,2);
986     let v = t.1;
987     let s = (t.0 + t.1) / 2;
988     let f = |v| v + t.0;
989     let r = f(t.1);
990     let e = t == (9,0);
991     let m =
992       match t {
993         (_,2) if t.0 > 2 => 1,
994         _ => 0,
995       };
996 }
997             "#,
998             r#"
999 fn main() {
1000     let ($0_0, _1) = (1,2);
1001     let v = _1;
1002     let s = (_0 + _1) / 2;
1003     let f = |v| v + _0;
1004     let r = f(_1);
1005     let e = /*t*/ == (9,0);
1006     let m =
1007       match /*t*/ {
1008         (_,2) if _0 > 2 => 1,
1009         _ => 0,
1010       };
1011 }
1012             "#,
1013         )
1014     }
1015
1016     #[test]
1017     fn non_trivial_tuple_assignment() {
1018         check_assist(
1019             assist,
1020             r#"
1021 fn main {
1022     let $0t = 
1023         if 1 > 2 {
1024             (1,2)
1025         } else {
1026             (5,6)
1027         };
1028     let v1 = t.0;
1029     let v2 =
1030         if t.0 > t.1 {
1031             t.0 - t.1
1032         } else {
1033             t.1 - t.0
1034         };
1035 }
1036             "#,
1037             r#"
1038 fn main {
1039     let ($0_0, _1) = 
1040         if 1 > 2 {
1041             (1,2)
1042         } else {
1043             (5,6)
1044         };
1045     let v1 = _0;
1046     let v2 =
1047         if _0 > _1 {
1048             _0 - _1
1049         } else {
1050             _1 - _0
1051         };
1052 }
1053             "#,
1054         )
1055     }
1056
1057     mod assist {
1058         use super::*;
1059         use crate::tests::check_assist_by_label;
1060
1061         fn assist(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
1062             destructure_tuple_binding_impl(acc, ctx, true)
1063         }
1064
1065         pub(crate) fn check_in_place_assist(
1066
1067             ra_fixture_before: &str,
1068             ra_fixture_after: &str,
1069         ) {
1070             check_assist_by_label(
1071                 assist, 
1072                 ra_fixture_before, 
1073                 ra_fixture_after, 
1074                 "Destructure tuple in place"
1075             );
1076         }
1077
1078         pub(crate) fn check_sub_pattern_assist(
1079             ra_fixture_before: &str,
1080             ra_fixture_after: &str,
1081         ) {
1082             check_assist_by_label(
1083                 assist, 
1084                 ra_fixture_before, 
1085                 ra_fixture_after, 
1086                 "Destructure tuple in sub-pattern"
1087             );
1088         }
1089
1090         pub(crate) fn check_both_assists(
1091             ra_fixture_before: &str,
1092             ra_fixture_after_in_place: &str,
1093             ra_fixture_after_in_sub_pattern: &str,
1094         ) {
1095             check_in_place_assist(ra_fixture_before, ra_fixture_after_in_place);
1096             check_sub_pattern_assist(ra_fixture_before, ra_fixture_after_in_sub_pattern);
1097         }
1098     }
1099
1100     /// Tests for destructure of tuple in sub-pattern: 
1101     /// `let $0t = (1,2);` -> `let t @ (_0, _1) = (1,2);`
1102     mod sub_pattern {
1103         use super::*;
1104         use super::assist::*;
1105         use crate::tests::check_assist_by_label;
1106
1107         #[test]
1108         fn destructure_in_sub_pattern() {
1109             check_sub_pattern_assist(
1110                 r#"
1111 #![feature(bindings_after_at)]
1112
1113 fn main() {
1114     let $0t = (1,2);
1115 }
1116                 "#,
1117                 r#"
1118 #![feature(bindings_after_at)]
1119
1120 fn main() {
1121     let t @ ($0_0, _1) = (1,2);
1122 }
1123                 "#,
1124             )
1125         }
1126     
1127         #[test]
1128         fn trigger_both_destructure_tuple_assists() {
1129             fn assist(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
1130                 destructure_tuple_binding_impl(acc, ctx, true)
1131             }
1132             let text = r#"
1133 fn main() {
1134     let $0t = (1,2);
1135 }
1136             "#;
1137             check_assist_by_label(
1138                 assist, 
1139                 text, 
1140                 r#"
1141 fn main() {
1142     let ($0_0, _1) = (1,2);
1143 }
1144             "#, 
1145                 "Destructure tuple in place",
1146             );
1147             check_assist_by_label(
1148                 assist, 
1149                 text, 
1150                 r#"
1151 fn main() {
1152     let t @ ($0_0, _1) = (1,2);
1153 }
1154             "#, 
1155                 "Destructure tuple in sub-pattern",
1156             );
1157         }
1158
1159         #[test]
1160         fn replace_indices() {
1161             check_sub_pattern_assist(
1162                 r#"
1163 fn main() {
1164     let $0t = (1,2);
1165     let v1 = t.0;
1166     let v2 = t.1;
1167 }
1168                 "#,
1169                 r#"
1170 fn main() {
1171     let t @ ($0_0, _1) = (1,2);
1172     let v1 = _0;
1173     let v2 = _1;
1174 }
1175                 "#,
1176             )
1177         }
1178     
1179         #[test]
1180         fn keep_function_call() {
1181             cov_mark::check!(destructure_tuple_call_with_subpattern);
1182             check_sub_pattern_assist(
1183                 r#"
1184 fn main() {
1185     let $0t = (1,2);
1186     let v = t.into();
1187 }
1188                 "#,
1189                 r#"
1190 fn main() {
1191     let t @ ($0_0, _1) = (1,2);
1192     let v = t.into();
1193 }
1194                 "#,
1195             )
1196         }
1197     
1198         #[test]
1199         fn keep_type() {
1200             check_sub_pattern_assist(
1201                 r#"
1202 fn main() {
1203     let $0t: (usize, i32) = (1,2);
1204     let v = t.1;
1205     let f = t.into();
1206 }
1207                 "#,
1208                 r#"
1209 fn main() {
1210     let t @ ($0_0, _1): (usize, i32) = (1,2);
1211     let v = _1;
1212     let f = t.into();
1213 }
1214                 "#,
1215             )
1216         }
1217
1218         #[test]
1219         fn in_function_args() {
1220             check_sub_pattern_assist(
1221                 r#"
1222 fn f($0t: (usize, usize)) {
1223     let v = t.0;
1224     let f = t.into();
1225 }
1226                 "#,
1227                 r#"
1228 fn f(t @ ($0_0, _1): (usize, usize)) {
1229     let v = _0;
1230     let f = t.into();
1231 }
1232                 "#,
1233             )
1234         }
1235
1236         #[test]
1237         fn with_ref() {
1238             check_sub_pattern_assist(
1239                 r#"
1240 fn main() {
1241     let ref $0t = (1,2);
1242     let v = t.1;
1243     let f = t.into();
1244 }
1245                 "#,
1246                 r#"
1247 fn main() {
1248     let ref t @ (ref $0_0, ref _1) = (1,2);
1249     let v = _1;
1250     let f = t.into();
1251 }
1252                 "#,
1253             )
1254         }
1255         #[test]
1256         fn with_mut() {
1257             check_sub_pattern_assist(
1258                 r#"
1259 fn main() {
1260     let mut $0t = (1,2);
1261     let v = t.1;
1262     let f = t.into();
1263 }
1264                 "#,
1265                 r#"
1266 fn main() {
1267     let mut t @ (mut $0_0, mut _1) = (1,2);
1268     let v = _1;
1269     let f = t.into();
1270 }
1271                 "#,
1272             )
1273         }
1274         #[test]
1275         fn with_ref_mut() {
1276             check_sub_pattern_assist(
1277                 r#"
1278 fn main() {
1279     let ref mut $0t = (1,2);
1280     let v = t.1;
1281     let f = t.into();
1282 }
1283                 "#,
1284                 r#"
1285 fn main() {
1286     let ref mut t @ (ref mut $0_0, ref mut _1) = (1,2);
1287     let v = _1;
1288     let f = t.into();
1289 }
1290                 "#,
1291             )
1292         }
1293     }
1294
1295     /// Tests for tuple usage in macro call:
1296     /// `dbg!(t.0)`
1297     mod in_macro_call {       
1298         use super::assist::*;
1299
1300
1301         #[test]
1302         fn detect_macro_call() {
1303             cov_mark::check!(destructure_tuple_macro_call);
1304             check_in_place_assist(
1305                 r#"
1306 macro_rules! m {
1307     ($e:expr) => { "foo"; $e };
1308 }
1309
1310 fn main() {
1311     let $0t = (1,2);
1312     m!(t.0);
1313 }
1314                 "#,
1315                 r#"
1316 macro_rules! m {
1317     ($e:expr) => { "foo"; $e };
1318 }
1319
1320 fn main() {
1321     let ($0_0, _1) = (1,2);
1322     m!(/*t*/.0);
1323 }
1324                 "#,
1325             )
1326         }
1327
1328         #[test]
1329         fn tuple_usage() {
1330             check_both_assists(
1331                 // leading `"foo"` to ensure `$e` doesn't start at position `0`
1332                 r#"
1333 macro_rules! m {
1334     ($e:expr) => { "foo"; $e };
1335 }
1336
1337 fn main() {
1338     let $0t = (1,2);
1339     m!(t);
1340 }
1341                 "#,
1342                 r#"
1343 macro_rules! m {
1344     ($e:expr) => { "foo"; $e };
1345 }
1346
1347 fn main() {
1348     let ($0_0, _1) = (1,2);
1349     m!(/*t*/);
1350 }
1351                 "#,
1352                 r#"
1353 macro_rules! m {
1354     ($e:expr) => { "foo"; $e };
1355 }
1356
1357 fn main() {
1358     let t @ ($0_0, _1) = (1,2);
1359     m!(t);
1360 }
1361                 "#,
1362             )
1363         }
1364
1365         #[test]
1366         fn tuple_function_usage() {
1367             check_both_assists(
1368                 r#"
1369 macro_rules! m {
1370     ($e:expr) => { "foo"; $e };
1371 }
1372
1373 fn main() {
1374     let $0t = (1,2);
1375     m!(t.into());
1376 }
1377                 "#,
1378                 r#"
1379 macro_rules! m {
1380     ($e:expr) => { "foo"; $e };
1381 }
1382
1383 fn main() {
1384     let ($0_0, _1) = (1,2);
1385     m!(/*t*/.into());
1386 }
1387                 "#,
1388                 r#"
1389 macro_rules! m {
1390     ($e:expr) => { "foo"; $e };
1391 }
1392
1393 fn main() {
1394     let t @ ($0_0, _1) = (1,2);
1395     m!(t.into());
1396 }
1397                 "#,
1398             )
1399         }
1400
1401         #[test]
1402         fn tuple_index_usage() {
1403             check_both_assists(
1404                 r#"
1405 macro_rules! m {
1406     ($e:expr) => { "foo"; $e };
1407 }
1408
1409 fn main() {
1410     let $0t = (1,2);
1411     m!(t.0);
1412 }
1413                 "#,
1414                 // FIXME: replace `t.0` with `_0` (cannot detect range of tuple index in macro call)
1415                 r#"
1416 macro_rules! m {
1417     ($e:expr) => { "foo"; $e };
1418 }
1419
1420 fn main() {
1421     let ($0_0, _1) = (1,2);
1422     m!(/*t*/.0);
1423 }
1424                 "#,
1425                 // FIXME: replace `t.0` with `_0`
1426                 r#"
1427 macro_rules! m {
1428     ($e:expr) => { "foo"; $e };
1429 }
1430
1431 fn main() {
1432     let t @ ($0_0, _1) = (1,2);
1433     m!(t.0);
1434 }
1435                 "#,
1436             )
1437         }
1438
1439         #[test]
1440         fn tuple_in_parentheses_index_usage() {
1441             check_both_assists(
1442                 r#"
1443 macro_rules! m {
1444     ($e:expr) => { "foo"; $e };
1445 }
1446
1447 fn main() {
1448     let $0t = (1,2);
1449     m!((t).0);
1450 }
1451                 "#,
1452                 // FIXME: replace `(t).0` with `_0`
1453                 r#"
1454 macro_rules! m {
1455     ($e:expr) => { "foo"; $e };
1456 }
1457
1458 fn main() {
1459     let ($0_0, _1) = (1,2);
1460     m!((/*t*/).0);
1461 }
1462                 "#,
1463                 // FIXME: replace `(t).0` with `_0`
1464                 r#"
1465 macro_rules! m {
1466     ($e:expr) => { "foo"; $e };
1467 }
1468
1469 fn main() {
1470     let t @ ($0_0, _1) = (1,2);
1471     m!((t).0);
1472 }
1473                 "#,
1474             )
1475         }
1476
1477         #[test]
1478         fn empty_macro() {
1479             check_in_place_assist(
1480                 r#"
1481 macro_rules! m {
1482     () => { "foo" };
1483     ($e:expr) => { $e; "foo" };
1484 }
1485
1486 fn main() {
1487     let $0t = (1,2);
1488     m!(t);
1489 }
1490                 "#,
1491                 // FIXME: macro allows no arg -> is valid. But assist should result in invalid code
1492                 r#"
1493 macro_rules! m {
1494     () => { "foo" };
1495     ($e:expr) => { $e; "foo" };
1496 }
1497
1498 fn main() {
1499     let ($0_0, _1) = (1,2);
1500     m!(/*t*/);
1501 }
1502                 "#,
1503             )
1504         }
1505
1506         #[test]
1507         fn tuple_index_in_macro() {
1508             check_both_assists(
1509                 r#"
1510 macro_rules! m {
1511     ($t:expr, $i:expr) => { $t.0 + $i };
1512 }
1513
1514 fn main() {
1515     let $0t = (1,2);
1516     m!(t, t.0);
1517 }
1518                 "#,
1519                 // FIXME: replace `t.0` in macro call (not IN macro) with `_0`
1520                 r#"
1521 macro_rules! m {
1522     ($t:expr, $i:expr) => { $t.0 + $i };
1523 }
1524
1525 fn main() {
1526     let ($0_0, _1) = (1,2);
1527     m!(/*t*/, /*t*/.0);
1528 }
1529                 "#,
1530                 // FIXME: replace `t.0` in macro call with `_0`
1531                 r#"
1532 macro_rules! m {
1533     ($t:expr, $i:expr) => { $t.0 + $i };
1534 }
1535
1536 fn main() {
1537     let t @ ($0_0, _1) = (1,2);
1538     m!(t, t.0);
1539 }
1540                 "#,
1541             )
1542
1543         }
1544     }
1545 }