]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
Auto merge of #103913 - Neutron3529:patch-1, r=thomcc
[rust.git] / src / tools / rust-analyzer / 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, Crate, HasAttrs, HasSource, ModuleDef, Semantics};
5 use ide_db::RootDatabase;
6 use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast};
7 use itertools::Itertools;
8 use syntax::ast::edit_in_place::Removable;
9 use syntax::ast::{self, make, AstNode, HasName, MatchArmList, MatchExpr, Pat};
10
11 use crate::{
12     utils::{self, render_snippet, Cursor},
13     AssistContext, AssistId, AssistKind, Assists,
14 };
15
16 // Assist: add_missing_match_arms
17 //
18 // Adds missing clauses to a `match` expression.
19 //
20 // ```
21 // enum Action { Move { distance: u32 }, Stop }
22 //
23 // fn handle(action: Action) {
24 //     match action {
25 //         $0
26 //     }
27 // }
28 // ```
29 // ->
30 // ```
31 // enum Action { Move { distance: u32 }, Stop }
32 //
33 // fn handle(action: Action) {
34 //     match action {
35 //         $0Action::Move { distance } => todo!(),
36 //         Action::Stop => todo!(),
37 //     }
38 // }
39 // ```
40 pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
41     let match_expr = ctx.find_node_at_offset_with_descend::<ast::MatchExpr>()?;
42     let match_arm_list = match_expr.match_arm_list()?;
43     let target_range = ctx.sema.original_range(match_expr.syntax()).range;
44
45     if let None = cursor_at_trivial_match_arm_list(ctx, &match_expr, &match_arm_list) {
46         let arm_list_range = ctx.sema.original_range(match_arm_list.syntax()).range;
47         let cursor_in_range = arm_list_range.contains_range(ctx.selection_trimmed());
48         if cursor_in_range {
49             cov_mark::hit!(not_applicable_outside_of_range_right);
50             return None;
51         }
52     }
53
54     let expr = match_expr.expr()?;
55
56     let mut has_catch_all_arm = false;
57
58     let top_lvl_pats: Vec<_> = match_arm_list
59         .arms()
60         .filter_map(|arm| Some((arm.pat()?, arm.guard().is_some())))
61         .flat_map(|(pat, has_guard)| {
62             match pat {
63                 // Special case OrPat as separate top-level pats
64                 Pat::OrPat(or_pat) => Either::Left(or_pat.pats()),
65                 _ => Either::Right(iter::once(pat)),
66             }
67             .map(move |pat| (pat, has_guard))
68         })
69         .map(|(pat, has_guard)| {
70             has_catch_all_arm |= !has_guard && matches!(pat, Pat::WildcardPat(_));
71             pat
72         })
73         // Exclude top level wildcards so that they are expanded by this assist, retains status quo in #8129.
74         .filter(|pat| !matches!(pat, Pat::WildcardPat(_)))
75         .collect();
76
77     let module = ctx.sema.scope(expr.syntax())?.module();
78     let (mut missing_pats, is_non_exhaustive): (
79         Peekable<Box<dyn Iterator<Item = (ast::Pat, bool)>>>,
80         bool,
81     ) = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
82         let is_non_exhaustive = enum_def.is_non_exhaustive(ctx.db(), module.krate());
83
84         let variants = enum_def.variants(ctx.db());
85
86         let missing_pats = variants
87             .into_iter()
88             .filter_map(|variant| {
89                 Some((
90                     build_pat(ctx.db(), module, variant, ctx.config.prefer_no_std)?,
91                     variant.should_be_hidden(ctx.db(), module.krate()),
92                 ))
93             })
94             .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
95
96         let option_enum = FamousDefs(&ctx.sema, module.krate()).core_option_Option().map(lift_enum);
97         let missing_pats: Box<dyn Iterator<Item = _>> = if Some(enum_def) == option_enum {
98             // Match `Some` variant first.
99             cov_mark::hit!(option_order);
100             Box::new(missing_pats.rev())
101         } else {
102             Box::new(missing_pats)
103         };
104         (missing_pats.peekable(), is_non_exhaustive)
105     } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
106         let is_non_exhaustive =
107             enum_defs.iter().any(|enum_def| enum_def.is_non_exhaustive(ctx.db(), module.krate()));
108
109         let mut n_arms = 1;
110         let variants_of_enums: Vec<Vec<ExtendedVariant>> = enum_defs
111             .into_iter()
112             .map(|enum_def| enum_def.variants(ctx.db()))
113             .inspect(|variants| n_arms *= variants.len())
114             .collect();
115
116         // When calculating the match arms for a tuple of enums, we want
117         // to create a match arm for each possible combination of enum
118         // values. The `multi_cartesian_product` method transforms
119         // Vec<Vec<EnumVariant>> into Vec<(EnumVariant, .., EnumVariant)>
120         // where each tuple represents a proposed match arm.
121
122         // A number of arms grows very fast on even a small tuple of large enums.
123         // We skip the assist beyond an arbitrary threshold.
124         if n_arms > 256 {
125             return None;
126         }
127         let missing_pats = variants_of_enums
128             .into_iter()
129             .multi_cartesian_product()
130             .inspect(|_| cov_mark::hit!(add_missing_match_arms_lazy_computation))
131             .map(|variants| {
132                 let is_hidden = variants
133                     .iter()
134                     .any(|variant| variant.should_be_hidden(ctx.db(), module.krate()));
135                 let patterns = variants.into_iter().filter_map(|variant| {
136                     build_pat(ctx.db(), module, variant, ctx.config.prefer_no_std)
137                 });
138
139                 (ast::Pat::from(make::tuple_pat(patterns)), is_hidden)
140             })
141             .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
142         ((Box::new(missing_pats) as Box<dyn Iterator<Item = _>>).peekable(), is_non_exhaustive)
143     } else {
144         return None;
145     };
146
147     let mut needs_catch_all_arm = is_non_exhaustive && !has_catch_all_arm;
148
149     if !needs_catch_all_arm && missing_pats.peek().is_none() {
150         return None;
151     }
152
153     acc.add(
154         AssistId("add_missing_match_arms", AssistKind::QuickFix),
155         "Fill match arms",
156         target_range,
157         |builder| {
158             let new_match_arm_list = match_arm_list.clone_for_update();
159             let missing_arms = missing_pats
160                 .map(|(pat, hidden)| {
161                     (make::match_arm(iter::once(pat), None, make::ext::expr_todo()), hidden)
162                 })
163                 .map(|(it, hidden)| (it.clone_for_update(), hidden));
164
165             let catch_all_arm = new_match_arm_list
166                 .arms()
167                 .find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_))));
168             if let Some(arm) = catch_all_arm {
169                 let is_empty_expr = arm.expr().map_or(true, |e| match e {
170                     ast::Expr::BlockExpr(b) => {
171                         b.statements().next().is_none() && b.tail_expr().is_none()
172                     }
173                     ast::Expr::TupleExpr(t) => t.fields().next().is_none(),
174                     _ => false,
175                 });
176                 if is_empty_expr {
177                     arm.remove();
178                 } else {
179                     cov_mark::hit!(add_missing_match_arms_empty_expr);
180                 }
181             }
182             let mut first_new_arm = None;
183             for (arm, hidden) in missing_arms {
184                 if hidden {
185                     needs_catch_all_arm = !has_catch_all_arm;
186                 } else {
187                     first_new_arm.get_or_insert_with(|| arm.clone());
188                     new_match_arm_list.add_arm(arm);
189                 }
190             }
191             if needs_catch_all_arm && !has_catch_all_arm {
192                 cov_mark::hit!(added_wildcard_pattern);
193                 let arm = make::match_arm(
194                     iter::once(make::wildcard_pat().into()),
195                     None,
196                     make::ext::expr_todo(),
197                 )
198                 .clone_for_update();
199                 first_new_arm.get_or_insert_with(|| arm.clone());
200                 new_match_arm_list.add_arm(arm);
201             }
202
203             let old_range = ctx.sema.original_range(match_arm_list.syntax()).range;
204             match (first_new_arm, ctx.config.snippet_cap) {
205                 (Some(first_new_arm), Some(cap)) => {
206                     let extend_lifetime;
207                     let cursor =
208                         match first_new_arm.syntax().descendants().find_map(ast::WildcardPat::cast)
209                         {
210                             Some(it) => {
211                                 extend_lifetime = it.syntax().clone();
212                                 Cursor::Replace(&extend_lifetime)
213                             }
214                             None => Cursor::Before(first_new_arm.syntax()),
215                         };
216                     let snippet = render_snippet(cap, new_match_arm_list.syntax(), cursor);
217                     builder.replace_snippet(cap, old_range, snippet);
218                 }
219                 _ => builder.replace(old_range, new_match_arm_list.to_string()),
220             }
221         },
222     )
223 }
224
225 fn cursor_at_trivial_match_arm_list(
226     ctx: &AssistContext<'_>,
227     match_expr: &MatchExpr,
228     match_arm_list: &MatchArmList,
229 ) -> Option<()> {
230     // match x { $0 }
231     if match_arm_list.arms().next() == None {
232         cov_mark::hit!(add_missing_match_arms_empty_body);
233         return Some(());
234     }
235
236     // match x {
237     //     bar => baz,
238     //     $0
239     // }
240     if let Some(last_arm) = match_arm_list.arms().last() {
241         let last_arm_range = last_arm.syntax().text_range();
242         let match_expr_range = match_expr.syntax().text_range();
243         if last_arm_range.end() <= ctx.offset() && ctx.offset() < match_expr_range.end() {
244             cov_mark::hit!(add_missing_match_arms_end_of_last_arm);
245             return Some(());
246         }
247     }
248
249     // match { _$0 => {...} }
250     let wild_pat = ctx.find_node_at_offset_with_descend::<ast::WildcardPat>()?;
251     let arm = wild_pat.syntax().parent().and_then(ast::MatchArm::cast)?;
252     let arm_match_expr = arm.syntax().ancestors().nth(2).and_then(ast::MatchExpr::cast)?;
253     if arm_match_expr == *match_expr {
254         cov_mark::hit!(add_missing_match_arms_trivial_arm);
255         return Some(());
256     }
257
258     None
259 }
260
261 fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool {
262     !existing_pats.iter().any(|pat| does_pat_match_variant(pat, var))
263 }
264
265 // Fixme: this is still somewhat limited, use hir_ty::diagnostics::match_check?
266 fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
267     match (pat, var) {
268         (Pat::WildcardPat(_), _) => true,
269         (Pat::TuplePat(tpat), Pat::TuplePat(tvar)) => {
270             tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v))
271         }
272         _ => utils::does_pat_match_variant(pat, var),
273     }
274 }
275
276 #[derive(Eq, PartialEq, Clone, Copy)]
277 enum ExtendedEnum {
278     Bool,
279     Enum(hir::Enum),
280 }
281
282 #[derive(Eq, PartialEq, Clone, Copy)]
283 enum ExtendedVariant {
284     True,
285     False,
286     Variant(hir::Variant),
287 }
288
289 impl ExtendedVariant {
290     fn should_be_hidden(self, db: &RootDatabase, krate: Crate) -> bool {
291         match self {
292             ExtendedVariant::Variant(var) => {
293                 var.attrs(db).has_doc_hidden() && var.module(db).krate() != krate
294             }
295             _ => false,
296         }
297     }
298 }
299
300 fn lift_enum(e: hir::Enum) -> ExtendedEnum {
301     ExtendedEnum::Enum(e)
302 }
303
304 impl ExtendedEnum {
305     fn is_non_exhaustive(self, db: &RootDatabase, krate: Crate) -> bool {
306         match self {
307             ExtendedEnum::Enum(e) => {
308                 e.attrs(db).by_key("non_exhaustive").exists() && e.module(db).krate() != krate
309             }
310             _ => false,
311         }
312     }
313
314     fn variants(self, db: &RootDatabase) -> Vec<ExtendedVariant> {
315         match self {
316             ExtendedEnum::Enum(e) => {
317                 e.variants(db).into_iter().map(ExtendedVariant::Variant).collect::<Vec<_>>()
318             }
319             ExtendedEnum::Bool => {
320                 Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False])
321             }
322         }
323     }
324 }
325
326 fn resolve_enum_def(sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
327     sema.type_of_expr(expr)?.adjusted().autoderef(sema.db).find_map(|ty| match ty.as_adt() {
328         Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
329         _ => ty.is_bool().then(|| ExtendedEnum::Bool),
330     })
331 }
332
333 fn resolve_tuple_of_enum_def(
334     sema: &Semantics<'_, RootDatabase>,
335     expr: &ast::Expr,
336 ) -> Option<Vec<ExtendedEnum>> {
337     sema.type_of_expr(expr)?
338         .adjusted()
339         .tuple_fields(sema.db)
340         .iter()
341         .map(|ty| {
342             ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
343                 Some(Adt::Enum(e)) => Some(lift_enum(e)),
344                 // For now we only handle expansion for a tuple of enums. Here
345                 // we map non-enum items to None and rely on `collect` to
346                 // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
347                 _ => ty.is_bool().then(|| ExtendedEnum::Bool),
348             })
349         })
350         .collect()
351 }
352
353 fn build_pat(
354     db: &RootDatabase,
355     module: hir::Module,
356     var: ExtendedVariant,
357     prefer_no_std: bool,
358 ) -> Option<ast::Pat> {
359     match var {
360         ExtendedVariant::Variant(var) => {
361             let path =
362                 mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var), prefer_no_std)?);
363
364             // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
365             let pat: ast::Pat = match var.source(db)?.value.kind() {
366                 ast::StructKind::Tuple(field_list) => {
367                     let pats =
368                         iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
369                     make::tuple_struct_pat(path, pats).into()
370                 }
371                 ast::StructKind::Record(field_list) => {
372                     let pats = field_list
373                         .fields()
374                         .map(|f| make::ext::simple_ident_pat(f.name().unwrap()).into());
375                     make::record_pat(path, pats).into()
376                 }
377                 ast::StructKind::Unit => make::path_pat(path),
378             };
379
380             Some(pat)
381         }
382         ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))),
383         ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))),
384     }
385 }
386
387 #[cfg(test)]
388 mod tests {
389     use crate::tests::{
390         check_assist, check_assist_not_applicable, check_assist_target, check_assist_unresolved,
391     };
392
393     use super::add_missing_match_arms;
394
395     #[test]
396     fn all_match_arms_provided() {
397         check_assist_not_applicable(
398             add_missing_match_arms,
399             r#"
400 enum A {
401     As,
402     Bs{x:i32, y:Option<i32>},
403     Cs(i32, Option<i32>),
404 }
405 fn main() {
406     match A::As$0 {
407         A::As,
408         A::Bs{x,y:Some(_)} => {}
409         A::Cs(_, Some(_)) => {}
410     }
411 }
412             "#,
413         );
414     }
415
416     #[test]
417     fn not_applicable_outside_of_range_left() {
418         check_assist_not_applicable(
419             add_missing_match_arms,
420             r#"
421 enum A { X, Y }
422
423 fn foo(a: A) {
424     $0 match a {
425         A::X => { }
426     }
427 }
428         "#,
429         );
430     }
431
432     #[test]
433     fn not_applicable_outside_of_range_right() {
434         cov_mark::check!(not_applicable_outside_of_range_right);
435         check_assist_not_applicable(
436             add_missing_match_arms,
437             r#"
438 enum A { X, Y }
439
440 fn foo(a: A) {
441     match a {$0
442         A::X => { }
443     }
444 }
445         "#,
446         );
447     }
448
449     #[test]
450     fn all_boolean_match_arms_provided() {
451         check_assist_not_applicable(
452             add_missing_match_arms,
453             r#"
454 fn foo(a: bool) {
455     match a$0 {
456         true => {}
457         false => {}
458     }
459 }
460 "#,
461         )
462     }
463
464     #[test]
465     fn tuple_of_non_enum() {
466         // for now this case is not handled, although it potentially could be
467         // in the future
468         check_assist_not_applicable(
469             add_missing_match_arms,
470             r#"
471 fn main() {
472     match (0, false)$0 {
473     }
474 }
475 "#,
476         );
477     }
478
479     #[test]
480     fn add_missing_match_arms_boolean() {
481         check_assist(
482             add_missing_match_arms,
483             r#"
484 fn foo(a: bool) {
485     match a$0 {
486     }
487 }
488 "#,
489             r#"
490 fn foo(a: bool) {
491     match a {
492         $0true => todo!(),
493         false => todo!(),
494     }
495 }
496 "#,
497         )
498     }
499
500     #[test]
501     fn partial_fill_boolean() {
502         check_assist(
503             add_missing_match_arms,
504             r#"
505 fn foo(a: bool) {
506     match a$0 {
507         true => {}
508     }
509 }
510 "#,
511             r#"
512 fn foo(a: bool) {
513     match a {
514         true => {}
515         $0false => todo!(),
516     }
517 }
518 "#,
519         )
520     }
521
522     #[test]
523     fn all_boolean_tuple_arms_provided() {
524         check_assist_not_applicable(
525             add_missing_match_arms,
526             r#"
527 fn foo(a: bool) {
528     match (a, a)$0 {
529         (true, true) => {}
530         (true, false) => {}
531         (false, true) => {}
532         (false, false) => {}
533     }
534 }
535 "#,
536         )
537     }
538
539     #[test]
540     fn fill_boolean_tuple() {
541         check_assist(
542             add_missing_match_arms,
543             r#"
544 fn foo(a: bool) {
545     match (a, a)$0 {
546     }
547 }
548 "#,
549             r#"
550 fn foo(a: bool) {
551     match (a, a) {
552         $0(true, true) => todo!(),
553         (true, false) => todo!(),
554         (false, true) => todo!(),
555         (false, false) => todo!(),
556     }
557 }
558 "#,
559         )
560     }
561
562     #[test]
563     fn partial_fill_boolean_tuple() {
564         check_assist(
565             add_missing_match_arms,
566             r#"
567 fn foo(a: bool) {
568     match (a, a)$0 {
569         (false, true) => {}
570     }
571 }
572 "#,
573             r#"
574 fn foo(a: bool) {
575     match (a, a) {
576         (false, true) => {}
577         $0(true, true) => todo!(),
578         (true, false) => todo!(),
579         (false, false) => todo!(),
580     }
581 }
582 "#,
583         )
584     }
585
586     #[test]
587     fn partial_fill_record_tuple() {
588         check_assist(
589             add_missing_match_arms,
590             r#"
591 enum A {
592     As,
593     Bs { x: i32, y: Option<i32> },
594     Cs(i32, Option<i32>),
595 }
596 fn main() {
597     match A::As$0 {
598         A::Bs { x, y: Some(_) } => {}
599         A::Cs(_, Some(_)) => {}
600     }
601 }
602 "#,
603             r#"
604 enum A {
605     As,
606     Bs { x: i32, y: Option<i32> },
607     Cs(i32, Option<i32>),
608 }
609 fn main() {
610     match A::As {
611         A::Bs { x, y: Some(_) } => {}
612         A::Cs(_, Some(_)) => {}
613         $0A::As => todo!(),
614     }
615 }
616 "#,
617         );
618     }
619
620     #[test]
621     fn partial_fill_option() {
622         check_assist(
623             add_missing_match_arms,
624             r#"
625 //- minicore: option
626 fn main() {
627     match None$0 {
628         None => {}
629     }
630 }
631 "#,
632             r#"
633 fn main() {
634     match None {
635         None => {}
636         Some(${0:_}) => todo!(),
637     }
638 }
639 "#,
640         );
641     }
642
643     #[test]
644     fn partial_fill_or_pat() {
645         check_assist(
646             add_missing_match_arms,
647             r#"
648 enum A { As, Bs, Cs(Option<i32>) }
649 fn main() {
650     match A::As$0 {
651         A::Cs(_) | A::Bs => {}
652     }
653 }
654 "#,
655             r#"
656 enum A { As, Bs, Cs(Option<i32>) }
657 fn main() {
658     match A::As {
659         A::Cs(_) | A::Bs => {}
660         $0A::As => todo!(),
661     }
662 }
663 "#,
664         );
665     }
666
667     #[test]
668     fn partial_fill() {
669         check_assist(
670             add_missing_match_arms,
671             r#"
672 enum A { As, Bs, Cs, Ds(String), Es(B) }
673 enum B { Xs, Ys }
674 fn main() {
675     match A::As$0 {
676         A::Bs if 0 < 1 => {}
677         A::Ds(_value) => { let x = 1; }
678         A::Es(B::Xs) => (),
679     }
680 }
681 "#,
682             r#"
683 enum A { As, Bs, Cs, Ds(String), Es(B) }
684 enum B { Xs, Ys }
685 fn main() {
686     match A::As {
687         A::Bs if 0 < 1 => {}
688         A::Ds(_value) => { let x = 1; }
689         A::Es(B::Xs) => (),
690         $0A::As => todo!(),
691         A::Cs => todo!(),
692     }
693 }
694 "#,
695         );
696     }
697
698     #[test]
699     fn partial_fill_bind_pat() {
700         check_assist(
701             add_missing_match_arms,
702             r#"
703 enum A { As, Bs, Cs(Option<i32>) }
704 fn main() {
705     match A::As$0 {
706         A::As(_) => {}
707         a @ A::Bs(_) => {}
708     }
709 }
710 "#,
711             r#"
712 enum A { As, Bs, Cs(Option<i32>) }
713 fn main() {
714     match A::As {
715         A::As(_) => {}
716         a @ A::Bs(_) => {}
717         A::Cs(${0:_}) => todo!(),
718     }
719 }
720 "#,
721         );
722     }
723
724     #[test]
725     fn add_missing_match_arms_empty_body() {
726         cov_mark::check!(add_missing_match_arms_empty_body);
727         check_assist(
728             add_missing_match_arms,
729             r#"
730 enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
731
732 fn main() {
733     let a = A::As;
734     match a {$0}
735 }
736 "#,
737             r#"
738 enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
739
740 fn main() {
741     let a = A::As;
742     match a {
743         $0A::As => todo!(),
744         A::Bs => todo!(),
745         A::Cs(_) => todo!(),
746         A::Ds(_, _) => todo!(),
747         A::Es { x, y } => todo!(),
748     }
749 }
750 "#,
751         );
752     }
753
754     #[test]
755     fn add_missing_match_arms_end_of_last_arm() {
756         cov_mark::check!(add_missing_match_arms_end_of_last_arm);
757         check_assist(
758             add_missing_match_arms,
759             r#"
760 enum A { One, Two }
761 enum B { One, Two }
762
763 fn main() {
764     let a = A::One;
765     let b = B::One;
766     match (a, b) {
767         (A::Two, B::One) => {},$0
768     }
769 }
770 "#,
771             r#"
772 enum A { One, Two }
773 enum B { One, Two }
774
775 fn main() {
776     let a = A::One;
777     let b = B::One;
778     match (a, b) {
779         (A::Two, B::One) => {},
780         $0(A::One, B::One) => todo!(),
781         (A::One, B::Two) => todo!(),
782         (A::Two, B::Two) => todo!(),
783     }
784 }
785 "#,
786         );
787     }
788
789     #[test]
790     fn add_missing_match_arms_tuple_of_enum() {
791         check_assist(
792             add_missing_match_arms,
793             r#"
794 enum A { One, Two }
795 enum B { One, Two }
796
797 fn main() {
798     let a = A::One;
799     let b = B::One;
800     match (a$0, b) {}
801 }
802 "#,
803             r#"
804 enum A { One, Two }
805 enum B { One, Two }
806
807 fn main() {
808     let a = A::One;
809     let b = B::One;
810     match (a, b) {
811         $0(A::One, B::One) => todo!(),
812         (A::One, B::Two) => todo!(),
813         (A::Two, B::One) => todo!(),
814         (A::Two, B::Two) => todo!(),
815     }
816 }
817 "#,
818         );
819     }
820
821     #[test]
822     fn add_missing_match_arms_tuple_of_enum_ref() {
823         check_assist(
824             add_missing_match_arms,
825             r#"
826 enum A { One, Two }
827 enum B { One, Two }
828
829 fn main() {
830     let a = A::One;
831     let b = B::One;
832     match (&a$0, &b) {}
833 }
834 "#,
835             r#"
836 enum A { One, Two }
837 enum B { One, Two }
838
839 fn main() {
840     let a = A::One;
841     let b = B::One;
842     match (&a, &b) {
843         $0(A::One, B::One) => todo!(),
844         (A::One, B::Two) => todo!(),
845         (A::Two, B::One) => todo!(),
846         (A::Two, B::Two) => todo!(),
847     }
848 }
849 "#,
850         );
851     }
852
853     #[test]
854     fn add_missing_match_arms_tuple_of_enum_partial() {
855         check_assist(
856             add_missing_match_arms,
857             r#"
858 enum A { One, Two }
859 enum B { One, Two }
860
861 fn main() {
862     let a = A::One;
863     let b = B::One;
864     match (a$0, b) {
865         (A::Two, B::One) => {}
866     }
867 }
868 "#,
869             r#"
870 enum A { One, Two }
871 enum B { One, Two }
872
873 fn main() {
874     let a = A::One;
875     let b = B::One;
876     match (a, b) {
877         (A::Two, B::One) => {}
878         $0(A::One, B::One) => todo!(),
879         (A::One, B::Two) => todo!(),
880         (A::Two, B::Two) => todo!(),
881     }
882 }
883 "#,
884         );
885     }
886
887     #[test]
888     fn add_missing_match_arms_tuple_of_enum_partial_with_wildcards() {
889         check_assist(
890             add_missing_match_arms,
891             r#"
892 //- minicore: option
893 fn main() {
894     let a = Some(1);
895     let b = Some(());
896     match (a$0, b) {
897         (Some(_), _) => {}
898         (None, Some(_)) => {}
899     }
900 }
901 "#,
902             r#"
903 fn main() {
904     let a = Some(1);
905     let b = Some(());
906     match (a, b) {
907         (Some(_), _) => {}
908         (None, Some(_)) => {}
909         $0(None, None) => todo!(),
910     }
911 }
912 "#,
913         );
914     }
915
916     #[test]
917     fn add_missing_match_arms_partial_with_deep_pattern() {
918         // Fixme: cannot handle deep patterns
919         check_assist_not_applicable(
920             add_missing_match_arms,
921             r#"
922 //- minicore: option
923 fn main() {
924     match $0Some(true) {
925         Some(true) => {}
926         None => {}
927     }
928 }
929 "#,
930         );
931     }
932
933     #[test]
934     fn add_missing_match_arms_tuple_of_enum_not_applicable() {
935         check_assist_not_applicable(
936             add_missing_match_arms,
937             r#"
938 enum A { One, Two }
939 enum B { One, Two }
940
941 fn main() {
942     let a = A::One;
943     let b = B::One;
944     match (a$0, b) {
945         (A::Two, B::One) => {}
946         (A::One, B::One) => {}
947         (A::One, B::Two) => {}
948         (A::Two, B::Two) => {}
949     }
950 }
951 "#,
952         );
953     }
954
955     #[test]
956     fn add_missing_match_arms_single_element_tuple_of_enum() {
957         check_assist(
958             add_missing_match_arms,
959             r#"
960 enum A { One, Two }
961
962 fn main() {
963     let a = A::One;
964     match (a$0, ) {
965     }
966 }
967 "#,
968             r#"
969 enum A { One, Two }
970
971 fn main() {
972     let a = A::One;
973     match (a, ) {
974         $0(A::One,) => todo!(),
975         (A::Two,) => todo!(),
976     }
977 }
978 "#,
979         );
980     }
981
982     #[test]
983     fn test_fill_match_arm_refs() {
984         check_assist(
985             add_missing_match_arms,
986             r#"
987 enum A { As }
988
989 fn foo(a: &A) {
990     match a$0 {
991     }
992 }
993 "#,
994             r#"
995 enum A { As }
996
997 fn foo(a: &A) {
998     match a {
999         $0A::As => todo!(),
1000     }
1001 }
1002 "#,
1003         );
1004
1005         check_assist(
1006             add_missing_match_arms,
1007             r#"
1008 enum A {
1009     Es { x: usize, y: usize }
1010 }
1011
1012 fn foo(a: &mut A) {
1013     match a$0 {
1014     }
1015 }
1016 "#,
1017             r#"
1018 enum A {
1019     Es { x: usize, y: usize }
1020 }
1021
1022 fn foo(a: &mut A) {
1023     match a {
1024         $0A::Es { x, y } => todo!(),
1025     }
1026 }
1027 "#,
1028         );
1029     }
1030
1031     #[test]
1032     fn add_missing_match_arms_target_simple() {
1033         check_assist_target(
1034             add_missing_match_arms,
1035             r#"
1036 enum E { X, Y }
1037
1038 fn main() {
1039     match E::X$0 {}
1040 }
1041 "#,
1042             "match E::X {}",
1043         );
1044     }
1045
1046     #[test]
1047     fn add_missing_match_arms_target_complex() {
1048         check_assist_target(
1049             add_missing_match_arms,
1050             r#"
1051 enum E { X, Y }
1052
1053 fn main() {
1054     match E::X$0 {
1055         E::X => {}
1056     }
1057 }
1058 "#,
1059             "match E::X {
1060         E::X => {}
1061     }",
1062         );
1063     }
1064
1065     #[test]
1066     fn add_missing_match_arms_trivial_arm() {
1067         cov_mark::check!(add_missing_match_arms_trivial_arm);
1068         check_assist(
1069             add_missing_match_arms,
1070             r#"
1071 enum E { X, Y }
1072
1073 fn main() {
1074     match E::X {
1075         $0_ => {}
1076     }
1077 }
1078 "#,
1079             r#"
1080 enum E { X, Y }
1081
1082 fn main() {
1083     match E::X {
1084         $0E::X => todo!(),
1085         E::Y => todo!(),
1086     }
1087 }
1088 "#,
1089         );
1090     }
1091
1092     #[test]
1093     fn wildcard_inside_expression_not_applicable() {
1094         check_assist_not_applicable(
1095             add_missing_match_arms,
1096             r#"
1097 enum E { X, Y }
1098
1099 fn foo(e : E) {
1100     match e {
1101         _ => {
1102             println!("1");$0
1103             println!("2");
1104         }
1105     }
1106 }
1107 "#,
1108         );
1109     }
1110
1111     #[test]
1112     fn add_missing_match_arms_qualifies_path() {
1113         check_assist(
1114             add_missing_match_arms,
1115             r#"
1116 mod foo { pub enum E { X, Y } }
1117 use foo::E::X;
1118
1119 fn main() {
1120     match X {
1121         $0
1122     }
1123 }
1124 "#,
1125             r#"
1126 mod foo { pub enum E { X, Y } }
1127 use foo::E::X;
1128
1129 fn main() {
1130     match X {
1131         $0X => todo!(),
1132         foo::E::Y => todo!(),
1133     }
1134 }
1135 "#,
1136         );
1137     }
1138
1139     #[test]
1140     fn add_missing_match_arms_preserves_comments() {
1141         check_assist(
1142             add_missing_match_arms,
1143             r#"
1144 enum A { One, Two }
1145 fn foo(a: A) {
1146     match a $0 {
1147         // foo bar baz
1148         A::One => {}
1149         // This is where the rest should be
1150     }
1151 }
1152 "#,
1153             r#"
1154 enum A { One, Two }
1155 fn foo(a: A) {
1156     match a  {
1157         // foo bar baz
1158         A::One => {}
1159         $0A::Two => todo!(),
1160         // This is where the rest should be
1161     }
1162 }
1163 "#,
1164         );
1165     }
1166
1167     #[test]
1168     fn add_missing_match_arms_preserves_comments_empty() {
1169         check_assist(
1170             add_missing_match_arms,
1171             r#"
1172 enum A { One, Two }
1173 fn foo(a: A) {
1174     match a {
1175         // foo bar baz$0
1176     }
1177 }
1178 "#,
1179             r#"
1180 enum A { One, Two }
1181 fn foo(a: A) {
1182     match a {
1183         $0A::One => todo!(),
1184         A::Two => todo!(),
1185         // foo bar baz
1186     }
1187 }
1188 "#,
1189         );
1190     }
1191
1192     #[test]
1193     fn add_missing_match_arms_placeholder() {
1194         check_assist(
1195             add_missing_match_arms,
1196             r#"
1197 enum A { One, Two, }
1198 fn foo(a: A) {
1199     match a$0 {
1200         _ => (),
1201     }
1202 }
1203 "#,
1204             r#"
1205 enum A { One, Two, }
1206 fn foo(a: A) {
1207     match a {
1208         $0A::One => todo!(),
1209         A::Two => todo!(),
1210     }
1211 }
1212 "#,
1213         );
1214     }
1215
1216     #[test]
1217     fn option_order() {
1218         cov_mark::check!(option_order);
1219         check_assist(
1220             add_missing_match_arms,
1221             r#"
1222 //- minicore: option
1223 fn foo(opt: Option<i32>) {
1224     match opt$0 {
1225     }
1226 }
1227 "#,
1228             r#"
1229 fn foo(opt: Option<i32>) {
1230     match opt {
1231         Some(${0:_}) => todo!(),
1232         None => todo!(),
1233     }
1234 }
1235 "#,
1236         );
1237     }
1238
1239     #[test]
1240     fn works_inside_macro_call() {
1241         check_assist(
1242             add_missing_match_arms,
1243             r#"
1244 macro_rules! m { ($expr:expr) => {$expr}}
1245 enum Test {
1246     A,
1247     B,
1248     C,
1249 }
1250
1251 fn foo(t: Test) {
1252     m!(match t$0 {});
1253 }"#,
1254             r#"
1255 macro_rules! m { ($expr:expr) => {$expr}}
1256 enum Test {
1257     A,
1258     B,
1259     C,
1260 }
1261
1262 fn foo(t: Test) {
1263     m!(match t {
1264     $0Test::A => todo!(),
1265     Test::B => todo!(),
1266     Test::C => todo!(),
1267 });
1268 }"#,
1269         );
1270     }
1271
1272     #[test]
1273     fn lazy_computation() {
1274         // Computing a single missing arm is enough to determine applicability of the assist.
1275         cov_mark::check_count!(add_missing_match_arms_lazy_computation, 1);
1276         check_assist_unresolved(
1277             add_missing_match_arms,
1278             r#"
1279 enum A { One, Two, }
1280 fn foo(tuple: (A, A)) {
1281     match $0tuple {};
1282 }
1283 "#,
1284         );
1285     }
1286
1287     #[test]
1288     fn adds_comma_before_new_arms() {
1289         check_assist(
1290             add_missing_match_arms,
1291             r#"
1292 fn foo(t: bool) {
1293     match $0t {
1294         true => 1 + 2
1295     }
1296 }"#,
1297             r#"
1298 fn foo(t: bool) {
1299     match t {
1300         true => 1 + 2,
1301         $0false => todo!(),
1302     }
1303 }"#,
1304         );
1305     }
1306
1307     #[test]
1308     fn does_not_add_extra_comma() {
1309         check_assist(
1310             add_missing_match_arms,
1311             r#"
1312 fn foo(t: bool) {
1313     match $0t {
1314         true => 1 + 2,
1315     }
1316 }"#,
1317             r#"
1318 fn foo(t: bool) {
1319     match t {
1320         true => 1 + 2,
1321         $0false => todo!(),
1322     }
1323 }"#,
1324         );
1325     }
1326
1327     #[test]
1328     fn does_not_remove_catch_all_with_non_empty_expr() {
1329         cov_mark::check!(add_missing_match_arms_empty_expr);
1330         check_assist(
1331             add_missing_match_arms,
1332             r#"
1333 fn foo(t: bool) {
1334     match $0t {
1335         _ => 1 + 2,
1336     }
1337 }"#,
1338             r#"
1339 fn foo(t: bool) {
1340     match t {
1341         _ => 1 + 2,
1342         $0true => todo!(),
1343         false => todo!(),
1344     }
1345 }"#,
1346         );
1347     }
1348
1349     #[test]
1350     fn does_not_fill_hidden_variants() {
1351         cov_mark::check!(added_wildcard_pattern);
1352         check_assist(
1353             add_missing_match_arms,
1354             r#"
1355 //- /main.rs crate:main deps:e
1356 fn foo(t: ::e::E) {
1357     match $0t {
1358     }
1359 }
1360 //- /e.rs crate:e
1361 pub enum E { A, #[doc(hidden)] B, }
1362 "#,
1363             r#"
1364 fn foo(t: ::e::E) {
1365     match t {
1366         $0e::E::A => todo!(),
1367         _ => todo!(),
1368     }
1369 }
1370 "#,
1371         );
1372     }
1373
1374     #[test]
1375     fn does_not_fill_hidden_variants_tuple() {
1376         cov_mark::check!(added_wildcard_pattern);
1377         check_assist(
1378             add_missing_match_arms,
1379             r#"
1380 //- /main.rs crate:main deps:e
1381 fn foo(t: (bool, ::e::E)) {
1382     match $0t {
1383     }
1384 }
1385 //- /e.rs crate:e
1386 pub enum E { A, #[doc(hidden)] B, }
1387 "#,
1388             r#"
1389 fn foo(t: (bool, ::e::E)) {
1390     match t {
1391         $0(true, e::E::A) => todo!(),
1392         (false, e::E::A) => todo!(),
1393         _ => todo!(),
1394     }
1395 }
1396 "#,
1397         );
1398     }
1399
1400     #[test]
1401     fn fills_wildcard_with_only_hidden_variants() {
1402         cov_mark::check!(added_wildcard_pattern);
1403         check_assist(
1404             add_missing_match_arms,
1405             r#"
1406 //- /main.rs crate:main deps:e
1407 fn foo(t: ::e::E) {
1408     match $0t {
1409     }
1410 }
1411 //- /e.rs crate:e
1412 pub enum E { #[doc(hidden)] A, }
1413 "#,
1414             r#"
1415 fn foo(t: ::e::E) {
1416     match t {
1417         ${0:_} => todo!(),
1418     }
1419 }
1420 "#,
1421         );
1422     }
1423
1424     #[test]
1425     fn does_not_fill_wildcard_when_hidden_variants_are_explicit() {
1426         check_assist_not_applicable(
1427             add_missing_match_arms,
1428             r#"
1429 //- /main.rs crate:main deps:e
1430 fn foo(t: ::e::E) {
1431     match $0t {
1432         e::E::A => todo!(),
1433     }
1434 }
1435 //- /e.rs crate:e
1436 pub enum E { #[doc(hidden)] A, }
1437 "#,
1438         );
1439     }
1440
1441     // FIXME: I don't think the assist should be applicable in this case
1442     #[test]
1443     fn does_not_fill_wildcard_with_wildcard() {
1444         check_assist(
1445             add_missing_match_arms,
1446             r#"
1447 //- /main.rs crate:main deps:e
1448 fn foo(t: ::e::E) {
1449     match $0t {
1450         _ => todo!(),
1451     }
1452 }
1453 //- /e.rs crate:e
1454 pub enum E { #[doc(hidden)] A, }
1455 "#,
1456             r#"
1457 fn foo(t: ::e::E) {
1458     match t {
1459         _ => todo!(),
1460     }
1461 }
1462 "#,
1463         );
1464     }
1465
1466     #[test]
1467     fn fills_wildcard_on_non_exhaustive_with_explicit_matches() {
1468         cov_mark::check!(added_wildcard_pattern);
1469         check_assist(
1470             add_missing_match_arms,
1471             r#"
1472 //- /main.rs crate:main deps:e
1473 fn foo(t: ::e::E) {
1474     match $0t {
1475         e::E::A => todo!(),
1476     }
1477 }
1478 //- /e.rs crate:e
1479 #[non_exhaustive]
1480 pub enum E { A, }
1481 "#,
1482             r#"
1483 fn foo(t: ::e::E) {
1484     match t {
1485         e::E::A => todo!(),
1486         ${0:_} => todo!(),
1487     }
1488 }
1489 "#,
1490         );
1491     }
1492
1493     #[test]
1494     fn fills_wildcard_on_non_exhaustive_without_matches() {
1495         cov_mark::check!(added_wildcard_pattern);
1496         check_assist(
1497             add_missing_match_arms,
1498             r#"
1499 //- /main.rs crate:main deps:e
1500 fn foo(t: ::e::E) {
1501     match $0t {
1502     }
1503 }
1504 //- /e.rs crate:e
1505 #[non_exhaustive]
1506 pub enum E { A, }
1507 "#,
1508             r#"
1509 fn foo(t: ::e::E) {
1510     match t {
1511         $0e::E::A => todo!(),
1512         _ => todo!(),
1513     }
1514 }
1515 "#,
1516         );
1517     }
1518
1519     #[test]
1520     fn fills_wildcard_on_non_exhaustive_with_doc_hidden() {
1521         cov_mark::check!(added_wildcard_pattern);
1522         check_assist(
1523             add_missing_match_arms,
1524             r#"
1525 //- /main.rs crate:main deps:e
1526 fn foo(t: ::e::E) {
1527     match $0t {
1528     }
1529 }
1530 //- /e.rs crate:e
1531 #[non_exhaustive]
1532 pub enum E { A, #[doc(hidden)] B }"#,
1533             r#"
1534 fn foo(t: ::e::E) {
1535     match t {
1536         $0e::E::A => todo!(),
1537         _ => todo!(),
1538     }
1539 }
1540 "#,
1541         );
1542     }
1543
1544     #[test]
1545     fn fills_wildcard_on_non_exhaustive_with_doc_hidden_with_explicit_arms() {
1546         cov_mark::check!(added_wildcard_pattern);
1547         check_assist(
1548             add_missing_match_arms,
1549             r#"
1550 //- /main.rs crate:main deps:e
1551 fn foo(t: ::e::E) {
1552     match $0t {
1553         e::E::A => todo!(),
1554     }
1555 }
1556 //- /e.rs crate:e
1557 #[non_exhaustive]
1558 pub enum E { A, #[doc(hidden)] B }"#,
1559             r#"
1560 fn foo(t: ::e::E) {
1561     match t {
1562         e::E::A => todo!(),
1563         ${0:_} => todo!(),
1564     }
1565 }
1566 "#,
1567         );
1568     }
1569
1570     #[test]
1571     fn fill_wildcard_with_partial_wildcard() {
1572         cov_mark::check!(added_wildcard_pattern);
1573         check_assist(
1574             add_missing_match_arms,
1575             r#"
1576 //- /main.rs crate:main deps:e
1577 fn foo(t: ::e::E, b: bool) {
1578     match $0t {
1579         _ if b => todo!(),
1580     }
1581 }
1582 //- /e.rs crate:e
1583 pub enum E { #[doc(hidden)] A, }"#,
1584             r#"
1585 fn foo(t: ::e::E, b: bool) {
1586     match t {
1587         _ if b => todo!(),
1588         ${0:_} => todo!(),
1589     }
1590 }
1591 "#,
1592         );
1593     }
1594
1595     #[test]
1596     fn does_not_fill_wildcard_with_partial_wildcard_and_wildcard() {
1597         check_assist(
1598             add_missing_match_arms,
1599             r#"
1600 //- /main.rs crate:main deps:e
1601 fn foo(t: ::e::E, b: bool) {
1602     match $0t {
1603         _ if b => todo!(),
1604         _ => todo!(),
1605     }
1606 }
1607 //- /e.rs crate:e
1608 pub enum E { #[doc(hidden)] A, }"#,
1609             r#"
1610 fn foo(t: ::e::E, b: bool) {
1611     match t {
1612         _ if b => todo!(),
1613         _ => todo!(),
1614     }
1615 }
1616 "#,
1617         );
1618     }
1619
1620     #[test]
1621     fn non_exhaustive_doc_hidden_tuple_fills_wildcard() {
1622         cov_mark::check!(added_wildcard_pattern);
1623         check_assist(
1624             add_missing_match_arms,
1625             r#"
1626 //- /main.rs crate:main deps:e
1627 fn foo(t: ::e::E) {
1628     match $0t {
1629     }
1630 }
1631 //- /e.rs crate:e
1632 #[non_exhaustive]
1633 pub enum E { A, #[doc(hidden)] B, }"#,
1634             r#"
1635 fn foo(t: ::e::E) {
1636     match t {
1637         $0e::E::A => todo!(),
1638         _ => todo!(),
1639     }
1640 }
1641 "#,
1642         );
1643     }
1644
1645     #[test]
1646     fn ignores_doc_hidden_for_crate_local_enums() {
1647         check_assist(
1648             add_missing_match_arms,
1649             r#"
1650 enum E { A, #[doc(hidden)] B, }
1651
1652 fn foo(t: E) {
1653     match $0t {
1654     }
1655 }"#,
1656             r#"
1657 enum E { A, #[doc(hidden)] B, }
1658
1659 fn foo(t: E) {
1660     match t {
1661         $0E::A => todo!(),
1662         E::B => todo!(),
1663     }
1664 }"#,
1665         );
1666     }
1667
1668     #[test]
1669     fn ignores_non_exhaustive_for_crate_local_enums() {
1670         check_assist(
1671             add_missing_match_arms,
1672             r#"
1673 #[non_exhaustive]
1674 enum E { A, B, }
1675
1676 fn foo(t: E) {
1677     match $0t {
1678     }
1679 }"#,
1680             r#"
1681 #[non_exhaustive]
1682 enum E { A, B, }
1683
1684 fn foo(t: E) {
1685     match t {
1686         $0E::A => todo!(),
1687         E::B => todo!(),
1688     }
1689 }"#,
1690         );
1691     }
1692
1693     #[test]
1694     fn ignores_doc_hidden_and_non_exhaustive_for_crate_local_enums() {
1695         check_assist(
1696             add_missing_match_arms,
1697             r#"
1698 #[non_exhaustive]
1699 enum E { A, #[doc(hidden)] B, }
1700
1701 fn foo(t: E) {
1702     match $0t {
1703     }
1704 }"#,
1705             r#"
1706 #[non_exhaustive]
1707 enum E { A, #[doc(hidden)] B, }
1708
1709 fn foo(t: E) {
1710     match t {
1711         $0E::A => todo!(),
1712         E::B => todo!(),
1713     }
1714 }"#,
1715         );
1716     }
1717 }