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