]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/fill_match_arms.rs
Test `fill_match_arms` for lazy computation.
[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 } => {}
35 //         Action::Stop => {}
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::expr_empty_block()))
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(|x| ExtendedVariant::Variant(x)).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 ide_db::helpers::FamousDefs;
282
283     use crate::tests::{
284         check_assist, check_assist_not_applicable, check_assist_target, check_assist_unresolved,
285     };
286
287     use super::fill_match_arms;
288
289     #[test]
290     fn all_match_arms_provided() {
291         check_assist_not_applicable(
292             fill_match_arms,
293             r#"
294 enum A {
295     As,
296     Bs{x:i32, y:Option<i32>},
297     Cs(i32, Option<i32>),
298 }
299 fn main() {
300     match A::As$0 {
301         A::As,
302         A::Bs{x,y:Some(_)} => {}
303         A::Cs(_, Some(_)) => {}
304     }
305 }
306             "#,
307         );
308     }
309
310     #[test]
311     fn all_boolean_match_arms_provided() {
312         check_assist_not_applicable(
313             fill_match_arms,
314             r#"
315 fn foo(a: bool) {
316     match a$0 {
317         true => {}
318         false => {}
319     }
320 }
321 "#,
322         )
323     }
324
325     #[test]
326     fn tuple_of_non_enum() {
327         // for now this case is not handled, although it potentially could be
328         // in the future
329         check_assist_not_applicable(
330             fill_match_arms,
331             r#"
332 fn main() {
333     match (0, false)$0 {
334     }
335 }
336 "#,
337         );
338     }
339
340     #[test]
341     fn fill_match_arms_boolean() {
342         check_assist(
343             fill_match_arms,
344             r#"
345 fn foo(a: bool) {
346     match a$0 {
347     }
348 }
349 "#,
350             r#"
351 fn foo(a: bool) {
352     match a {
353         $0true => {}
354         false => {}
355     }
356 }
357 "#,
358         )
359     }
360
361     #[test]
362     fn partial_fill_boolean() {
363         check_assist(
364             fill_match_arms,
365             r#"
366 fn foo(a: bool) {
367     match a$0 {
368         true => {}
369     }
370 }
371 "#,
372             r#"
373 fn foo(a: bool) {
374     match a {
375         true => {}
376         $0false => {}
377     }
378 }
379 "#,
380         )
381     }
382
383     #[test]
384     fn all_boolean_tuple_arms_provided() {
385         check_assist_not_applicable(
386             fill_match_arms,
387             r#"
388 fn foo(a: bool) {
389     match (a, a)$0 {
390         (true, true) => {}
391         (true, false) => {}
392         (false, true) => {}
393         (false, false) => {}
394     }
395 }
396 "#,
397         )
398     }
399
400     #[test]
401     fn fill_boolean_tuple() {
402         check_assist(
403             fill_match_arms,
404             r#"
405 fn foo(a: bool) {
406     match (a, a)$0 {
407     }
408 }
409 "#,
410             r#"
411 fn foo(a: bool) {
412     match (a, a) {
413         $0(true, true) => {}
414         (true, false) => {}
415         (false, true) => {}
416         (false, false) => {}
417     }
418 }
419 "#,
420         )
421     }
422
423     #[test]
424     fn partial_fill_boolean_tuple() {
425         check_assist(
426             fill_match_arms,
427             r#"
428 fn foo(a: bool) {
429     match (a, a)$0 {
430         (false, true) => {}
431     }
432 }
433 "#,
434             r#"
435 fn foo(a: bool) {
436     match (a, a) {
437         (false, true) => {}
438         $0(true, true) => {}
439         (true, false) => {}
440         (false, false) => {}
441     }
442 }
443 "#,
444         )
445     }
446
447     #[test]
448     fn partial_fill_record_tuple() {
449         check_assist(
450             fill_match_arms,
451             r#"
452 enum A {
453     As,
454     Bs { x: i32, y: Option<i32> },
455     Cs(i32, Option<i32>),
456 }
457 fn main() {
458     match A::As$0 {
459         A::Bs { x, y: Some(_) } => {}
460         A::Cs(_, Some(_)) => {}
461     }
462 }
463 "#,
464             r#"
465 enum A {
466     As,
467     Bs { x: i32, y: Option<i32> },
468     Cs(i32, Option<i32>),
469 }
470 fn main() {
471     match A::As {
472         A::Bs { x, y: Some(_) } => {}
473         A::Cs(_, Some(_)) => {}
474         $0A::As => {}
475     }
476 }
477 "#,
478         );
479     }
480
481     #[test]
482     fn partial_fill_option() {
483         check_assist(
484             fill_match_arms,
485             r#"
486 enum Option<T> { Some(T), None }
487 use Option::*;
488
489 fn main() {
490     match None$0 {
491         None => {}
492     }
493 }
494             "#,
495             r#"
496 enum Option<T> { Some(T), None }
497 use Option::*;
498
499 fn main() {
500     match None {
501         None => {}
502         Some(${0:_}) => {}
503     }
504 }
505             "#,
506         );
507     }
508
509     #[test]
510     fn partial_fill_or_pat() {
511         check_assist(
512             fill_match_arms,
513             r#"
514 enum A { As, Bs, Cs(Option<i32>) }
515 fn main() {
516     match A::As$0 {
517         A::Cs(_) | A::Bs => {}
518     }
519 }
520 "#,
521             r#"
522 enum A { As, Bs, Cs(Option<i32>) }
523 fn main() {
524     match A::As {
525         A::Cs(_) | A::Bs => {}
526         $0A::As => {}
527     }
528 }
529 "#,
530         );
531     }
532
533     #[test]
534     fn partial_fill() {
535         check_assist(
536             fill_match_arms,
537             r#"
538 enum A { As, Bs, Cs, Ds(String), Es(B) }
539 enum B { Xs, Ys }
540 fn main() {
541     match A::As$0 {
542         A::Bs if 0 < 1 => {}
543         A::Ds(_value) => { let x = 1; }
544         A::Es(B::Xs) => (),
545     }
546 }
547 "#,
548             r#"
549 enum A { As, Bs, Cs, Ds(String), Es(B) }
550 enum B { Xs, Ys }
551 fn main() {
552     match A::As {
553         A::Bs if 0 < 1 => {}
554         A::Ds(_value) => { let x = 1; }
555         A::Es(B::Xs) => (),
556         $0A::As => {}
557         A::Cs => {}
558     }
559 }
560 "#,
561         );
562     }
563
564     #[test]
565     fn partial_fill_bind_pat() {
566         check_assist(
567             fill_match_arms,
568             r#"
569 enum A { As, Bs, Cs(Option<i32>) }
570 fn main() {
571     match A::As$0 {
572         A::As(_) => {}
573         a @ A::Bs(_) => {}
574     }
575 }
576 "#,
577             r#"
578 enum A { As, Bs, Cs(Option<i32>) }
579 fn main() {
580     match A::As {
581         A::As(_) => {}
582         a @ A::Bs(_) => {}
583         A::Cs(${0:_}) => {}
584     }
585 }
586 "#,
587         );
588     }
589
590     #[test]
591     fn fill_match_arms_empty_body() {
592         check_assist(
593             fill_match_arms,
594             r#"
595 enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
596
597 fn main() {
598     let a = A::As;
599     match a$0 {}
600 }
601 "#,
602             r#"
603 enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
604
605 fn main() {
606     let a = A::As;
607     match a {
608         $0A::As => {}
609         A::Bs => {}
610         A::Cs(_) => {}
611         A::Ds(_, _) => {}
612         A::Es { x, y } => {}
613     }
614 }
615 "#,
616         );
617     }
618
619     #[test]
620     fn fill_match_arms_tuple_of_enum() {
621         check_assist(
622             fill_match_arms,
623             r#"
624 enum A { One, Two }
625 enum B { One, Two }
626
627 fn main() {
628     let a = A::One;
629     let b = B::One;
630     match (a$0, b) {}
631 }
632 "#,
633             r#"
634 enum A { One, Two }
635 enum B { One, Two }
636
637 fn main() {
638     let a = A::One;
639     let b = B::One;
640     match (a, b) {
641         $0(A::One, B::One) => {}
642         (A::One, B::Two) => {}
643         (A::Two, B::One) => {}
644         (A::Two, B::Two) => {}
645     }
646 }
647 "#,
648         );
649     }
650
651     #[test]
652     fn fill_match_arms_tuple_of_enum_ref() {
653         check_assist(
654             fill_match_arms,
655             r#"
656 enum A { One, Two }
657 enum B { One, Two }
658
659 fn main() {
660     let a = A::One;
661     let b = B::One;
662     match (&a$0, &b) {}
663 }
664 "#,
665             r#"
666 enum A { One, Two }
667 enum B { One, Two }
668
669 fn main() {
670     let a = A::One;
671     let b = B::One;
672     match (&a, &b) {
673         $0(A::One, B::One) => {}
674         (A::One, B::Two) => {}
675         (A::Two, B::One) => {}
676         (A::Two, B::Two) => {}
677     }
678 }
679 "#,
680         );
681     }
682
683     #[test]
684     fn fill_match_arms_tuple_of_enum_partial() {
685         check_assist(
686             fill_match_arms,
687             r#"
688 enum A { One, Two }
689 enum B { One, Two }
690
691 fn main() {
692     let a = A::One;
693     let b = B::One;
694     match (a$0, b) {
695         (A::Two, B::One) => {}
696     }
697 }
698 "#,
699             r#"
700 enum A { One, Two }
701 enum B { One, Two }
702
703 fn main() {
704     let a = A::One;
705     let b = B::One;
706     match (a, b) {
707         (A::Two, B::One) => {}
708         $0(A::One, B::One) => {}
709         (A::One, B::Two) => {}
710         (A::Two, B::Two) => {}
711     }
712 }
713 "#,
714         );
715     }
716
717     #[test]
718     fn fill_match_arms_tuple_of_enum_partial_with_wildcards() {
719         let ra_fixture = r#"
720 fn main() {
721     let a = Some(1);
722     let b = Some(());
723     match (a$0, b) {
724         (Some(_), _) => {}
725         (None, Some(_)) => {}
726     }
727 }
728 "#;
729         check_assist(
730             fill_match_arms,
731             &format!("//- /main.rs crate:main deps:core{}{}", ra_fixture, FamousDefs::FIXTURE),
732             r#"
733 fn main() {
734     let a = Some(1);
735     let b = Some(());
736     match (a, b) {
737         (Some(_), _) => {}
738         (None, Some(_)) => {}
739         $0(None, None) => {}
740     }
741 }
742 "#,
743         );
744     }
745
746     #[test]
747     fn fill_match_arms_partial_with_deep_pattern() {
748         // Fixme: cannot handle deep patterns
749         let ra_fixture = r#"
750 fn main() {
751     match $0Some(true) {
752         Some(true) => {}
753         None => {}
754     }
755 }
756 "#;
757         check_assist_not_applicable(
758             fill_match_arms,
759             &format!("//- /main.rs crate:main deps:core{}{}", ra_fixture, FamousDefs::FIXTURE),
760         );
761     }
762
763     #[test]
764     fn fill_match_arms_tuple_of_enum_not_applicable() {
765         check_assist_not_applicable(
766             fill_match_arms,
767             r#"
768 enum A { One, Two }
769 enum B { One, Two }
770
771 fn main() {
772     let a = A::One;
773     let b = B::One;
774     match (a$0, b) {
775         (A::Two, B::One) => {}
776         (A::One, B::One) => {}
777         (A::One, B::Two) => {}
778         (A::Two, B::Two) => {}
779     }
780 }
781 "#,
782         );
783     }
784
785     #[test]
786     fn fill_match_arms_single_element_tuple_of_enum() {
787         check_assist(
788             fill_match_arms,
789             r#"
790 enum A { One, Two }
791
792 fn main() {
793     let a = A::One;
794     match (a$0, ) {
795     }
796 }
797 "#,
798             r#"
799 enum A { One, Two }
800
801 fn main() {
802     let a = A::One;
803     match (a, ) {
804         $0(A::One,) => {}
805         (A::Two,) => {}
806     }
807 }
808 "#,
809         );
810     }
811
812     #[test]
813     fn test_fill_match_arm_refs() {
814         check_assist(
815             fill_match_arms,
816             r#"
817 enum A { As }
818
819 fn foo(a: &A) {
820     match a$0 {
821     }
822 }
823 "#,
824             r#"
825 enum A { As }
826
827 fn foo(a: &A) {
828     match a {
829         $0A::As => {}
830     }
831 }
832 "#,
833         );
834
835         check_assist(
836             fill_match_arms,
837             r#"
838 enum A {
839     Es { x: usize, y: usize }
840 }
841
842 fn foo(a: &mut A) {
843     match a$0 {
844     }
845 }
846 "#,
847             r#"
848 enum A {
849     Es { x: usize, y: usize }
850 }
851
852 fn foo(a: &mut A) {
853     match a {
854         $0A::Es { x, y } => {}
855     }
856 }
857 "#,
858         );
859     }
860
861     #[test]
862     fn fill_match_arms_target() {
863         check_assist_target(
864             fill_match_arms,
865             r#"
866 enum E { X, Y }
867
868 fn main() {
869     match E::X$0 {}
870 }
871 "#,
872             "match E::X {}",
873         );
874     }
875
876     #[test]
877     fn fill_match_arms_trivial_arm() {
878         check_assist(
879             fill_match_arms,
880             r#"
881 enum E { X, Y }
882
883 fn main() {
884     match E::X {
885         $0_ => {}
886     }
887 }
888 "#,
889             r#"
890 enum E { X, Y }
891
892 fn main() {
893     match E::X {
894         $0E::X => {}
895         E::Y => {}
896     }
897 }
898 "#,
899         );
900     }
901
902     #[test]
903     fn fill_match_arms_qualifies_path() {
904         check_assist(
905             fill_match_arms,
906             r#"
907 mod foo { pub enum E { X, Y } }
908 use foo::E::X;
909
910 fn main() {
911     match X {
912         $0
913     }
914 }
915 "#,
916             r#"
917 mod foo { pub enum E { X, Y } }
918 use foo::E::X;
919
920 fn main() {
921     match X {
922         $0X => {}
923         foo::E::Y => {}
924     }
925 }
926 "#,
927         );
928     }
929
930     #[test]
931     fn fill_match_arms_preserves_comments() {
932         check_assist(
933             fill_match_arms,
934             r#"
935 enum A { One, Two }
936 fn foo(a: A) {
937     match a {
938         // foo bar baz$0
939         A::One => {}
940         // This is where the rest should be
941     }
942 }
943 "#,
944             r#"
945 enum A { One, Two }
946 fn foo(a: A) {
947     match a {
948         // foo bar baz
949         A::One => {}
950         $0A::Two => {}
951         // This is where the rest should be
952     }
953 }
954 "#,
955         );
956     }
957
958     #[test]
959     fn fill_match_arms_preserves_comments_empty() {
960         check_assist(
961             fill_match_arms,
962             r#"
963 enum A { One, Two }
964 fn foo(a: A) {
965     match a {
966         // foo bar baz$0
967     }
968 }
969 "#,
970             r#"
971 enum A { One, Two }
972 fn foo(a: A) {
973     match a {
974         $0A::One => {}
975         A::Two => {}
976         // foo bar baz
977     }
978 }
979 "#,
980         );
981     }
982
983     #[test]
984     fn fill_match_arms_placeholder() {
985         check_assist(
986             fill_match_arms,
987             r#"
988 enum A { One, Two, }
989 fn foo(a: A) {
990     match a$0 {
991         _ => (),
992     }
993 }
994 "#,
995             r#"
996 enum A { One, Two, }
997 fn foo(a: A) {
998     match a {
999         $0A::One => {}
1000         A::Two => {}
1001     }
1002 }
1003 "#,
1004         );
1005     }
1006
1007     #[test]
1008     fn option_order() {
1009         cov_mark::check!(option_order);
1010         let before = r#"
1011 fn foo(opt: Option<i32>) {
1012     match opt$0 {
1013     }
1014 }
1015 "#;
1016         let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
1017
1018         check_assist(
1019             fill_match_arms,
1020             before,
1021             r#"
1022 fn foo(opt: Option<i32>) {
1023     match opt {
1024         Some(${0:_}) => {}
1025         None => {}
1026     }
1027 }
1028 "#,
1029         );
1030     }
1031
1032     #[test]
1033     fn works_inside_macro_call() {
1034         check_assist(
1035             fill_match_arms,
1036             r#"
1037 macro_rules! m { ($expr:expr) => {$expr}}
1038 enum Test {
1039     A,
1040     B,
1041     C,
1042 }
1043
1044 fn foo(t: Test) {
1045     m!(match t$0 {});
1046 }"#,
1047             r#"
1048 macro_rules! m { ($expr:expr) => {$expr}}
1049 enum Test {
1050     A,
1051     B,
1052     C,
1053 }
1054
1055 fn foo(t: Test) {
1056     m!(match t {
1057     $0Test::A => {}
1058     Test::B => {}
1059     Test::C => {}
1060 });
1061 }"#,
1062         );
1063     }
1064
1065     #[test]
1066     fn lazy_computation() {
1067         // Computing a single missing arm is enough to determine applicability of the assist.
1068         cov_mark::check_count!(fill_match_arms_lazy_computation, 1);
1069         check_assist_unresolved(
1070             fill_match_arms,
1071             r#"
1072 enum A { One, Two, }
1073 fn foo(tuple: (A, A)) {
1074     match $0tuple {};
1075 }
1076 "#,
1077         );
1078     }
1079 }