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