ide_diagnostics/handlers/
missing_match_arms.rs

1use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
2
3// Diagnostic: missing-match-arm
4//
5// This diagnostic is triggered if `match` block is missing one or more match arms.
6pub(crate) fn missing_match_arms(
7    ctx: &DiagnosticsContext<'_>,
8    d: &hir::MissingMatchArms,
9) -> Diagnostic {
10    Diagnostic::new_with_syntax_node_ptr(
11        ctx,
12        DiagnosticCode::RustcHardError("E0004"),
13        format!("missing match arm: {}", d.uncovered_patterns),
14        d.scrutinee_expr.map(Into::into),
15    )
16    .stable()
17}
18
19#[cfg(test)]
20mod tests {
21    use crate::{
22        DiagnosticsConfig,
23        tests::{
24            check_diagnostics, check_diagnostics_with_config, check_diagnostics_with_disabled,
25        },
26    };
27    use test_utils::skip_slow_tests;
28
29    #[track_caller]
30    fn check_diagnostics_no_bails(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
31        cov_mark::check_count!(validate_match_bailed_out, 0);
32        crate::tests::check_diagnostics(ra_fixture)
33    }
34
35    #[test]
36    fn empty_body() {
37        let mut config = DiagnosticsConfig::test_sample();
38        config.disabled.insert("syntax-error".to_owned());
39        check_diagnostics_with_config(
40            config,
41            r#"
42fn main() {
43    match 0;
44}
45"#,
46        );
47    }
48
49    #[test]
50    fn empty_tuple() {
51        check_diagnostics_no_bails(
52            r#"
53fn main() {
54    match () { }
55        //^^ error: missing match arm: type `()` is non-empty
56    match (()) { }
57        //^^^^ error: missing match arm: type `()` is non-empty
58
59    match () { _ => (), }
60    match () { () => (), }
61    match (()) { (()) => (), }
62}
63"#,
64        );
65    }
66
67    #[test]
68    fn tuple_of_two_empty_tuple() {
69        check_diagnostics_no_bails(
70            r#"
71fn main() {
72    match ((), ()) { }
73        //^^^^^^^^ error: missing match arm: type `((), ())` is non-empty
74
75    match ((), ()) { ((), ()) => (), }
76}
77"#,
78        );
79    }
80
81    #[test]
82    fn boolean() {
83        check_diagnostics_no_bails(
84            r#"
85fn test_main() {
86    match false { }
87        //^^^^^ error: missing match arm: type `bool` is non-empty
88    match false { true => (), }
89        //^^^^^ error: missing match arm: `false` not covered
90    match (false, true) {}
91        //^^^^^^^^^^^^^ error: missing match arm: type `(bool, bool)` is non-empty
92    match (false, true) { (true, true) => (), }
93        //^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered
94    match (false, true) {
95        //^^^^^^^^^^^^^ error: missing match arm: `(true, true)` not covered
96        (false, true) => (),
97        (false, false) => (),
98        (true, false) => (),
99    }
100    match (false, true) { (true, _x) => (), }
101        //^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered
102
103    match false { true => (), false => (), }
104    match (false, true) {
105        (false, _) => (),
106        (true, false) => (),
107        (_, true) => (),
108    }
109    match (false, true) {
110        (true, true) => (),
111        (true, false) => (),
112        (false, true) => (),
113        (false, false) => (),
114    }
115    match (false, true) {
116        (true, _x) => (),
117        (false, true) => (),
118        (false, false) => (),
119    }
120    match (false, true, false) {
121        (false, ..) => (),
122        (true, ..) => (),
123    }
124    match (false, true, false) {
125        (.., false) => (),
126        (.., true) => (),
127    }
128    match (false, true, false) { (..) => (), }
129}
130"#,
131        );
132    }
133
134    #[test]
135    fn tuple_of_tuple_and_bools() {
136        check_diagnostics_no_bails(
137            r#"
138fn main() {
139    match (false, ((), false)) {}
140        //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: type `(bool, ((), bool))` is non-empty
141    match (false, ((), false)) { (true, ((), true)) => (), }
142        //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered
143    match (false, ((), false)) { (true, _) => (), }
144        //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(false, _)` not covered
145
146    match (false, ((), false)) {
147        (true, ((), true)) => (),
148        (true, ((), false)) => (),
149        (false, ((), true)) => (),
150        (false, ((), false)) => (),
151    }
152    match (false, ((), false)) {
153        (true, ((), true)) => (),
154        (true, ((), false)) => (),
155        (false, _) => (),
156    }
157}
158"#,
159        );
160    }
161
162    #[test]
163    fn enums() {
164        check_diagnostics_no_bails(
165            r#"
166enum Either { A, B, }
167
168fn main() {
169    match Either::A { }
170        //^^^^^^^^^ error: missing match arm: `A` and `B` not covered
171    match Either::B { Either::A => (), }
172        //^^^^^^^^^ error: missing match arm: `B` not covered
173
174    match &Either::B {
175        //^^^^^^^^^^ error: missing match arm: `&B` not covered
176        Either::A => (),
177    }
178
179    match Either::B {
180        Either::A => (), Either::B => (),
181    }
182    match &Either::B {
183        Either::A => (), Either::B => (),
184    }
185}
186"#,
187        );
188    }
189
190    #[test]
191    fn enum_containing_bool() {
192        check_diagnostics_no_bails(
193            r#"
194enum Either { A(bool), B }
195
196fn main() {
197    match Either::B { }
198        //^^^^^^^^^ error: missing match arm: `A(_)` and `B` not covered
199    match Either::B {
200        //^^^^^^^^^ error: missing match arm: `A(false)` not covered
201        Either::A(true) => (), Either::B => ()
202    }
203
204    match Either::B {
205        Either::A(true) => (),
206        Either::A(false) => (),
207        Either::B => (),
208    }
209    match Either::B {
210        Either::B => (),
211        _ => (),
212    }
213    match Either::B {
214        Either::A(_) => (),
215        Either::B => (),
216    }
217
218}
219        "#,
220        );
221    }
222
223    #[test]
224    fn enum_different_sizes() {
225        check_diagnostics_no_bails(
226            r#"
227enum Either { A(bool), B(bool, bool) }
228
229fn main() {
230    match Either::A(false) {
231        //^^^^^^^^^^^^^^^^ error: missing match arm: `B(true, _)` not covered
232        Either::A(_) => (),
233        Either::B(false, _) => (),
234    }
235
236    match Either::A(false) {
237        Either::A(_) => (),
238        Either::B(true, _) => (),
239        Either::B(false, _) => (),
240    }
241    match Either::A(false) {
242        Either::A(true) | Either::A(false) => (),
243        Either::B(true, _) => (),
244        Either::B(false, _) => (),
245    }
246}
247"#,
248        );
249    }
250
251    #[test]
252    fn tuple_of_enum_no_diagnostic() {
253        check_diagnostics_no_bails(
254            r#"
255enum Either { A(bool), B(bool, bool) }
256enum Either2 { C, D }
257
258fn main() {
259    match (Either::A(false), Either2::C) {
260        (Either::A(true), _) | (Either::A(false), _) => (),
261        (Either::B(true, _), Either2::C) => (),
262        (Either::B(false, _), Either2::C) => (),
263        (Either::B(_, _), Either2::D) => (),
264    }
265}
266"#,
267        );
268    }
269
270    #[test]
271    fn or_pattern_no_diagnostic() {
272        check_diagnostics_no_bails(
273            r#"
274enum Either {A, B}
275
276fn main() {
277    match (Either::A, Either::B) {
278        (Either::A | Either::B, _) => (),
279    }
280}"#,
281        )
282    }
283
284    #[test]
285    fn mismatched_types() {
286        cov_mark::check_count!(validate_match_bailed_out, 4);
287        // Match statements with arms that don't match the
288        // expression pattern do not fire this diagnostic.
289        check_diagnostics_with_disabled(
290            r#"
291enum Either { A, B }
292enum Either2 { C, D }
293
294fn main() {
295    match Either::A {
296        Either2::C => (),
297      //^^^^^^^^^^ error: expected Either, found Either2
298        Either2::D => (),
299      //^^^^^^^^^^ error: expected Either, found Either2
300    }
301    match (true, false) {
302        (true, false, true) => (),
303      //^^^^^^^^^^^^^^^^^^^ error: expected (bool, bool), found (bool, bool, bool)
304        (true) => (),
305      // ^^^^  error: expected (bool, bool), found bool
306    }
307    match (true, false) { (true,) => {} }
308                        //^^^^^^^ error: expected (bool, bool), found (bool,)
309    match (0) { () => () }
310              //^^ error: expected i32, found ()
311    match Unresolved::Bar { Unresolved::Baz => () }
312}
313        "#,
314            &["E0425"],
315        );
316    }
317
318    #[test]
319    fn mismatched_types_issue_15883() {
320        // Check we don't panic.
321        cov_mark::check!(validate_match_bailed_out);
322        check_diagnostics(
323            r#"
324//- minicore: option
325fn main() {
326    match Some((true, false)) {
327        Some(true) | Some(false) => {}
328        //   ^^^^ error: expected (bool, bool), found bool
329        //                ^^^^^ error: expected (bool, bool), found bool
330        None => {}
331    }
332}
333            "#,
334        );
335    }
336
337    #[test]
338    fn mismatched_types_in_or_patterns() {
339        cov_mark::check_count!(validate_match_bailed_out, 2);
340        check_diagnostics(
341            r#"
342fn main() {
343    match false { true | () => {} }
344                       //^^ error: expected bool, found ()
345    match (false,) { (true | (),) => {} }
346                           //^^ error: expected bool, found ()
347}
348"#,
349        );
350    }
351
352    #[test]
353    fn malformed_match_arm_tuple_enum_missing_pattern() {
354        // We are testing to be sure we don't panic here when the match
355        // arm `Either::B` is missing its pattern.
356        check_diagnostics_no_bails(
357            r#"
358enum Either { A, B(u32) }
359
360fn main() {
361    match Either::A {
362        Either::A => (),
363        Either::B() => (),
364              // ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 1 field
365    }
366}
367"#,
368        );
369    }
370
371    #[test]
372    fn malformed_match_arm_extra_fields() {
373        cov_mark::check_count!(validate_match_bailed_out, 2);
374        check_diagnostics(
375            r#"
376enum A { B(isize, isize), C }
377fn main() {
378    match A::B(1, 2) {
379        A::B(_, _, _) => (),
380                // ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields
381    }
382    match A::B(1, 2) {
383        A::C(_) => (),
384         // ^^^ error: this pattern has 1 field, but the corresponding tuple struct has 0 fields
385    }
386}
387"#,
388        );
389    }
390
391    #[test]
392    fn expr_diverges() {
393        check_diagnostics(
394            r#"
395enum Either { A, B }
396
397fn main() {
398    match loop {} {
399        Either::A => (),
400        Either::B => (),
401    }
402    match loop {} {
403       // ^^^^^^^ error: missing match arm: `B` not covered
404        Either::A => (),
405    }
406    match loop { break Either::A } {
407        //^^^^^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `B` not covered
408        Either::A => (),
409    }
410    match loop { break Either::A } {
411        Either::A => (),
412        Either::B => (),
413    }
414}
415"#,
416        );
417    }
418
419    #[test]
420    fn expr_partially_diverges() {
421        check_diagnostics_no_bails(
422            r#"
423enum Either<T> { A(T), B }
424
425fn foo() -> Either<!> { Either::B }
426fn main() -> u32 {
427    match foo() {
428        Either::A(val) => val,
429        Either::B => 0,
430    }
431}
432"#,
433        );
434    }
435
436    #[test]
437    fn enum_record() {
438        check_diagnostics_no_bails(
439            r#"
440enum Either { A { foo: bool }, B }
441
442fn main() {
443    let a = Either::A { foo: true };
444    match a { }
445        //^ error: missing match arm: `A { .. }` and `B` not covered
446    match a { Either::A { foo: true } => () }
447        //^ error: missing match arm: `B` not covered
448    match a {
449        Either::A { } => (),
450      //^^^^^^^^^ 💡 error: missing structure fields:
451      //        | - foo
452        Either::B => (),
453    }
454    match a {
455        //^ error: missing match arm: `B` not covered
456        Either::A { } => (),
457    } //^^^^^^^^^ 💡 error: missing structure fields:
458      //        | - foo
459
460    match a {
461        Either::A { foo: true } => (),
462        Either::A { foo: false } => (),
463        Either::B => (),
464    }
465    match a {
466        Either::A { foo: _ } => (),
467        Either::B => (),
468    }
469}
470"#,
471        );
472    }
473
474    #[test]
475    fn enum_record_fields_out_of_order() {
476        check_diagnostics_no_bails(
477            r#"
478enum Either {
479    A { foo: bool, bar: () },
480    B,
481}
482
483fn main() {
484    let a = Either::A { foo: true, bar: () };
485    match a {
486        //^ error: missing match arm: `B` not covered
487        Either::A { bar: (), foo: false } => (),
488        Either::A { foo: true, bar: () } => (),
489    }
490
491    match a {
492        Either::A { bar: (), foo: false } => (),
493        Either::A { foo: true, bar: () } => (),
494        Either::B => (),
495    }
496}
497"#,
498        );
499    }
500
501    #[test]
502    fn enum_record_ellipsis() {
503        check_diagnostics_no_bails(
504            r#"
505enum Either {
506    A { foo: bool, bar: bool },
507    B,
508}
509
510fn main() {
511    let a = Either::B;
512    match a {
513        //^ error: missing match arm: `A { foo: false, .. }` not covered
514        Either::A { foo: true, .. } => (),
515        Either::B => (),
516    }
517    match a {
518        //^ error: missing match arm: `B` not covered
519        Either::A { .. } => (),
520    }
521
522    match a {
523        Either::A { foo: true, .. } => (),
524        Either::A { foo: false, .. } => (),
525        Either::B => (),
526    }
527
528    match a {
529        Either::A { .. } => (),
530        Either::B => (),
531    }
532}
533"#,
534        );
535    }
536
537    #[test]
538    fn enum_tuple_partial_ellipsis() {
539        check_diagnostics_no_bails(
540            r#"
541enum Either {
542    A(bool, bool, bool, bool),
543    B,
544}
545
546fn main() {
547    match Either::B {
548        //^^^^^^^^^ error: missing match arm: `A(false, _, _, true)` not covered
549        Either::A(true, .., true) => (),
550        Either::A(true, .., false) => (),
551        Either::A(false, .., false) => (),
552        Either::B => (),
553    }
554    match Either::B {
555        //^^^^^^^^^ error: missing match arm: `A(false, _, _, false)` not covered
556        Either::A(true, .., true) => (),
557        Either::A(true, .., false) => (),
558        Either::A(.., true) => (),
559        Either::B => (),
560    }
561
562    match Either::B {
563        Either::A(true, .., true) => (),
564        Either::A(true, .., false) => (),
565        Either::A(false, .., true) => (),
566        Either::A(false, .., false) => (),
567        Either::B => (),
568    }
569    match Either::B {
570        Either::A(true, .., true) => (),
571        Either::A(true, .., false) => (),
572        Either::A(.., true) => (),
573        Either::A(.., false) => (),
574        Either::B => (),
575    }
576}
577"#,
578        );
579    }
580
581    #[test]
582    fn never() {
583        check_diagnostics_no_bails(
584            r#"
585enum Never {}
586
587fn enum_(never: Never) {
588    match never {}
589}
590fn enum_ref(never: &Never) {
591    match never {}
592        //^^^^^ error: missing match arm: type `&Never` is non-empty
593}
594fn bang(never: !) {
595    match never {}
596}
597"#,
598        );
599    }
600
601    #[test]
602    fn unknown_type() {
603        check_diagnostics_no_bails(
604            r#"
605enum Option<T> { Some(T), None }
606
607#[allow(unused)]
608fn main() {
609    // `Never` is deliberately not defined so that it's an uninferred type.
610    // We ignore these to avoid triggering bugs in the analysis.
611    match Option::<Never>::None {
612        Option::None => (),
613        Option::Some(never) => match never {},
614    }
615    match Option::<Never>::None {
616        Option::Some(_never) => {},
617    }
618}
619"#,
620        );
621    }
622
623    #[test]
624    fn arity_mismatch_issue_16746() {
625        check_diagnostics_with_disabled(
626            r#"
627fn main() {
628    let (a, ) = (0, 0);
629}
630"#,
631            &["E0308"],
632        );
633    }
634
635    #[test]
636    fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
637        check_diagnostics_no_bails(
638            r#"
639fn main() {
640    match (false, true, false) {
641        //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(true, _, _)` not covered
642        (false, ..) => (),
643    }
644}"#,
645        );
646    }
647
648    #[test]
649    fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
650        check_diagnostics_no_bails(
651            r#"
652fn main() {
653    match (false, true, false) {
654        //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(_, _, true)` not covered
655        (.., false) => (),
656    }
657}"#,
658        );
659    }
660
661    #[test]
662    fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() {
663        check_diagnostics_no_bails(
664            r#"
665fn main() {
666    match (false, true, false) {
667        //^^^^^^^^^^^^^^^^^^^^ error: missing match arm: `(false, _, _)` not covered
668        (true, .., false) => (),
669    }
670}"#,
671        );
672    }
673
674    #[test]
675    fn record_struct() {
676        check_diagnostics_no_bails(
677            r#"struct Foo { a: bool }
678fn main(f: Foo) {
679    match f {}
680        //^ error: missing match arm: type `Foo` is non-empty
681    match f { Foo { a: true } => () }
682        //^ error: missing match arm: `Foo { a: false }` not covered
683    match &f { Foo { a: true } => () }
684        //^^ error: missing match arm: `&Foo { a: false }` not covered
685    match f { Foo { a: _ } => () }
686    match f {
687        Foo { a: true } => (),
688        Foo { a: false } => (),
689    }
690    match &f {
691        Foo { a: true } => (),
692        Foo { a: false } => (),
693    }
694}
695"#,
696        );
697    }
698
699    #[test]
700    fn tuple_struct() {
701        check_diagnostics_no_bails(
702            r#"struct Foo(bool);
703fn main(f: Foo) {
704    match f {}
705        //^ error: missing match arm: type `Foo` is non-empty
706    match f { Foo(true) => () }
707        //^ error: missing match arm: `Foo(false)` not covered
708    match f {
709        Foo(true) => (),
710        Foo(false) => (),
711    }
712}
713"#,
714        );
715    }
716
717    #[test]
718    fn unit_struct() {
719        check_diagnostics_no_bails(
720            r#"struct Foo;
721fn main(f: Foo) {
722    match f {}
723        //^ error: missing match arm: type `Foo` is non-empty
724    match f { Foo => () }
725}
726"#,
727        );
728    }
729
730    #[test]
731    fn record_struct_ellipsis() {
732        check_diagnostics_no_bails(
733            r#"struct Foo { foo: bool, bar: bool }
734fn main(f: Foo) {
735    match f { Foo { foo: true, .. } => () }
736        //^ error: missing match arm: `Foo { foo: false, .. }` not covered
737    match f {
738        //^ error: missing match arm: `Foo { foo: false, bar: true }` not covered
739        Foo { foo: true, .. } => (),
740        Foo { bar: false, .. } => ()
741    }
742    match f { Foo { .. } => () }
743    match f {
744        Foo { foo: true, .. } => (),
745        Foo { foo: false, .. } => ()
746    }
747}
748"#,
749        );
750    }
751
752    #[test]
753    fn internal_or() {
754        check_diagnostics_no_bails(
755            r#"
756fn main() {
757    enum Either { A(bool), B }
758    match Either::B {
759        //^^^^^^^^^ error: missing match arm: `B` not covered
760        Either::A(true | false) => (),
761    }
762}
763"#,
764        );
765    }
766
767    #[test]
768    fn no_panic_at_unimplemented_subpattern_type() {
769        cov_mark::check_count!(validate_match_bailed_out, 1);
770
771        check_diagnostics(
772            r#"
773struct S { a: char}
774fn main(v: S) {
775    match v { S{ a }      => { _ = a; } }
776    match v { S{ a: _x }  => {} }
777    match v { S{ a: 'a' } => {} }
778    match v { S{..}       => {} }
779    match v { _           => {} }
780    match v { }
781        //^ error: missing match arm: type `S` is non-empty
782}
783"#,
784        );
785    }
786
787    #[test]
788    fn binding() {
789        check_diagnostics_no_bails(
790            r#"
791fn main() {
792    match true {
793        _x @ true => {}
794        false     => {}
795    }
796    match true { _x @ true => {} }
797        //^^^^ error: missing match arm: `false` not covered
798}
799"#,
800        );
801    }
802
803    #[test]
804    fn binding_ref_has_correct_type() {
805        // Asserts `PatKind::Binding(ref _x): bool`, not &bool.
806        // If that's not true match checking will panic with "incompatible constructors"
807        // FIXME: make facilities to test this directly like `tests::check_infer(..)`
808        check_diagnostics_no_bails(
809            r#"
810enum Foo { A }
811fn main() {
812    match Foo::A {
813        ref _x => {}
814        Foo::A => {}
815    }
816    match (true,) {
817        (ref _x,) => {}
818        (true,) => {}
819    }
820}
821"#,
822        );
823    }
824
825    #[test]
826    fn enum_non_exhaustive() {
827        check_diagnostics_no_bails(
828            r#"
829//- /lib.rs crate:lib
830#[non_exhaustive]
831pub enum E { A, B }
832fn _local() {
833    match E::A { _ => {} }
834    match E::A {
835        E::A => {}
836        E::B => {}
837    }
838    match E::A {
839        E::A | E::B => {}
840    }
841}
842
843//- /main.rs crate:main deps:lib
844use lib::E;
845fn main() {
846    match E::A { _ => {} }
847    match E::A {
848        //^^^^ error: missing match arm: `_` not covered
849        E::A => {}
850        E::B => {}
851    }
852    match E::A {
853        //^^^^ error: missing match arm: `_` not covered
854        E::A | E::B => {}
855    }
856}
857"#,
858        );
859    }
860
861    #[test]
862    fn match_guard() {
863        check_diagnostics_no_bails(
864            r#"
865fn main() {
866    match true {
867        true if false => {}
868        true          => {}
869        false         => {}
870    }
871    match true {
872        //^^^^ error: missing match arm: `true` not covered
873        true if false => {}
874        false         => {}
875    }
876}
877"#,
878        );
879    }
880
881    #[test]
882    fn pattern_type_is_of_substitution() {
883        check_diagnostics_no_bails(
884            r#"
885struct Foo<T>(T);
886struct Bar;
887fn main() {
888    match Foo(Bar) {
889        _ | Foo(Bar) => {}
890    }
891}
892"#,
893        );
894    }
895
896    #[test]
897    fn record_struct_no_such_field() {
898        cov_mark::check_count!(validate_match_bailed_out, 1);
899
900        check_diagnostics(
901            r#"
902struct Foo { }
903fn main(f: Foo) {
904    match f { Foo { bar } => () }
905                 // ^^^ error: no such field
906}
907"#,
908        );
909    }
910
911    #[test]
912    fn match_ergonomics_issue_9095() {
913        check_diagnostics_no_bails(
914            r#"
915enum Foo<T> { A(T) }
916fn main() {
917    match &Foo::A(true) {
918        _ => {}
919        Foo::A(_) => {}
920    }
921}
922"#,
923        );
924    }
925
926    #[test]
927    fn normalize_field_ty() {
928        check_diagnostics_no_bails(
929            r"
930trait Trait { type Projection; }
931enum E {Foo, Bar}
932struct A;
933impl Trait for A { type Projection = E; }
934struct Next<T: Trait>(T::Projection);
935static __: () = {
936    let n: Next<A> = Next(E::Foo);
937    match n { Next(E::Foo) => {} }
938    //    ^ error: missing match arm: `Next(Bar)` not covered
939    match n { Next(E::Foo | E::Bar) => {} }
940    match n { Next(E::Foo | _     ) => {} }
941    match n { Next(_      | E::Bar) => {} }
942    match n {      _ | Next(E::Bar) => {} }
943    match &n { Next(E::Foo | E::Bar) => {} }
944    match &n {      _ | Next(E::Bar) => {} }
945};",
946        );
947    }
948
949    #[test]
950    fn binding_mode_by_ref() {
951        check_diagnostics_no_bails(
952            r"
953enum E{ A, B }
954fn foo() {
955    match &E::A {
956        E::A => {}
957        _x => {}
958    }
959}",
960        );
961    }
962
963    #[test]
964    fn macro_or_pat() {
965        check_diagnostics_no_bails(
966            r#"
967macro_rules! m {
968    () => {
969        Enum::Type1 | Enum::Type2
970    };
971}
972
973enum Enum {
974    Type1,
975    Type2,
976    Type3,
977}
978
979fn f(ty: Enum) {
980    match ty {
981        //^^ error: missing match arm: `Type3` not covered
982        m!() => (),
983    }
984
985    match ty {
986        m!() | Enum::Type3 => ()
987    }
988}
989"#,
990        );
991    }
992
993    #[test]
994    fn unexpected_ty_fndef() {
995        cov_mark::check!(validate_match_bailed_out);
996        check_diagnostics_with_disabled(
997            r"
998enum Exp {
999    Tuple(()),
1000}
1001fn f() {
1002    match __unknown {
1003        Exp::Tuple => {}
1004    }
1005}",
1006            &["E0425"],
1007        );
1008    }
1009
1010    #[test]
1011    fn exponential_match() {
1012        if skip_slow_tests() {
1013            return;
1014        }
1015        // Constructs a match where match checking takes exponential time. Ensures we bail early.
1016        use std::fmt::Write;
1017        let struct_arity = 50;
1018        let mut code = String::new();
1019        write!(code, "struct BigStruct {{").unwrap();
1020        for i in 0..struct_arity {
1021            write!(code, "  field{i}: bool,").unwrap();
1022        }
1023        write!(code, "}}").unwrap();
1024        write!(code, "fn big_match(s: BigStruct) {{").unwrap();
1025        write!(code, "  match s {{").unwrap();
1026        for i in 0..struct_arity {
1027            write!(code, "    BigStruct {{ field{i}: true, ..}} => {{}},").unwrap();
1028            write!(code, "    BigStruct {{ field{i}: false, ..}} => {{}},").unwrap();
1029        }
1030        write!(code, "    _ => {{}},").unwrap();
1031        write!(code, "  }}").unwrap();
1032        write!(code, "}}").unwrap();
1033        check_diagnostics_no_bails(&code);
1034    }
1035
1036    #[test]
1037    fn min_exhaustive() {
1038        check_diagnostics(
1039            r#"
1040//- minicore: result
1041fn test(x: Result<i32, !>) {
1042    match x {
1043        Ok(_y) => {}
1044    }
1045}
1046"#,
1047        );
1048        check_diagnostics(
1049            r#"
1050//- minicore: result
1051fn test(ptr: *const Result<i32, !>) {
1052    unsafe {
1053        match *ptr {
1054            //^^^^ error: missing match arm: `Err(!)` not covered
1055            Ok(_x) => {}
1056        }
1057    }
1058}
1059"#,
1060        );
1061        check_diagnostics(
1062            r#"
1063//- minicore: result
1064fn test(x: Result<i32, &'static !>) {
1065    match x {
1066        //^ error: missing match arm: `Err(_)` not covered
1067        Ok(_y) => {}
1068    }
1069}
1070"#,
1071        );
1072    }
1073
1074    mod rust_unstable {
1075        use super::*;
1076
1077        #[test]
1078        fn rfc_1872_exhaustive_patterns() {
1079            check_diagnostics_no_bails(
1080                r"
1081//- minicore: option, result
1082#![feature(exhaustive_patterns)]
1083enum Void {}
1084fn test() {
1085    match None::<!> { None => () }
1086    match Result::<u8, !>::Ok(2) { Ok(_) => () }
1087    match Result::<u8, Void>::Ok(2) { Ok(_) => () }
1088    match (2, loop {}) {}
1089    match Result::<!, !>::Ok(loop {}) {}
1090    match (&loop {}) {} // https://github.com/rust-lang/rust/issues/50642#issuecomment-388234919
1091    //    ^^^^^^^^^^ error: missing match arm: type `&!` is non-empty
1092}",
1093            );
1094        }
1095
1096        #[test]
1097        fn rfc_1872_private_uninhabitedness() {
1098            check_diagnostics_no_bails(
1099                r"
1100//- minicore: option
1101//- /lib.rs crate:lib
1102#![feature(exhaustive_patterns)]
1103pub struct PrivatelyUninhabited { private_field: Void }
1104enum Void {}
1105fn test_local(x: Option<PrivatelyUninhabited>) {
1106    match x {}
1107} //      ^ error: missing match arm: `None` not covered
1108//- /main.rs crate:main deps:lib
1109#![feature(exhaustive_patterns)]
1110fn test(x: Option<lib::PrivatelyUninhabited>) {
1111    match x {}
1112    //    ^ error: missing match arm: `None` and `Some(_)` not covered
1113}",
1114            );
1115        }
1116    }
1117
1118    #[test]
1119    fn non_exhaustive_may_be_empty() {
1120        check_diagnostics_no_bails(
1121            r"
1122//- /main.rs crate:main deps:dep
1123// In a different crate
1124fn empty_match_on_empty_struct<T>(x: dep::UninhabitedStruct) -> T {
1125    match x {}
1126}
1127//- /dep.rs crate:dep
1128#[non_exhaustive]
1129pub struct UninhabitedStruct {
1130    pub never: !,
1131    // other fields
1132}
1133",
1134        );
1135    }
1136
1137    mod false_negatives {
1138        //! The implementation of match checking here is a work in progress. As we roll this out, we
1139        //! prefer false negatives to false positives (ideally there would be no false positives). This
1140        //! test module should document known false negatives. Eventually we will have a complete
1141        //! implementation of match checking and this module will be empty.
1142        //!
1143        //! The reasons for documenting known false negatives:
1144        //!
1145        //!   1. It acts as a backlog of work that can be done to improve the behavior of the system.
1146        //!   2. It ensures the code doesn't panic when handling these cases.
1147        use super::*;
1148
1149        #[test]
1150        fn integers() {
1151            cov_mark::check_count!(validate_match_bailed_out, 1);
1152
1153            // We don't currently check integer exhaustiveness.
1154            check_diagnostics(
1155                r#"
1156fn main() {
1157    match 5 {
1158        10 => (),
1159        11..20 => (),
1160    }
1161}
1162"#,
1163            );
1164        }
1165
1166        #[test]
1167        fn reference_patterns_at_top_level() {
1168            cov_mark::check_count!(validate_match_bailed_out, 1);
1169
1170            check_diagnostics(
1171                r#"
1172//- minicore: copy
1173fn main() {
1174    match &false {
1175        &true => {}
1176    }
1177}
1178            "#,
1179            );
1180        }
1181
1182        #[test]
1183        fn reference_patterns_in_fields() {
1184            cov_mark::check_count!(validate_match_bailed_out, 1);
1185            check_diagnostics(
1186                r#"
1187//- minicore: copy
1188fn main() {
1189    match (&false,) {
1190        //^^^^^^^^^ error: missing match arm: `(&false,)` not covered
1191        (true,) => {}
1192    }
1193    match (&false,) {
1194        (&true,) => {}
1195    }
1196}
1197            "#,
1198            );
1199        }
1200    }
1201}