]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/fill_match_arms.rs
Merge #9308
[rust.git] / crates / ide_assists / src / handlers / fill_match_arms.rs
1 use std::iter::{self, Peekable};
2
3 use either::Either;
4 use hir::{Adt, HasSource, ModuleDef, Semantics};
5 use ide_db::helpers::{mod_path_to_ast, FamousDefs};
6 use ide_db::RootDatabase;
7 use itertools::Itertools;
8 use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
9
10 use crate::{
11     utils::{self, render_snippet, Cursor},
12     AssistContext, AssistId, AssistKind, Assists,
13 };
14
15 // Assist: fill_match_arms
16 //
17 // Adds missing clauses to a `match` expression.
18 //
19 // ```
20 // enum Action { Move { distance: u32 }, Stop }
21 //
22 // fn handle(action: Action) {
23 //     match action {
24 //         $0
25 //     }
26 // }
27 // ```
28 // ->
29 // ```
30 // enum Action { Move { distance: u32 }, Stop }
31 //
32 // fn handle(action: Action) {
33 //     match action {
34 //         $0Action::Move { distance } => todo!(),
35 //         Action::Stop => todo!(),
36 //     }
37 // }
38 // ```
39 pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
40     let match_expr = ctx.find_node_at_offset_with_descend::<ast::MatchExpr>()?;
41     let match_arm_list = match_expr.match_arm_list()?;
42
43     let expr = match_expr.expr()?;
44
45     let mut arms: Vec<MatchArm> = match_arm_list.arms().collect();
46     if arms.len() == 1 {
47         if let Some(Pat::WildcardPat(..)) = arms[0].pat() {
48             arms.clear();
49         }
50     }
51
52     let top_lvl_pats: Vec<_> = arms
53         .iter()
54         .filter_map(ast::MatchArm::pat)
55         .flat_map(|pat| match pat {
56             // Special case OrPat as separate top-level pats
57             Pat::OrPat(or_pat) => Either::Left(or_pat.pats()),
58             _ => Either::Right(iter::once(pat)),
59         })
60         // Exclude top level wildcards so that they are expanded by this assist, retains status quo in #8129.
61         .filter(|pat| !matches!(pat, Pat::WildcardPat(_)))
62         .collect();
63
64     let module = ctx.sema.scope(expr.syntax()).module()?;
65
66     let mut missing_pats: Peekable<Box<dyn Iterator<Item = ast::Pat>>> = if let Some(enum_def) =
67         resolve_enum_def(&ctx.sema, &expr)
68     {
69         let variants = enum_def.variants(ctx.db());
70
71         let missing_pats = variants
72             .into_iter()
73             .filter_map(|variant| build_pat(ctx.db(), module, variant))
74             .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat));
75
76         let missing_pats: Box<dyn Iterator<Item = _>> = if Some(enum_def)
77             == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option().map(lift_enum)
78         {
79             // Match `Some` variant first.
80             cov_mark::hit!(option_order);
81             Box::new(missing_pats.rev())
82         } else {
83             Box::new(missing_pats)
84         };
85         missing_pats.peekable()
86     } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
87         let mut n_arms = 1;
88         let variants_of_enums: Vec<Vec<ExtendedVariant>> = enum_defs
89             .into_iter()
90             .map(|enum_def| enum_def.variants(ctx.db()))
91             .inspect(|variants| n_arms *= variants.len())
92             .collect();
93
94         // When calculating the match arms for a tuple of enums, we want
95         // to create a match arm for each possible combination of enum
96         // values. The `multi_cartesian_product` method transforms
97         // Vec<Vec<EnumVariant>> into Vec<(EnumVariant, .., EnumVariant)>
98         // where each tuple represents a proposed match arm.
99
100         // A number of arms grows very fast on even a small tuple of large enums.
101         // We skip the assist beyond an arbitrary threshold.
102         if n_arms > 256 {
103             return None;
104         }
105         let missing_pats = variants_of_enums
106             .into_iter()
107             .multi_cartesian_product()
108             .inspect(|_| cov_mark::hit!(fill_match_arms_lazy_computation))
109             .map(|variants| {
110                 let patterns =
111                     variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant));
112                 ast::Pat::from(make::tuple_pat(patterns))
113             })
114             .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat));
115         (Box::new(missing_pats) as Box<dyn Iterator<Item = _>>).peekable()
116     } else {
117         return None;
118     };
119
120     if missing_pats.peek().is_none() {
121         return None;
122     }
123
124     let target = ctx.sema.original_range(match_expr.syntax()).range;
125     acc.add(
126         AssistId("fill_match_arms", AssistKind::QuickFix),
127         "Fill match arms",
128         target,
129         |builder| {
130             let new_match_arm_list = match_arm_list.clone_for_update();
131             let missing_arms = missing_pats
132                 .map(|pat| make::match_arm(iter::once(pat), make::ext::expr_todo()))
133                 .map(|it| it.clone_for_update());
134
135             let catch_all_arm = new_match_arm_list
136                 .arms()
137                 .find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_))));
138             if let Some(arm) = catch_all_arm {
139                 arm.remove()
140             }
141             let mut first_new_arm = None;
142             for arm in missing_arms {
143                 first_new_arm.get_or_insert_with(|| arm.clone());
144                 new_match_arm_list.add_arm(arm);
145             }
146
147             let old_range = ctx.sema.original_range(match_arm_list.syntax()).range;
148             match (first_new_arm, ctx.config.snippet_cap) {
149                 (Some(first_new_arm), Some(cap)) => {
150                     let extend_lifetime;
151                     let cursor =
152                         match first_new_arm.syntax().descendants().find_map(ast::WildcardPat::cast)
153                         {
154                             Some(it) => {
155                                 extend_lifetime = it.syntax().clone();
156                                 Cursor::Replace(&extend_lifetime)
157                             }
158                             None => Cursor::Before(first_new_arm.syntax()),
159                         };
160                     let snippet = render_snippet(cap, new_match_arm_list.syntax(), cursor);
161                     builder.replace_snippet(cap, old_range, snippet);
162                 }
163                 _ => builder.replace(old_range, new_match_arm_list.to_string()),
164             }
165         },
166     )
167 }
168
169 fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool {
170     !existing_pats.iter().any(|pat| does_pat_match_variant(pat, var))
171 }
172
173 // Fixme: this is still somewhat limited, use hir_ty::diagnostics::match_check?
174 fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
175     match (pat, var) {
176         (Pat::WildcardPat(_), _) => true,
177         (Pat::TuplePat(tpat), Pat::TuplePat(tvar)) => {
178             tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v))
179         }
180         _ => utils::does_pat_match_variant(pat, var),
181     }
182 }
183
184 #[derive(Eq, PartialEq, Clone, Copy)]
185 enum ExtendedEnum {
186     Bool,
187     Enum(hir::Enum),
188 }
189
190 #[derive(Eq, PartialEq, Clone, Copy)]
191 enum ExtendedVariant {
192     True,
193     False,
194     Variant(hir::Variant),
195 }
196
197 fn lift_enum(e: hir::Enum) -> ExtendedEnum {
198     ExtendedEnum::Enum(e)
199 }
200
201 impl ExtendedEnum {
202     fn variants(self, db: &RootDatabase) -> Vec<ExtendedVariant> {
203         match self {
204             ExtendedEnum::Enum(e) => {
205                 e.variants(db).into_iter().map(ExtendedVariant::Variant).collect::<Vec<_>>()
206             }
207             ExtendedEnum::Bool => {
208                 Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False])
209             }
210         }
211     }
212 }
213
214 fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
215     sema.type_of_expr(expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
216         Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
217         _ => {
218             if ty.is_bool() {
219                 Some(ExtendedEnum::Bool)
220             } else {
221                 None
222             }
223         }
224     })
225 }
226
227 fn resolve_tuple_of_enum_def(
228     sema: &Semantics<RootDatabase>,
229     expr: &ast::Expr,
230 ) -> Option<Vec<ExtendedEnum>> {
231     sema.type_of_expr(expr)?
232         .tuple_fields(sema.db)
233         .iter()
234         .map(|ty| {
235             ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
236                 Some(Adt::Enum(e)) => Some(lift_enum(e)),
237                 // For now we only handle expansion for a tuple of enums. Here
238                 // we map non-enum items to None and rely on `collect` to
239                 // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
240                 _ => {
241                     if ty.is_bool() {
242                         Some(ExtendedEnum::Bool)
243                     } else {
244                         None
245                     }
246                 }
247             })
248         })
249         .collect()
250 }
251
252 fn build_pat(db: &RootDatabase, module: hir::Module, var: ExtendedVariant) -> Option<ast::Pat> {
253     match var {
254         ExtendedVariant::Variant(var) => {
255             let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?);
256
257             // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
258             let pat: ast::Pat = match var.source(db)?.value.kind() {
259                 ast::StructKind::Tuple(field_list) => {
260                     let pats =
261                         iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
262                     make::tuple_struct_pat(path, pats).into()
263                 }
264                 ast::StructKind::Record(field_list) => {
265                     let pats =
266                         field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into());
267                     make::record_pat(path, pats).into()
268                 }
269                 ast::StructKind::Unit => make::path_pat(path),
270             };
271
272             Some(pat)
273         }
274         ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))),
275         ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))),
276     }
277 }
278
279 #[cfg(test)]
280 mod tests {
281     use crate::tests::{
282         check_assist, check_assist_not_applicable, check_assist_target, check_assist_unresolved,
283     };
284
285     use super::fill_match_arms;
286
287     #[test]
288     fn all_match_arms_provided() {
289         check_assist_not_applicable(
290             fill_match_arms,
291             r#"
292 enum A {
293     As,
294     Bs{x:i32, y:Option<i32>},
295     Cs(i32, Option<i32>),
296 }
297 fn main() {
298     match A::As$0 {
299         A::As,
300         A::Bs{x,y:Some(_)} => {}
301         A::Cs(_, Some(_)) => {}
302     }
303 }
304             "#,
305         );
306     }
307
308     #[test]
309     fn all_boolean_match_arms_provided() {
310         check_assist_not_applicable(
311             fill_match_arms,
312             r#"
313 fn foo(a: bool) {
314     match a$0 {
315         true => {}
316         false => {}
317     }
318 }
319 "#,
320         )
321     }
322
323     #[test]
324     fn tuple_of_non_enum() {
325         // for now this case is not handled, although it potentially could be
326         // in the future
327         check_assist_not_applicable(
328             fill_match_arms,
329             r#"
330 fn main() {
331     match (0, false)$0 {
332     }
333 }
334 "#,
335         );
336     }
337
338     #[test]
339     fn fill_match_arms_boolean() {
340         check_assist(
341             fill_match_arms,
342             r#"
343 fn foo(a: bool) {
344     match a$0 {
345     }
346 }
347 "#,
348             r#"
349 fn foo(a: bool) {
350     match a {
351         $0true => todo!(),
352         false => todo!(),
353     }
354 }
355 "#,
356         )
357     }
358
359     #[test]
360     fn partial_fill_boolean() {
361         check_assist(
362             fill_match_arms,
363             r#"
364 fn foo(a: bool) {
365     match a$0 {
366         true => {}
367     }
368 }
369 "#,
370             r#"
371 fn foo(a: bool) {
372     match a {
373         true => {}
374         $0false => todo!(),
375     }
376 }
377 "#,
378         )
379     }
380
381     #[test]
382     fn all_boolean_tuple_arms_provided() {
383         check_assist_not_applicable(
384             fill_match_arms,
385             r#"
386 fn foo(a: bool) {
387     match (a, a)$0 {
388         (true, true) => {}
389         (true, false) => {}
390         (false, true) => {}
391         (false, false) => {}
392     }
393 }
394 "#,
395         )
396     }
397
398     #[test]
399     fn fill_boolean_tuple() {
400         check_assist(
401             fill_match_arms,
402             r#"
403 fn foo(a: bool) {
404     match (a, a)$0 {
405     }
406 }
407 "#,
408             r#"
409 fn foo(a: bool) {
410     match (a, a) {
411         $0(true, true) => todo!(),
412         (true, false) => todo!(),
413         (false, true) => todo!(),
414         (false, false) => todo!(),
415     }
416 }
417 "#,
418         )
419     }
420
421     #[test]
422     fn partial_fill_boolean_tuple() {
423         check_assist(
424             fill_match_arms,
425             r#"
426 fn foo(a: bool) {
427     match (a, a)$0 {
428         (false, true) => {}
429     }
430 }
431 "#,
432             r#"
433 fn foo(a: bool) {
434     match (a, a) {
435         (false, true) => {}
436         $0(true, true) => todo!(),
437         (true, false) => todo!(),
438         (false, false) => todo!(),
439     }
440 }
441 "#,
442         )
443     }
444
445     #[test]
446     fn partial_fill_record_tuple() {
447         check_assist(
448             fill_match_arms,
449             r#"
450 enum A {
451     As,
452     Bs { x: i32, y: Option<i32> },
453     Cs(i32, Option<i32>),
454 }
455 fn main() {
456     match A::As$0 {
457         A::Bs { x, y: Some(_) } => {}
458         A::Cs(_, Some(_)) => {}
459     }
460 }
461 "#,
462             r#"
463 enum A {
464     As,
465     Bs { x: i32, y: Option<i32> },
466     Cs(i32, Option<i32>),
467 }
468 fn main() {
469     match A::As {
470         A::Bs { x, y: Some(_) } => {}
471         A::Cs(_, Some(_)) => {}
472         $0A::As => todo!(),
473     }
474 }
475 "#,
476         );
477     }
478
479     #[test]
480     fn partial_fill_option() {
481         check_assist(
482             fill_match_arms,
483             r#"
484 enum Option<T> { Some(T), None }
485 use Option::*;
486
487 fn main() {
488     match None$0 {
489         None => {}
490     }
491 }
492             "#,
493             r#"
494 enum Option<T> { Some(T), None }
495 use Option::*;
496
497 fn main() {
498     match None {
499         None => {}
500         Some(${0:_}) => todo!(),
501     }
502 }
503             "#,
504         );
505     }
506
507     #[test]
508     fn partial_fill_or_pat() {
509         check_assist(
510             fill_match_arms,
511             r#"
512 enum A { As, Bs, Cs(Option<i32>) }
513 fn main() {
514     match A::As$0 {
515         A::Cs(_) | A::Bs => {}
516     }
517 }
518 "#,
519             r#"
520 enum A { As, Bs, Cs(Option<i32>) }
521 fn main() {
522     match A::As {
523         A::Cs(_) | A::Bs => {}
524         $0A::As => todo!(),
525     }
526 }
527 "#,
528         );
529     }
530
531     #[test]
532     fn partial_fill() {
533         check_assist(
534             fill_match_arms,
535             r#"
536 enum A { As, Bs, Cs, Ds(String), Es(B) }
537 enum B { Xs, Ys }
538 fn main() {
539     match A::As$0 {
540         A::Bs if 0 < 1 => {}
541         A::Ds(_value) => { let x = 1; }
542         A::Es(B::Xs) => (),
543     }
544 }
545 "#,
546             r#"
547 enum A { As, Bs, Cs, Ds(String), Es(B) }
548 enum B { Xs, Ys }
549 fn main() {
550     match A::As {
551         A::Bs if 0 < 1 => {}
552         A::Ds(_value) => { let x = 1; }
553         A::Es(B::Xs) => (),
554         $0A::As => todo!(),
555         A::Cs => todo!(),
556     }
557 }
558 "#,
559         );
560     }
561
562     #[test]
563     fn partial_fill_bind_pat() {
564         check_assist(
565             fill_match_arms,
566             r#"
567 enum A { As, Bs, Cs(Option<i32>) }
568 fn main() {
569     match A::As$0 {
570         A::As(_) => {}
571         a @ A::Bs(_) => {}
572     }
573 }
574 "#,
575             r#"
576 enum A { As, Bs, Cs(Option<i32>) }
577 fn main() {
578     match A::As {
579         A::As(_) => {}
580         a @ A::Bs(_) => {}
581         A::Cs(${0:_}) => todo!(),
582     }
583 }
584 "#,
585         );
586     }
587
588     #[test]
589     fn fill_match_arms_empty_body() {
590         check_assist(
591             fill_match_arms,
592             r#"
593 enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
594
595 fn main() {
596     let a = A::As;
597     match a$0 {}
598 }
599 "#,
600             r#"
601 enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
602
603 fn main() {
604     let a = A::As;
605     match a {
606         $0A::As => todo!(),
607         A::Bs => todo!(),
608         A::Cs(_) => todo!(),
609         A::Ds(_, _) => todo!(),
610         A::Es { x, y } => todo!(),
611     }
612 }
613 "#,
614         );
615     }
616
617     #[test]
618     fn fill_match_arms_tuple_of_enum() {
619         check_assist(
620             fill_match_arms,
621             r#"
622 enum A { One, Two }
623 enum B { One, Two }
624
625 fn main() {
626     let a = A::One;
627     let b = B::One;
628     match (a$0, b) {}
629 }
630 "#,
631             r#"
632 enum A { One, Two }
633 enum B { One, Two }
634
635 fn main() {
636     let a = A::One;
637     let b = B::One;
638     match (a, b) {
639         $0(A::One, B::One) => todo!(),
640         (A::One, B::Two) => todo!(),
641         (A::Two, B::One) => todo!(),
642         (A::Two, B::Two) => todo!(),
643     }
644 }
645 "#,
646         );
647     }
648
649     #[test]
650     fn fill_match_arms_tuple_of_enum_ref() {
651         check_assist(
652             fill_match_arms,
653             r#"
654 enum A { One, Two }
655 enum B { One, Two }
656
657 fn main() {
658     let a = A::One;
659     let b = B::One;
660     match (&a$0, &b) {}
661 }
662 "#,
663             r#"
664 enum A { One, Two }
665 enum B { One, Two }
666
667 fn main() {
668     let a = A::One;
669     let b = B::One;
670     match (&a, &b) {
671         $0(A::One, B::One) => todo!(),
672         (A::One, B::Two) => todo!(),
673         (A::Two, B::One) => todo!(),
674         (A::Two, B::Two) => todo!(),
675     }
676 }
677 "#,
678         );
679     }
680
681     #[test]
682     fn fill_match_arms_tuple_of_enum_partial() {
683         check_assist(
684             fill_match_arms,
685             r#"
686 enum A { One, Two }
687 enum B { One, Two }
688
689 fn main() {
690     let a = A::One;
691     let b = B::One;
692     match (a$0, b) {
693         (A::Two, B::One) => {}
694     }
695 }
696 "#,
697             r#"
698 enum A { One, Two }
699 enum B { One, Two }
700
701 fn main() {
702     let a = A::One;
703     let b = B::One;
704     match (a, b) {
705         (A::Two, B::One) => {}
706         $0(A::One, B::One) => todo!(),
707         (A::One, B::Two) => todo!(),
708         (A::Two, B::Two) => todo!(),
709     }
710 }
711 "#,
712         );
713     }
714
715     #[test]
716     fn fill_match_arms_tuple_of_enum_partial_with_wildcards() {
717         check_assist(
718             fill_match_arms,
719             r#"
720 //- minicore: option
721 fn main() {
722     let a = Some(1);
723     let b = Some(());
724     match (a$0, b) {
725         (Some(_), _) => {}
726         (None, Some(_)) => {}
727     }
728 }
729 "#,
730             r#"
731 fn main() {
732     let a = Some(1);
733     let b = Some(());
734     match (a, b) {
735         (Some(_), _) => {}
736         (None, Some(_)) => {}
737         $0(None, None) => todo!(),
738     }
739 }
740 "#,
741         );
742     }
743
744     #[test]
745     fn fill_match_arms_partial_with_deep_pattern() {
746         // Fixme: cannot handle deep patterns
747         check_assist_not_applicable(
748             fill_match_arms,
749             r#"
750 //- minicore: option
751 fn main() {
752     match $0Some(true) {
753         Some(true) => {}
754         None => {}
755     }
756 }
757 "#,
758         );
759     }
760
761     #[test]
762     fn fill_match_arms_tuple_of_enum_not_applicable() {
763         check_assist_not_applicable(
764             fill_match_arms,
765             r#"
766 enum A { One, Two }
767 enum B { One, Two }
768
769 fn main() {
770     let a = A::One;
771     let b = B::One;
772     match (a$0, b) {
773         (A::Two, B::One) => {}
774         (A::One, B::One) => {}
775         (A::One, B::Two) => {}
776         (A::Two, B::Two) => {}
777     }
778 }
779 "#,
780         );
781     }
782
783     #[test]
784     fn fill_match_arms_single_element_tuple_of_enum() {
785         check_assist(
786             fill_match_arms,
787             r#"
788 enum A { One, Two }
789
790 fn main() {
791     let a = A::One;
792     match (a$0, ) {
793     }
794 }
795 "#,
796             r#"
797 enum A { One, Two }
798
799 fn main() {
800     let a = A::One;
801     match (a, ) {
802         $0(A::One,) => todo!(),
803         (A::Two,) => todo!(),
804     }
805 }
806 "#,
807         );
808     }
809
810     #[test]
811     fn test_fill_match_arm_refs() {
812         check_assist(
813             fill_match_arms,
814             r#"
815 enum A { As }
816
817 fn foo(a: &A) {
818     match a$0 {
819     }
820 }
821 "#,
822             r#"
823 enum A { As }
824
825 fn foo(a: &A) {
826     match a {
827         $0A::As => todo!(),
828     }
829 }
830 "#,
831         );
832
833         check_assist(
834             fill_match_arms,
835             r#"
836 enum A {
837     Es { x: usize, y: usize }
838 }
839
840 fn foo(a: &mut A) {
841     match a$0 {
842     }
843 }
844 "#,
845             r#"
846 enum A {
847     Es { x: usize, y: usize }
848 }
849
850 fn foo(a: &mut A) {
851     match a {
852         $0A::Es { x, y } => todo!(),
853     }
854 }
855 "#,
856         );
857     }
858
859     #[test]
860     fn fill_match_arms_target() {
861         check_assist_target(
862             fill_match_arms,
863             r#"
864 enum E { X, Y }
865
866 fn main() {
867     match E::X$0 {}
868 }
869 "#,
870             "match E::X {}",
871         );
872     }
873
874     #[test]
875     fn fill_match_arms_trivial_arm() {
876         check_assist(
877             fill_match_arms,
878             r#"
879 enum E { X, Y }
880
881 fn main() {
882     match E::X {
883         $0_ => {}
884     }
885 }
886 "#,
887             r#"
888 enum E { X, Y }
889
890 fn main() {
891     match E::X {
892         $0E::X => todo!(),
893         E::Y => todo!(),
894     }
895 }
896 "#,
897         );
898     }
899
900     #[test]
901     fn fill_match_arms_qualifies_path() {
902         check_assist(
903             fill_match_arms,
904             r#"
905 mod foo { pub enum E { X, Y } }
906 use foo::E::X;
907
908 fn main() {
909     match X {
910         $0
911     }
912 }
913 "#,
914             r#"
915 mod foo { pub enum E { X, Y } }
916 use foo::E::X;
917
918 fn main() {
919     match X {
920         $0X => todo!(),
921         foo::E::Y => todo!(),
922     }
923 }
924 "#,
925         );
926     }
927
928     #[test]
929     fn fill_match_arms_preserves_comments() {
930         check_assist(
931             fill_match_arms,
932             r#"
933 enum A { One, Two }
934 fn foo(a: A) {
935     match a {
936         // foo bar baz$0
937         A::One => {}
938         // This is where the rest should be
939     }
940 }
941 "#,
942             r#"
943 enum A { One, Two }
944 fn foo(a: A) {
945     match a {
946         // foo bar baz
947         A::One => {}
948         $0A::Two => todo!(),
949         // This is where the rest should be
950     }
951 }
952 "#,
953         );
954     }
955
956     #[test]
957     fn fill_match_arms_preserves_comments_empty() {
958         check_assist(
959             fill_match_arms,
960             r#"
961 enum A { One, Two }
962 fn foo(a: A) {
963     match a {
964         // foo bar baz$0
965     }
966 }
967 "#,
968             r#"
969 enum A { One, Two }
970 fn foo(a: A) {
971     match a {
972         $0A::One => todo!(),
973         A::Two => todo!(),
974         // foo bar baz
975     }
976 }
977 "#,
978         );
979     }
980
981     #[test]
982     fn fill_match_arms_placeholder() {
983         check_assist(
984             fill_match_arms,
985             r#"
986 enum A { One, Two, }
987 fn foo(a: A) {
988     match a$0 {
989         _ => (),
990     }
991 }
992 "#,
993             r#"
994 enum A { One, Two, }
995 fn foo(a: A) {
996     match a {
997         $0A::One => todo!(),
998         A::Two => todo!(),
999     }
1000 }
1001 "#,
1002         );
1003     }
1004
1005     #[test]
1006     fn option_order() {
1007         cov_mark::check!(option_order);
1008         check_assist(
1009             fill_match_arms,
1010             r#"
1011 //- minicore: option
1012 fn foo(opt: Option<i32>) {
1013     match opt$0 {
1014     }
1015 }
1016 "#,
1017             r#"
1018 fn foo(opt: Option<i32>) {
1019     match opt {
1020         Some(${0:_}) => todo!(),
1021         None => todo!(),
1022     }
1023 }
1024 "#,
1025         );
1026     }
1027
1028     #[test]
1029     fn works_inside_macro_call() {
1030         check_assist(
1031             fill_match_arms,
1032             r#"
1033 macro_rules! m { ($expr:expr) => {$expr}}
1034 enum Test {
1035     A,
1036     B,
1037     C,
1038 }
1039
1040 fn foo(t: Test) {
1041     m!(match t$0 {});
1042 }"#,
1043             r#"
1044 macro_rules! m { ($expr:expr) => {$expr}}
1045 enum Test {
1046     A,
1047     B,
1048     C,
1049 }
1050
1051 fn foo(t: Test) {
1052     m!(match t {
1053     $0Test::A => todo!(),
1054     Test::B => todo!(),
1055     Test::C => todo!(),
1056 });
1057 }"#,
1058         );
1059     }
1060
1061     #[test]
1062     fn lazy_computation() {
1063         // Computing a single missing arm is enough to determine applicability of the assist.
1064         cov_mark::check_count!(fill_match_arms_lazy_computation, 1);
1065         check_assist_unresolved(
1066             fill_match_arms,
1067             r#"
1068 enum A { One, Two, }
1069 fn foo(tuple: (A, A)) {
1070     match $0tuple {};
1071 }
1072 "#,
1073         );
1074     }
1075
1076     #[test]
1077     fn adds_comma_before_new_arms() {
1078         check_assist(
1079             fill_match_arms,
1080             r#"
1081 fn foo(t: bool) {
1082     match $0t {
1083         true => 1 + 2
1084     }
1085 }"#,
1086             r#"
1087 fn foo(t: bool) {
1088     match t {
1089         true => 1 + 2,
1090         $0false => todo!(),
1091     }
1092 }"#,
1093         );
1094     }
1095
1096     #[test]
1097     fn does_not_add_extra_comma() {
1098         check_assist(
1099             fill_match_arms,
1100             r#"
1101 fn foo(t: bool) {
1102     match $0t {
1103         true => 1 + 2,
1104     }
1105 }"#,
1106             r#"
1107 fn foo(t: bool) {
1108     match t {
1109         true => 1 + 2,
1110         $0false => todo!(),
1111     }
1112 }"#,
1113         );
1114     }
1115 }