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