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