diff options
author | Aleksey Kladov <[email protected]> | 2021-06-13 19:44:31 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2021-06-13 19:44:31 +0100 |
commit | b292e1b9da39813e2739cb450c263e7502c97c8d (patch) | |
tree | 92a09f547c7d7ca41f90ebdc68f0f81ac1805560 | |
parent | 3478897f86cc1b3e3f83e9d4e7cedff41721fb04 (diff) |
internal: refactor missing match arms diagnostics
-rw-r--r-- | crates/hir/src/diagnostics.rs | 19 | ||||
-rw-r--r-- | crates/hir/src/lib.rs | 13 | ||||
-rw-r--r-- | crates/ide/src/diagnostics.rs | 909 | ||||
-rw-r--r-- | crates/ide/src/diagnostics/missing_match_arms.rs | 924 |
4 files changed, 935 insertions, 930 deletions
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index c2d608eb5..5cffef47f 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs | |||
@@ -38,6 +38,7 @@ diagnostics![ | |||
38 | MacroError, | 38 | MacroError, |
39 | MismatchedArgCount, | 39 | MismatchedArgCount, |
40 | MissingFields, | 40 | MissingFields, |
41 | MissingMatchArms, | ||
41 | MissingOkOrSomeInTailExpr, | 42 | MissingOkOrSomeInTailExpr, |
42 | MissingUnsafe, | 43 | MissingUnsafe, |
43 | NoSuchField, | 44 | NoSuchField, |
@@ -149,9 +150,6 @@ pub struct MissingOkOrSomeInTailExpr { | |||
149 | pub required: String, | 150 | pub required: String, |
150 | } | 151 | } |
151 | 152 | ||
152 | // Diagnostic: missing-match-arm | ||
153 | // | ||
154 | // This diagnostic is triggered if `match` block is missing one or more match arms. | ||
155 | #[derive(Debug)] | 153 | #[derive(Debug)] |
156 | pub struct MissingMatchArms { | 154 | pub struct MissingMatchArms { |
157 | pub file: HirFileId, | 155 | pub file: HirFileId, |
@@ -159,21 +157,6 @@ pub struct MissingMatchArms { | |||
159 | pub arms: AstPtr<ast::MatchArmList>, | 157 | pub arms: AstPtr<ast::MatchArmList>, |
160 | } | 158 | } |
161 | 159 | ||
162 | impl Diagnostic for MissingMatchArms { | ||
163 | fn code(&self) -> DiagnosticCode { | ||
164 | DiagnosticCode("missing-match-arm") | ||
165 | } | ||
166 | fn message(&self) -> String { | ||
167 | String::from("Missing match arm") | ||
168 | } | ||
169 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
170 | InFile { file_id: self.file, value: self.match_expr.clone().into() } | ||
171 | } | ||
172 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
173 | self | ||
174 | } | ||
175 | } | ||
176 | |||
177 | #[derive(Debug)] | 160 | #[derive(Debug)] |
178 | pub struct InternalBailedOut { | 161 | pub struct InternalBailedOut { |
179 | pub file: HirFileId, | 162 | pub file: HirFileId, |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index fc147ade3..2e794ff4a 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -1209,11 +1209,14 @@ impl Function { | |||
1209 | if let (Some(match_expr), Some(arms)) = | 1209 | if let (Some(match_expr), Some(arms)) = |
1210 | (match_expr.expr(), match_expr.match_arm_list()) | 1210 | (match_expr.expr(), match_expr.match_arm_list()) |
1211 | { | 1211 | { |
1212 | sink.push(MissingMatchArms { | 1212 | acc.push( |
1213 | file: source_ptr.file_id, | 1213 | MissingMatchArms { |
1214 | match_expr: AstPtr::new(&match_expr), | 1214 | file: source_ptr.file_id, |
1215 | arms: AstPtr::new(&arms), | 1215 | match_expr: AstPtr::new(&match_expr), |
1216 | }) | 1216 | arms: AstPtr::new(&arms), |
1217 | } | ||
1218 | .into(), | ||
1219 | ) | ||
1217 | } | 1220 | } |
1218 | } | 1221 | } |
1219 | } | 1222 | } |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 7978c1fc2..01b68232e 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -10,6 +10,7 @@ mod incorrect_case; | |||
10 | mod macro_error; | 10 | mod macro_error; |
11 | mod mismatched_arg_count; | 11 | mod mismatched_arg_count; |
12 | mod missing_fields; | 12 | mod missing_fields; |
13 | mod missing_match_arms; | ||
13 | mod missing_ok_or_some_in_tail_expr; | 14 | mod missing_ok_or_some_in_tail_expr; |
14 | mod missing_unsafe; | 15 | mod missing_unsafe; |
15 | mod no_such_field; | 16 | mod no_such_field; |
@@ -205,6 +206,7 @@ pub(crate) fn diagnostics( | |||
205 | AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d), | 206 | AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d), |
206 | AnyDiagnostic::MismatchedArgCount(d) => mismatched_arg_count::mismatched_arg_count(&ctx, &d), | 207 | AnyDiagnostic::MismatchedArgCount(d) => mismatched_arg_count::mismatched_arg_count(&ctx, &d), |
207 | AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d), | 208 | AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d), |
209 | AnyDiagnostic::MissingMatchArms(d) => missing_match_arms::missing_match_arms(&ctx, &d), | ||
208 | AnyDiagnostic::MissingOkOrSomeInTailExpr(d) => missing_ok_or_some_in_tail_expr::missing_ok_or_some_in_tail_expr(&ctx, &d), | 210 | AnyDiagnostic::MissingOkOrSomeInTailExpr(d) => missing_ok_or_some_in_tail_expr::missing_ok_or_some_in_tail_expr(&ctx, &d), |
209 | AnyDiagnostic::MissingUnsafe(d) => missing_unsafe::missing_unsafe(&ctx, &d), | 211 | AnyDiagnostic::MissingUnsafe(d) => missing_unsafe::missing_unsafe(&ctx, &d), |
210 | AnyDiagnostic::NoSuchField(d) => no_such_field::no_such_field(&ctx, &d), | 212 | AnyDiagnostic::NoSuchField(d) => no_such_field::no_such_field(&ctx, &d), |
@@ -545,910 +547,3 @@ pub struct Claims { | |||
545 | ); | 547 | ); |
546 | } | 548 | } |
547 | } | 549 | } |
548 | |||
549 | #[cfg(test)] | ||
550 | pub(super) mod match_check_tests { | ||
551 | use crate::diagnostics::tests::check_diagnostics; | ||
552 | |||
553 | #[test] | ||
554 | fn empty_tuple() { | ||
555 | check_diagnostics( | ||
556 | r#" | ||
557 | fn main() { | ||
558 | match () { } | ||
559 | //^^ Missing match arm | ||
560 | match (()) { } | ||
561 | //^^^^ Missing match arm | ||
562 | |||
563 | match () { _ => (), } | ||
564 | match () { () => (), } | ||
565 | match (()) { (()) => (), } | ||
566 | } | ||
567 | "#, | ||
568 | ); | ||
569 | } | ||
570 | |||
571 | #[test] | ||
572 | fn tuple_of_two_empty_tuple() { | ||
573 | check_diagnostics( | ||
574 | r#" | ||
575 | fn main() { | ||
576 | match ((), ()) { } | ||
577 | //^^^^^^^^ Missing match arm | ||
578 | |||
579 | match ((), ()) { ((), ()) => (), } | ||
580 | } | ||
581 | "#, | ||
582 | ); | ||
583 | } | ||
584 | |||
585 | #[test] | ||
586 | fn boolean() { | ||
587 | check_diagnostics( | ||
588 | r#" | ||
589 | fn test_main() { | ||
590 | match false { } | ||
591 | //^^^^^ Missing match arm | ||
592 | match false { true => (), } | ||
593 | //^^^^^ Missing match arm | ||
594 | match (false, true) {} | ||
595 | //^^^^^^^^^^^^^ Missing match arm | ||
596 | match (false, true) { (true, true) => (), } | ||
597 | //^^^^^^^^^^^^^ Missing match arm | ||
598 | match (false, true) { | ||
599 | //^^^^^^^^^^^^^ Missing match arm | ||
600 | (false, true) => (), | ||
601 | (false, false) => (), | ||
602 | (true, false) => (), | ||
603 | } | ||
604 | match (false, true) { (true, _x) => (), } | ||
605 | //^^^^^^^^^^^^^ Missing match arm | ||
606 | |||
607 | match false { true => (), false => (), } | ||
608 | match (false, true) { | ||
609 | (false, _) => (), | ||
610 | (true, false) => (), | ||
611 | (_, true) => (), | ||
612 | } | ||
613 | match (false, true) { | ||
614 | (true, true) => (), | ||
615 | (true, false) => (), | ||
616 | (false, true) => (), | ||
617 | (false, false) => (), | ||
618 | } | ||
619 | match (false, true) { | ||
620 | (true, _x) => (), | ||
621 | (false, true) => (), | ||
622 | (false, false) => (), | ||
623 | } | ||
624 | match (false, true, false) { | ||
625 | (false, ..) => (), | ||
626 | (true, ..) => (), | ||
627 | } | ||
628 | match (false, true, false) { | ||
629 | (.., false) => (), | ||
630 | (.., true) => (), | ||
631 | } | ||
632 | match (false, true, false) { (..) => (), } | ||
633 | } | ||
634 | "#, | ||
635 | ); | ||
636 | } | ||
637 | |||
638 | #[test] | ||
639 | fn tuple_of_tuple_and_bools() { | ||
640 | check_diagnostics( | ||
641 | r#" | ||
642 | fn main() { | ||
643 | match (false, ((), false)) {} | ||
644 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
645 | match (false, ((), false)) { (true, ((), true)) => (), } | ||
646 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
647 | match (false, ((), false)) { (true, _) => (), } | ||
648 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
649 | |||
650 | match (false, ((), false)) { | ||
651 | (true, ((), true)) => (), | ||
652 | (true, ((), false)) => (), | ||
653 | (false, ((), true)) => (), | ||
654 | (false, ((), false)) => (), | ||
655 | } | ||
656 | match (false, ((), false)) { | ||
657 | (true, ((), true)) => (), | ||
658 | (true, ((), false)) => (), | ||
659 | (false, _) => (), | ||
660 | } | ||
661 | } | ||
662 | "#, | ||
663 | ); | ||
664 | } | ||
665 | |||
666 | #[test] | ||
667 | fn enums() { | ||
668 | check_diagnostics( | ||
669 | r#" | ||
670 | enum Either { A, B, } | ||
671 | |||
672 | fn main() { | ||
673 | match Either::A { } | ||
674 | //^^^^^^^^^ Missing match arm | ||
675 | match Either::B { Either::A => (), } | ||
676 | //^^^^^^^^^ Missing match arm | ||
677 | |||
678 | match &Either::B { | ||
679 | //^^^^^^^^^^ Missing match arm | ||
680 | Either::A => (), | ||
681 | } | ||
682 | |||
683 | match Either::B { | ||
684 | Either::A => (), Either::B => (), | ||
685 | } | ||
686 | match &Either::B { | ||
687 | Either::A => (), Either::B => (), | ||
688 | } | ||
689 | } | ||
690 | "#, | ||
691 | ); | ||
692 | } | ||
693 | |||
694 | #[test] | ||
695 | fn enum_containing_bool() { | ||
696 | check_diagnostics( | ||
697 | r#" | ||
698 | enum Either { A(bool), B } | ||
699 | |||
700 | fn main() { | ||
701 | match Either::B { } | ||
702 | //^^^^^^^^^ Missing match arm | ||
703 | match Either::B { | ||
704 | //^^^^^^^^^ Missing match arm | ||
705 | Either::A(true) => (), Either::B => () | ||
706 | } | ||
707 | |||
708 | match Either::B { | ||
709 | Either::A(true) => (), | ||
710 | Either::A(false) => (), | ||
711 | Either::B => (), | ||
712 | } | ||
713 | match Either::B { | ||
714 | Either::B => (), | ||
715 | _ => (), | ||
716 | } | ||
717 | match Either::B { | ||
718 | Either::A(_) => (), | ||
719 | Either::B => (), | ||
720 | } | ||
721 | |||
722 | } | ||
723 | "#, | ||
724 | ); | ||
725 | } | ||
726 | |||
727 | #[test] | ||
728 | fn enum_different_sizes() { | ||
729 | check_diagnostics( | ||
730 | r#" | ||
731 | enum Either { A(bool), B(bool, bool) } | ||
732 | |||
733 | fn main() { | ||
734 | match Either::A(false) { | ||
735 | //^^^^^^^^^^^^^^^^ Missing match arm | ||
736 | Either::A(_) => (), | ||
737 | Either::B(false, _) => (), | ||
738 | } | ||
739 | |||
740 | match Either::A(false) { | ||
741 | Either::A(_) => (), | ||
742 | Either::B(true, _) => (), | ||
743 | Either::B(false, _) => (), | ||
744 | } | ||
745 | match Either::A(false) { | ||
746 | Either::A(true) | Either::A(false) => (), | ||
747 | Either::B(true, _) => (), | ||
748 | Either::B(false, _) => (), | ||
749 | } | ||
750 | } | ||
751 | "#, | ||
752 | ); | ||
753 | } | ||
754 | |||
755 | #[test] | ||
756 | fn tuple_of_enum_no_diagnostic() { | ||
757 | check_diagnostics( | ||
758 | r#" | ||
759 | enum Either { A(bool), B(bool, bool) } | ||
760 | enum Either2 { C, D } | ||
761 | |||
762 | fn main() { | ||
763 | match (Either::A(false), Either2::C) { | ||
764 | (Either::A(true), _) | (Either::A(false), _) => (), | ||
765 | (Either::B(true, _), Either2::C) => (), | ||
766 | (Either::B(false, _), Either2::C) => (), | ||
767 | (Either::B(_, _), Either2::D) => (), | ||
768 | } | ||
769 | } | ||
770 | "#, | ||
771 | ); | ||
772 | } | ||
773 | |||
774 | #[test] | ||
775 | fn or_pattern_no_diagnostic() { | ||
776 | check_diagnostics( | ||
777 | r#" | ||
778 | enum Either {A, B} | ||
779 | |||
780 | fn main() { | ||
781 | match (Either::A, Either::B) { | ||
782 | (Either::A | Either::B, _) => (), | ||
783 | } | ||
784 | }"#, | ||
785 | ) | ||
786 | } | ||
787 | |||
788 | #[test] | ||
789 | fn mismatched_types() { | ||
790 | // Match statements with arms that don't match the | ||
791 | // expression pattern do not fire this diagnostic. | ||
792 | check_diagnostics( | ||
793 | r#" | ||
794 | enum Either { A, B } | ||
795 | enum Either2 { C, D } | ||
796 | |||
797 | fn main() { | ||
798 | match Either::A { | ||
799 | Either2::C => (), | ||
800 | // ^^^^^^^^^^ Internal: match check bailed out | ||
801 | Either2::D => (), | ||
802 | } | ||
803 | match (true, false) { | ||
804 | (true, false, true) => (), | ||
805 | // ^^^^^^^^^^^^^^^^^^^ Internal: match check bailed out | ||
806 | (true) => (), | ||
807 | } | ||
808 | match (true, false) { (true,) => {} } | ||
809 | // ^^^^^^^ Internal: match check bailed out | ||
810 | match (0) { () => () } | ||
811 | // ^^ Internal: match check bailed out | ||
812 | match Unresolved::Bar { Unresolved::Baz => () } | ||
813 | } | ||
814 | "#, | ||
815 | ); | ||
816 | } | ||
817 | |||
818 | #[test] | ||
819 | fn mismatched_types_in_or_patterns() { | ||
820 | check_diagnostics( | ||
821 | r#" | ||
822 | fn main() { | ||
823 | match false { true | () => {} } | ||
824 | // ^^^^^^^^^ Internal: match check bailed out | ||
825 | match (false,) { (true | (),) => {} } | ||
826 | // ^^^^^^^^^^^^ Internal: match check bailed out | ||
827 | } | ||
828 | "#, | ||
829 | ); | ||
830 | } | ||
831 | |||
832 | #[test] | ||
833 | fn malformed_match_arm_tuple_enum_missing_pattern() { | ||
834 | // We are testing to be sure we don't panic here when the match | ||
835 | // arm `Either::B` is missing its pattern. | ||
836 | check_diagnostics( | ||
837 | r#" | ||
838 | enum Either { A, B(u32) } | ||
839 | |||
840 | fn main() { | ||
841 | match Either::A { | ||
842 | Either::A => (), | ||
843 | Either::B() => (), | ||
844 | } | ||
845 | } | ||
846 | "#, | ||
847 | ); | ||
848 | } | ||
849 | |||
850 | #[test] | ||
851 | fn malformed_match_arm_extra_fields() { | ||
852 | check_diagnostics( | ||
853 | r#" | ||
854 | enum A { B(isize, isize), C } | ||
855 | fn main() { | ||
856 | match A::B(1, 2) { | ||
857 | A::B(_, _, _) => (), | ||
858 | // ^^^^^^^^^^^^^ Internal: match check bailed out | ||
859 | } | ||
860 | match A::B(1, 2) { | ||
861 | A::C(_) => (), | ||
862 | // ^^^^^^^ Internal: match check bailed out | ||
863 | } | ||
864 | } | ||
865 | "#, | ||
866 | ); | ||
867 | } | ||
868 | |||
869 | #[test] | ||
870 | fn expr_diverges() { | ||
871 | check_diagnostics( | ||
872 | r#" | ||
873 | enum Either { A, B } | ||
874 | |||
875 | fn main() { | ||
876 | match loop {} { | ||
877 | Either::A => (), | ||
878 | // ^^^^^^^^^ Internal: match check bailed out | ||
879 | Either::B => (), | ||
880 | } | ||
881 | match loop {} { | ||
882 | Either::A => (), | ||
883 | // ^^^^^^^^^ Internal: match check bailed out | ||
884 | } | ||
885 | match loop { break Foo::A } { | ||
886 | //^^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
887 | Either::A => (), | ||
888 | } | ||
889 | match loop { break Foo::A } { | ||
890 | Either::A => (), | ||
891 | Either::B => (), | ||
892 | } | ||
893 | } | ||
894 | "#, | ||
895 | ); | ||
896 | } | ||
897 | |||
898 | #[test] | ||
899 | fn expr_partially_diverges() { | ||
900 | check_diagnostics( | ||
901 | r#" | ||
902 | enum Either<T> { A(T), B } | ||
903 | |||
904 | fn foo() -> Either<!> { Either::B } | ||
905 | fn main() -> u32 { | ||
906 | match foo() { | ||
907 | Either::A(val) => val, | ||
908 | Either::B => 0, | ||
909 | } | ||
910 | } | ||
911 | "#, | ||
912 | ); | ||
913 | } | ||
914 | |||
915 | #[test] | ||
916 | fn enum_record() { | ||
917 | check_diagnostics( | ||
918 | r#" | ||
919 | enum Either { A { foo: bool }, B } | ||
920 | |||
921 | fn main() { | ||
922 | let a = Either::A { foo: true }; | ||
923 | match a { } | ||
924 | //^ Missing match arm | ||
925 | match a { Either::A { foo: true } => () } | ||
926 | //^ Missing match arm | ||
927 | match a { | ||
928 | Either::A { } => (), | ||
929 | //^^^^^^^^^ Missing structure fields: | ||
930 | // | - foo | ||
931 | Either::B => (), | ||
932 | } | ||
933 | match a { | ||
934 | //^ Missing match arm | ||
935 | Either::A { } => (), | ||
936 | } //^^^^^^^^^ Missing structure fields: | ||
937 | // | - foo | ||
938 | |||
939 | match a { | ||
940 | Either::A { foo: true } => (), | ||
941 | Either::A { foo: false } => (), | ||
942 | Either::B => (), | ||
943 | } | ||
944 | match a { | ||
945 | Either::A { foo: _ } => (), | ||
946 | Either::B => (), | ||
947 | } | ||
948 | } | ||
949 | "#, | ||
950 | ); | ||
951 | } | ||
952 | |||
953 | #[test] | ||
954 | fn enum_record_fields_out_of_order() { | ||
955 | check_diagnostics( | ||
956 | r#" | ||
957 | enum Either { | ||
958 | A { foo: bool, bar: () }, | ||
959 | B, | ||
960 | } | ||
961 | |||
962 | fn main() { | ||
963 | let a = Either::A { foo: true, bar: () }; | ||
964 | match a { | ||
965 | //^ Missing match arm | ||
966 | Either::A { bar: (), foo: false } => (), | ||
967 | Either::A { foo: true, bar: () } => (), | ||
968 | } | ||
969 | |||
970 | match a { | ||
971 | Either::A { bar: (), foo: false } => (), | ||
972 | Either::A { foo: true, bar: () } => (), | ||
973 | Either::B => (), | ||
974 | } | ||
975 | } | ||
976 | "#, | ||
977 | ); | ||
978 | } | ||
979 | |||
980 | #[test] | ||
981 | fn enum_record_ellipsis() { | ||
982 | check_diagnostics( | ||
983 | r#" | ||
984 | enum Either { | ||
985 | A { foo: bool, bar: bool }, | ||
986 | B, | ||
987 | } | ||
988 | |||
989 | fn main() { | ||
990 | let a = Either::B; | ||
991 | match a { | ||
992 | //^ Missing match arm | ||
993 | Either::A { foo: true, .. } => (), | ||
994 | Either::B => (), | ||
995 | } | ||
996 | match a { | ||
997 | //^ Missing match arm | ||
998 | Either::A { .. } => (), | ||
999 | } | ||
1000 | |||
1001 | match a { | ||
1002 | Either::A { foo: true, .. } => (), | ||
1003 | Either::A { foo: false, .. } => (), | ||
1004 | Either::B => (), | ||
1005 | } | ||
1006 | |||
1007 | match a { | ||
1008 | Either::A { .. } => (), | ||
1009 | Either::B => (), | ||
1010 | } | ||
1011 | } | ||
1012 | "#, | ||
1013 | ); | ||
1014 | } | ||
1015 | |||
1016 | #[test] | ||
1017 | fn enum_tuple_partial_ellipsis() { | ||
1018 | check_diagnostics( | ||
1019 | r#" | ||
1020 | enum Either { | ||
1021 | A(bool, bool, bool, bool), | ||
1022 | B, | ||
1023 | } | ||
1024 | |||
1025 | fn main() { | ||
1026 | match Either::B { | ||
1027 | //^^^^^^^^^ Missing match arm | ||
1028 | Either::A(true, .., true) => (), | ||
1029 | Either::A(true, .., false) => (), | ||
1030 | Either::A(false, .., false) => (), | ||
1031 | Either::B => (), | ||
1032 | } | ||
1033 | match Either::B { | ||
1034 | //^^^^^^^^^ Missing match arm | ||
1035 | Either::A(true, .., true) => (), | ||
1036 | Either::A(true, .., false) => (), | ||
1037 | Either::A(.., true) => (), | ||
1038 | Either::B => (), | ||
1039 | } | ||
1040 | |||
1041 | match Either::B { | ||
1042 | Either::A(true, .., true) => (), | ||
1043 | Either::A(true, .., false) => (), | ||
1044 | Either::A(false, .., true) => (), | ||
1045 | Either::A(false, .., false) => (), | ||
1046 | Either::B => (), | ||
1047 | } | ||
1048 | match Either::B { | ||
1049 | Either::A(true, .., true) => (), | ||
1050 | Either::A(true, .., false) => (), | ||
1051 | Either::A(.., true) => (), | ||
1052 | Either::A(.., false) => (), | ||
1053 | Either::B => (), | ||
1054 | } | ||
1055 | } | ||
1056 | "#, | ||
1057 | ); | ||
1058 | } | ||
1059 | |||
1060 | #[test] | ||
1061 | fn never() { | ||
1062 | check_diagnostics( | ||
1063 | r#" | ||
1064 | enum Never {} | ||
1065 | |||
1066 | fn enum_(never: Never) { | ||
1067 | match never {} | ||
1068 | } | ||
1069 | fn enum_ref(never: &Never) { | ||
1070 | match never {} | ||
1071 | //^^^^^ Missing match arm | ||
1072 | } | ||
1073 | fn bang(never: !) { | ||
1074 | match never {} | ||
1075 | } | ||
1076 | "#, | ||
1077 | ); | ||
1078 | } | ||
1079 | |||
1080 | #[test] | ||
1081 | fn unknown_type() { | ||
1082 | check_diagnostics( | ||
1083 | r#" | ||
1084 | enum Option<T> { Some(T), None } | ||
1085 | |||
1086 | fn main() { | ||
1087 | // `Never` is deliberately not defined so that it's an uninferred type. | ||
1088 | match Option::<Never>::None { | ||
1089 | None => (), | ||
1090 | Some(never) => match never {}, | ||
1091 | // ^^^^^^^^^^^ Internal: match check bailed out | ||
1092 | } | ||
1093 | match Option::<Never>::None { | ||
1094 | //^^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
1095 | Option::Some(_never) => {}, | ||
1096 | } | ||
1097 | } | ||
1098 | "#, | ||
1099 | ); | ||
1100 | } | ||
1101 | |||
1102 | #[test] | ||
1103 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | ||
1104 | check_diagnostics( | ||
1105 | r#" | ||
1106 | fn main() { | ||
1107 | match (false, true, false) { | ||
1108 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
1109 | (false, ..) => (), | ||
1110 | } | ||
1111 | }"#, | ||
1112 | ); | ||
1113 | } | ||
1114 | |||
1115 | #[test] | ||
1116 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | ||
1117 | check_diagnostics( | ||
1118 | r#" | ||
1119 | fn main() { | ||
1120 | match (false, true, false) { | ||
1121 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
1122 | (.., false) => (), | ||
1123 | } | ||
1124 | }"#, | ||
1125 | ); | ||
1126 | } | ||
1127 | |||
1128 | #[test] | ||
1129 | fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() { | ||
1130 | check_diagnostics( | ||
1131 | r#" | ||
1132 | fn main() { | ||
1133 | match (false, true, false) { | ||
1134 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
1135 | (true, .., false) => (), | ||
1136 | } | ||
1137 | }"#, | ||
1138 | ); | ||
1139 | } | ||
1140 | |||
1141 | #[test] | ||
1142 | fn record_struct() { | ||
1143 | check_diagnostics( | ||
1144 | r#"struct Foo { a: bool } | ||
1145 | fn main(f: Foo) { | ||
1146 | match f {} | ||
1147 | //^ Missing match arm | ||
1148 | match f { Foo { a: true } => () } | ||
1149 | //^ Missing match arm | ||
1150 | match &f { Foo { a: true } => () } | ||
1151 | //^^ Missing match arm | ||
1152 | match f { Foo { a: _ } => () } | ||
1153 | match f { | ||
1154 | Foo { a: true } => (), | ||
1155 | Foo { a: false } => (), | ||
1156 | } | ||
1157 | match &f { | ||
1158 | Foo { a: true } => (), | ||
1159 | Foo { a: false } => (), | ||
1160 | } | ||
1161 | } | ||
1162 | "#, | ||
1163 | ); | ||
1164 | } | ||
1165 | |||
1166 | #[test] | ||
1167 | fn tuple_struct() { | ||
1168 | check_diagnostics( | ||
1169 | r#"struct Foo(bool); | ||
1170 | fn main(f: Foo) { | ||
1171 | match f {} | ||
1172 | //^ Missing match arm | ||
1173 | match f { Foo(true) => () } | ||
1174 | //^ Missing match arm | ||
1175 | match f { | ||
1176 | Foo(true) => (), | ||
1177 | Foo(false) => (), | ||
1178 | } | ||
1179 | } | ||
1180 | "#, | ||
1181 | ); | ||
1182 | } | ||
1183 | |||
1184 | #[test] | ||
1185 | fn unit_struct() { | ||
1186 | check_diagnostics( | ||
1187 | r#"struct Foo; | ||
1188 | fn main(f: Foo) { | ||
1189 | match f {} | ||
1190 | //^ Missing match arm | ||
1191 | match f { Foo => () } | ||
1192 | } | ||
1193 | "#, | ||
1194 | ); | ||
1195 | } | ||
1196 | |||
1197 | #[test] | ||
1198 | fn record_struct_ellipsis() { | ||
1199 | check_diagnostics( | ||
1200 | r#"struct Foo { foo: bool, bar: bool } | ||
1201 | fn main(f: Foo) { | ||
1202 | match f { Foo { foo: true, .. } => () } | ||
1203 | //^ Missing match arm | ||
1204 | match f { | ||
1205 | //^ Missing match arm | ||
1206 | Foo { foo: true, .. } => (), | ||
1207 | Foo { bar: false, .. } => () | ||
1208 | } | ||
1209 | match f { Foo { .. } => () } | ||
1210 | match f { | ||
1211 | Foo { foo: true, .. } => (), | ||
1212 | Foo { foo: false, .. } => () | ||
1213 | } | ||
1214 | } | ||
1215 | "#, | ||
1216 | ); | ||
1217 | } | ||
1218 | |||
1219 | #[test] | ||
1220 | fn internal_or() { | ||
1221 | check_diagnostics( | ||
1222 | r#" | ||
1223 | fn main() { | ||
1224 | enum Either { A(bool), B } | ||
1225 | match Either::B { | ||
1226 | //^^^^^^^^^ Missing match arm | ||
1227 | Either::A(true | false) => (), | ||
1228 | } | ||
1229 | } | ||
1230 | "#, | ||
1231 | ); | ||
1232 | } | ||
1233 | |||
1234 | #[test] | ||
1235 | fn no_panic_at_unimplemented_subpattern_type() { | ||
1236 | check_diagnostics( | ||
1237 | r#" | ||
1238 | struct S { a: char} | ||
1239 | fn main(v: S) { | ||
1240 | match v { S{ a } => {} } | ||
1241 | match v { S{ a: _x } => {} } | ||
1242 | match v { S{ a: 'a' } => {} } | ||
1243 | //^^^^^^^^^^^ Internal: match check bailed out | ||
1244 | match v { S{..} => {} } | ||
1245 | match v { _ => {} } | ||
1246 | match v { } | ||
1247 | //^ Missing match arm | ||
1248 | } | ||
1249 | "#, | ||
1250 | ); | ||
1251 | } | ||
1252 | |||
1253 | #[test] | ||
1254 | fn binding() { | ||
1255 | check_diagnostics( | ||
1256 | r#" | ||
1257 | fn main() { | ||
1258 | match true { | ||
1259 | _x @ true => {} | ||
1260 | false => {} | ||
1261 | } | ||
1262 | match true { _x @ true => {} } | ||
1263 | //^^^^ Missing match arm | ||
1264 | } | ||
1265 | "#, | ||
1266 | ); | ||
1267 | } | ||
1268 | |||
1269 | #[test] | ||
1270 | fn binding_ref_has_correct_type() { | ||
1271 | // Asserts `PatKind::Binding(ref _x): bool`, not &bool. | ||
1272 | // If that's not true match checking will panic with "incompatible constructors" | ||
1273 | // FIXME: make facilities to test this directly like `tests::check_infer(..)` | ||
1274 | check_diagnostics( | ||
1275 | r#" | ||
1276 | enum Foo { A } | ||
1277 | fn main() { | ||
1278 | // FIXME: this should not bail out but current behavior is such as the old algorithm. | ||
1279 | // ExprValidator::validate_match(..) checks types of top level patterns incorrecly. | ||
1280 | match Foo::A { | ||
1281 | ref _x => {} | ||
1282 | // ^^^^^^ Internal: match check bailed out | ||
1283 | Foo::A => {} | ||
1284 | } | ||
1285 | match (true,) { | ||
1286 | (ref _x,) => {} | ||
1287 | (true,) => {} | ||
1288 | } | ||
1289 | } | ||
1290 | "#, | ||
1291 | ); | ||
1292 | } | ||
1293 | |||
1294 | #[test] | ||
1295 | fn enum_non_exhaustive() { | ||
1296 | check_diagnostics( | ||
1297 | r#" | ||
1298 | //- /lib.rs crate:lib | ||
1299 | #[non_exhaustive] | ||
1300 | pub enum E { A, B } | ||
1301 | fn _local() { | ||
1302 | match E::A { _ => {} } | ||
1303 | match E::A { | ||
1304 | E::A => {} | ||
1305 | E::B => {} | ||
1306 | } | ||
1307 | match E::A { | ||
1308 | E::A | E::B => {} | ||
1309 | } | ||
1310 | } | ||
1311 | |||
1312 | //- /main.rs crate:main deps:lib | ||
1313 | use lib::E; | ||
1314 | fn main() { | ||
1315 | match E::A { _ => {} } | ||
1316 | match E::A { | ||
1317 | //^^^^ Missing match arm | ||
1318 | E::A => {} | ||
1319 | E::B => {} | ||
1320 | } | ||
1321 | match E::A { | ||
1322 | //^^^^ Missing match arm | ||
1323 | E::A | E::B => {} | ||
1324 | } | ||
1325 | } | ||
1326 | "#, | ||
1327 | ); | ||
1328 | } | ||
1329 | |||
1330 | #[test] | ||
1331 | fn match_guard() { | ||
1332 | check_diagnostics( | ||
1333 | r#" | ||
1334 | fn main() { | ||
1335 | match true { | ||
1336 | true if false => {} | ||
1337 | true => {} | ||
1338 | false => {} | ||
1339 | } | ||
1340 | match true { | ||
1341 | //^^^^ Missing match arm | ||
1342 | true if false => {} | ||
1343 | false => {} | ||
1344 | } | ||
1345 | } | ||
1346 | "#, | ||
1347 | ); | ||
1348 | } | ||
1349 | |||
1350 | #[test] | ||
1351 | fn pattern_type_is_of_substitution() { | ||
1352 | cov_mark::check!(match_check_wildcard_expanded_to_substitutions); | ||
1353 | check_diagnostics( | ||
1354 | r#" | ||
1355 | struct Foo<T>(T); | ||
1356 | struct Bar; | ||
1357 | fn main() { | ||
1358 | match Foo(Bar) { | ||
1359 | _ | Foo(Bar) => {} | ||
1360 | } | ||
1361 | } | ||
1362 | "#, | ||
1363 | ); | ||
1364 | } | ||
1365 | |||
1366 | #[test] | ||
1367 | fn record_struct_no_such_field() { | ||
1368 | check_diagnostics( | ||
1369 | r#" | ||
1370 | struct Foo { } | ||
1371 | fn main(f: Foo) { | ||
1372 | match f { Foo { bar } => () } | ||
1373 | // ^^^^^^^^^^^ Internal: match check bailed out | ||
1374 | } | ||
1375 | "#, | ||
1376 | ); | ||
1377 | } | ||
1378 | |||
1379 | #[test] | ||
1380 | fn match_ergonomics_issue_9095() { | ||
1381 | check_diagnostics( | ||
1382 | r#" | ||
1383 | enum Foo<T> { A(T) } | ||
1384 | fn main() { | ||
1385 | match &Foo::A(true) { | ||
1386 | _ => {} | ||
1387 | Foo::A(_) => {} | ||
1388 | } | ||
1389 | } | ||
1390 | "#, | ||
1391 | ); | ||
1392 | } | ||
1393 | |||
1394 | mod false_negatives { | ||
1395 | //! The implementation of match checking here is a work in progress. As we roll this out, we | ||
1396 | //! prefer false negatives to false positives (ideally there would be no false positives). This | ||
1397 | //! test module should document known false negatives. Eventually we will have a complete | ||
1398 | //! implementation of match checking and this module will be empty. | ||
1399 | //! | ||
1400 | //! The reasons for documenting known false negatives: | ||
1401 | //! | ||
1402 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. | ||
1403 | //! 2. It ensures the code doesn't panic when handling these cases. | ||
1404 | use super::*; | ||
1405 | |||
1406 | #[test] | ||
1407 | fn integers() { | ||
1408 | // We don't currently check integer exhaustiveness. | ||
1409 | check_diagnostics( | ||
1410 | r#" | ||
1411 | fn main() { | ||
1412 | match 5 { | ||
1413 | 10 => (), | ||
1414 | // ^^ Internal: match check bailed out | ||
1415 | 11..20 => (), | ||
1416 | } | ||
1417 | } | ||
1418 | "#, | ||
1419 | ); | ||
1420 | } | ||
1421 | |||
1422 | #[test] | ||
1423 | fn reference_patterns_at_top_level() { | ||
1424 | check_diagnostics( | ||
1425 | r#" | ||
1426 | fn main() { | ||
1427 | match &false { | ||
1428 | &true => {} | ||
1429 | // ^^^^^ Internal: match check bailed out | ||
1430 | } | ||
1431 | } | ||
1432 | "#, | ||
1433 | ); | ||
1434 | } | ||
1435 | |||
1436 | #[test] | ||
1437 | fn reference_patterns_in_fields() { | ||
1438 | check_diagnostics( | ||
1439 | r#" | ||
1440 | fn main() { | ||
1441 | match (&false,) { | ||
1442 | (true,) => {} | ||
1443 | // ^^^^^^^ Internal: match check bailed out | ||
1444 | } | ||
1445 | match (&false,) { | ||
1446 | (&true,) => {} | ||
1447 | // ^^^^^^^^ Internal: match check bailed out | ||
1448 | } | ||
1449 | } | ||
1450 | "#, | ||
1451 | ); | ||
1452 | } | ||
1453 | } | ||
1454 | } | ||
diff --git a/crates/ide/src/diagnostics/missing_match_arms.rs b/crates/ide/src/diagnostics/missing_match_arms.rs new file mode 100644 index 000000000..6b977fa59 --- /dev/null +++ b/crates/ide/src/diagnostics/missing_match_arms.rs | |||
@@ -0,0 +1,924 @@ | |||
1 | use hir::InFile; | ||
2 | |||
3 | use crate::diagnostics::{Diagnostic, DiagnosticsContext}; | ||
4 | |||
5 | // Diagnostic: missing-match-arm | ||
6 | // | ||
7 | // This diagnostic is triggered if `match` block is missing one or more match arms. | ||
8 | pub(super) fn missing_match_arms( | ||
9 | ctx: &DiagnosticsContext<'_>, | ||
10 | d: &hir::MissingMatchArms, | ||
11 | ) -> Diagnostic { | ||
12 | Diagnostic::new( | ||
13 | "missing-match-arm", | ||
14 | "missing match arm", | ||
15 | ctx.sema.diagnostics_display_range(InFile::new(d.file, d.match_expr.clone().into())).range, | ||
16 | ) | ||
17 | } | ||
18 | |||
19 | #[cfg(test)] | ||
20 | pub(super) mod tests { | ||
21 | use crate::diagnostics::tests::check_diagnostics; | ||
22 | |||
23 | #[test] | ||
24 | fn empty_tuple() { | ||
25 | check_diagnostics( | ||
26 | r#" | ||
27 | fn main() { | ||
28 | match () { } | ||
29 | //^^ missing match arm | ||
30 | match (()) { } | ||
31 | //^^^^ missing match arm | ||
32 | |||
33 | match () { _ => (), } | ||
34 | match () { () => (), } | ||
35 | match (()) { (()) => (), } | ||
36 | } | ||
37 | "#, | ||
38 | ); | ||
39 | } | ||
40 | |||
41 | #[test] | ||
42 | fn tuple_of_two_empty_tuple() { | ||
43 | check_diagnostics( | ||
44 | r#" | ||
45 | fn main() { | ||
46 | match ((), ()) { } | ||
47 | //^^^^^^^^ missing match arm | ||
48 | |||
49 | match ((), ()) { ((), ()) => (), } | ||
50 | } | ||
51 | "#, | ||
52 | ); | ||
53 | } | ||
54 | |||
55 | #[test] | ||
56 | fn boolean() { | ||
57 | check_diagnostics( | ||
58 | r#" | ||
59 | fn test_main() { | ||
60 | match false { } | ||
61 | //^^^^^ missing match arm | ||
62 | match false { true => (), } | ||
63 | //^^^^^ missing match arm | ||
64 | match (false, true) {} | ||
65 | //^^^^^^^^^^^^^ missing match arm | ||
66 | match (false, true) { (true, true) => (), } | ||
67 | //^^^^^^^^^^^^^ missing match arm | ||
68 | match (false, true) { | ||
69 | //^^^^^^^^^^^^^ missing match arm | ||
70 | (false, true) => (), | ||
71 | (false, false) => (), | ||
72 | (true, false) => (), | ||
73 | } | ||
74 | match (false, true) { (true, _x) => (), } | ||
75 | //^^^^^^^^^^^^^ missing match arm | ||
76 | |||
77 | match false { true => (), false => (), } | ||
78 | match (false, true) { | ||
79 | (false, _) => (), | ||
80 | (true, false) => (), | ||
81 | (_, true) => (), | ||
82 | } | ||
83 | match (false, true) { | ||
84 | (true, true) => (), | ||
85 | (true, false) => (), | ||
86 | (false, true) => (), | ||
87 | (false, false) => (), | ||
88 | } | ||
89 | match (false, true) { | ||
90 | (true, _x) => (), | ||
91 | (false, true) => (), | ||
92 | (false, false) => (), | ||
93 | } | ||
94 | match (false, true, false) { | ||
95 | (false, ..) => (), | ||
96 | (true, ..) => (), | ||
97 | } | ||
98 | match (false, true, false) { | ||
99 | (.., false) => (), | ||
100 | (.., true) => (), | ||
101 | } | ||
102 | match (false, true, false) { (..) => (), } | ||
103 | } | ||
104 | "#, | ||
105 | ); | ||
106 | } | ||
107 | |||
108 | #[test] | ||
109 | fn tuple_of_tuple_and_bools() { | ||
110 | check_diagnostics( | ||
111 | r#" | ||
112 | fn main() { | ||
113 | match (false, ((), false)) {} | ||
114 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
115 | match (false, ((), false)) { (true, ((), true)) => (), } | ||
116 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
117 | match (false, ((), false)) { (true, _) => (), } | ||
118 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
119 | |||
120 | match (false, ((), false)) { | ||
121 | (true, ((), true)) => (), | ||
122 | (true, ((), false)) => (), | ||
123 | (false, ((), true)) => (), | ||
124 | (false, ((), false)) => (), | ||
125 | } | ||
126 | match (false, ((), false)) { | ||
127 | (true, ((), true)) => (), | ||
128 | (true, ((), false)) => (), | ||
129 | (false, _) => (), | ||
130 | } | ||
131 | } | ||
132 | "#, | ||
133 | ); | ||
134 | } | ||
135 | |||
136 | #[test] | ||
137 | fn enums() { | ||
138 | check_diagnostics( | ||
139 | r#" | ||
140 | enum Either { A, B, } | ||
141 | |||
142 | fn main() { | ||
143 | match Either::A { } | ||
144 | //^^^^^^^^^ missing match arm | ||
145 | match Either::B { Either::A => (), } | ||
146 | //^^^^^^^^^ missing match arm | ||
147 | |||
148 | match &Either::B { | ||
149 | //^^^^^^^^^^ missing match arm | ||
150 | Either::A => (), | ||
151 | } | ||
152 | |||
153 | match Either::B { | ||
154 | Either::A => (), Either::B => (), | ||
155 | } | ||
156 | match &Either::B { | ||
157 | Either::A => (), Either::B => (), | ||
158 | } | ||
159 | } | ||
160 | "#, | ||
161 | ); | ||
162 | } | ||
163 | |||
164 | #[test] | ||
165 | fn enum_containing_bool() { | ||
166 | check_diagnostics( | ||
167 | r#" | ||
168 | enum Either { A(bool), B } | ||
169 | |||
170 | fn main() { | ||
171 | match Either::B { } | ||
172 | //^^^^^^^^^ missing match arm | ||
173 | match Either::B { | ||
174 | //^^^^^^^^^ missing match arm | ||
175 | Either::A(true) => (), Either::B => () | ||
176 | } | ||
177 | |||
178 | match Either::B { | ||
179 | Either::A(true) => (), | ||
180 | Either::A(false) => (), | ||
181 | Either::B => (), | ||
182 | } | ||
183 | match Either::B { | ||
184 | Either::B => (), | ||
185 | _ => (), | ||
186 | } | ||
187 | match Either::B { | ||
188 | Either::A(_) => (), | ||
189 | Either::B => (), | ||
190 | } | ||
191 | |||
192 | } | ||
193 | "#, | ||
194 | ); | ||
195 | } | ||
196 | |||
197 | #[test] | ||
198 | fn enum_different_sizes() { | ||
199 | check_diagnostics( | ||
200 | r#" | ||
201 | enum Either { A(bool), B(bool, bool) } | ||
202 | |||
203 | fn main() { | ||
204 | match Either::A(false) { | ||
205 | //^^^^^^^^^^^^^^^^ missing match arm | ||
206 | Either::A(_) => (), | ||
207 | Either::B(false, _) => (), | ||
208 | } | ||
209 | |||
210 | match Either::A(false) { | ||
211 | Either::A(_) => (), | ||
212 | Either::B(true, _) => (), | ||
213 | Either::B(false, _) => (), | ||
214 | } | ||
215 | match Either::A(false) { | ||
216 | Either::A(true) | Either::A(false) => (), | ||
217 | Either::B(true, _) => (), | ||
218 | Either::B(false, _) => (), | ||
219 | } | ||
220 | } | ||
221 | "#, | ||
222 | ); | ||
223 | } | ||
224 | |||
225 | #[test] | ||
226 | fn tuple_of_enum_no_diagnostic() { | ||
227 | check_diagnostics( | ||
228 | r#" | ||
229 | enum Either { A(bool), B(bool, bool) } | ||
230 | enum Either2 { C, D } | ||
231 | |||
232 | fn main() { | ||
233 | match (Either::A(false), Either2::C) { | ||
234 | (Either::A(true), _) | (Either::A(false), _) => (), | ||
235 | (Either::B(true, _), Either2::C) => (), | ||
236 | (Either::B(false, _), Either2::C) => (), | ||
237 | (Either::B(_, _), Either2::D) => (), | ||
238 | } | ||
239 | } | ||
240 | "#, | ||
241 | ); | ||
242 | } | ||
243 | |||
244 | #[test] | ||
245 | fn or_pattern_no_diagnostic() { | ||
246 | check_diagnostics( | ||
247 | r#" | ||
248 | enum Either {A, B} | ||
249 | |||
250 | fn main() { | ||
251 | match (Either::A, Either::B) { | ||
252 | (Either::A | Either::B, _) => (), | ||
253 | } | ||
254 | }"#, | ||
255 | ) | ||
256 | } | ||
257 | |||
258 | #[test] | ||
259 | fn mismatched_types() { | ||
260 | // Match statements with arms that don't match the | ||
261 | // expression pattern do not fire this diagnostic. | ||
262 | check_diagnostics( | ||
263 | r#" | ||
264 | enum Either { A, B } | ||
265 | enum Either2 { C, D } | ||
266 | |||
267 | fn main() { | ||
268 | match Either::A { | ||
269 | Either2::C => (), | ||
270 | // ^^^^^^^^^^ Internal: match check bailed out | ||
271 | Either2::D => (), | ||
272 | } | ||
273 | match (true, false) { | ||
274 | (true, false, true) => (), | ||
275 | // ^^^^^^^^^^^^^^^^^^^ Internal: match check bailed out | ||
276 | (true) => (), | ||
277 | } | ||
278 | match (true, false) { (true,) => {} } | ||
279 | // ^^^^^^^ Internal: match check bailed out | ||
280 | match (0) { () => () } | ||
281 | // ^^ Internal: match check bailed out | ||
282 | match Unresolved::Bar { Unresolved::Baz => () } | ||
283 | } | ||
284 | "#, | ||
285 | ); | ||
286 | } | ||
287 | |||
288 | #[test] | ||
289 | fn mismatched_types_in_or_patterns() { | ||
290 | check_diagnostics( | ||
291 | r#" | ||
292 | fn main() { | ||
293 | match false { true | () => {} } | ||
294 | // ^^^^^^^^^ Internal: match check bailed out | ||
295 | match (false,) { (true | (),) => {} } | ||
296 | // ^^^^^^^^^^^^ Internal: match check bailed out | ||
297 | } | ||
298 | "#, | ||
299 | ); | ||
300 | } | ||
301 | |||
302 | #[test] | ||
303 | fn malformed_match_arm_tuple_enum_missing_pattern() { | ||
304 | // We are testing to be sure we don't panic here when the match | ||
305 | // arm `Either::B` is missing its pattern. | ||
306 | check_diagnostics( | ||
307 | r#" | ||
308 | enum Either { A, B(u32) } | ||
309 | |||
310 | fn main() { | ||
311 | match Either::A { | ||
312 | Either::A => (), | ||
313 | Either::B() => (), | ||
314 | } | ||
315 | } | ||
316 | "#, | ||
317 | ); | ||
318 | } | ||
319 | |||
320 | #[test] | ||
321 | fn malformed_match_arm_extra_fields() { | ||
322 | check_diagnostics( | ||
323 | r#" | ||
324 | enum A { B(isize, isize), C } | ||
325 | fn main() { | ||
326 | match A::B(1, 2) { | ||
327 | A::B(_, _, _) => (), | ||
328 | // ^^^^^^^^^^^^^ Internal: match check bailed out | ||
329 | } | ||
330 | match A::B(1, 2) { | ||
331 | A::C(_) => (), | ||
332 | // ^^^^^^^ Internal: match check bailed out | ||
333 | } | ||
334 | } | ||
335 | "#, | ||
336 | ); | ||
337 | } | ||
338 | |||
339 | #[test] | ||
340 | fn expr_diverges() { | ||
341 | check_diagnostics( | ||
342 | r#" | ||
343 | enum Either { A, B } | ||
344 | |||
345 | fn main() { | ||
346 | match loop {} { | ||
347 | Either::A => (), | ||
348 | // ^^^^^^^^^ Internal: match check bailed out | ||
349 | Either::B => (), | ||
350 | } | ||
351 | match loop {} { | ||
352 | Either::A => (), | ||
353 | // ^^^^^^^^^ Internal: match check bailed out | ||
354 | } | ||
355 | match loop { break Foo::A } { | ||
356 | //^^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
357 | Either::A => (), | ||
358 | } | ||
359 | match loop { break Foo::A } { | ||
360 | Either::A => (), | ||
361 | Either::B => (), | ||
362 | } | ||
363 | } | ||
364 | "#, | ||
365 | ); | ||
366 | } | ||
367 | |||
368 | #[test] | ||
369 | fn expr_partially_diverges() { | ||
370 | check_diagnostics( | ||
371 | r#" | ||
372 | enum Either<T> { A(T), B } | ||
373 | |||
374 | fn foo() -> Either<!> { Either::B } | ||
375 | fn main() -> u32 { | ||
376 | match foo() { | ||
377 | Either::A(val) => val, | ||
378 | Either::B => 0, | ||
379 | } | ||
380 | } | ||
381 | "#, | ||
382 | ); | ||
383 | } | ||
384 | |||
385 | #[test] | ||
386 | fn enum_record() { | ||
387 | check_diagnostics( | ||
388 | r#" | ||
389 | enum Either { A { foo: bool }, B } | ||
390 | |||
391 | fn main() { | ||
392 | let a = Either::A { foo: true }; | ||
393 | match a { } | ||
394 | //^ missing match arm | ||
395 | match a { Either::A { foo: true } => () } | ||
396 | //^ missing match arm | ||
397 | match a { | ||
398 | Either::A { } => (), | ||
399 | //^^^^^^^^^ Missing structure fields: | ||
400 | // | - foo | ||
401 | Either::B => (), | ||
402 | } | ||
403 | match a { | ||
404 | //^ missing match arm | ||
405 | Either::A { } => (), | ||
406 | } //^^^^^^^^^ Missing structure fields: | ||
407 | // | - foo | ||
408 | |||
409 | match a { | ||
410 | Either::A { foo: true } => (), | ||
411 | Either::A { foo: false } => (), | ||
412 | Either::B => (), | ||
413 | } | ||
414 | match a { | ||
415 | Either::A { foo: _ } => (), | ||
416 | Either::B => (), | ||
417 | } | ||
418 | } | ||
419 | "#, | ||
420 | ); | ||
421 | } | ||
422 | |||
423 | #[test] | ||
424 | fn enum_record_fields_out_of_order() { | ||
425 | check_diagnostics( | ||
426 | r#" | ||
427 | enum Either { | ||
428 | A { foo: bool, bar: () }, | ||
429 | B, | ||
430 | } | ||
431 | |||
432 | fn main() { | ||
433 | let a = Either::A { foo: true, bar: () }; | ||
434 | match a { | ||
435 | //^ missing match arm | ||
436 | Either::A { bar: (), foo: false } => (), | ||
437 | Either::A { foo: true, bar: () } => (), | ||
438 | } | ||
439 | |||
440 | match a { | ||
441 | Either::A { bar: (), foo: false } => (), | ||
442 | Either::A { foo: true, bar: () } => (), | ||
443 | Either::B => (), | ||
444 | } | ||
445 | } | ||
446 | "#, | ||
447 | ); | ||
448 | } | ||
449 | |||
450 | #[test] | ||
451 | fn enum_record_ellipsis() { | ||
452 | check_diagnostics( | ||
453 | r#" | ||
454 | enum Either { | ||
455 | A { foo: bool, bar: bool }, | ||
456 | B, | ||
457 | } | ||
458 | |||
459 | fn main() { | ||
460 | let a = Either::B; | ||
461 | match a { | ||
462 | //^ missing match arm | ||
463 | Either::A { foo: true, .. } => (), | ||
464 | Either::B => (), | ||
465 | } | ||
466 | match a { | ||
467 | //^ missing match arm | ||
468 | Either::A { .. } => (), | ||
469 | } | ||
470 | |||
471 | match a { | ||
472 | Either::A { foo: true, .. } => (), | ||
473 | Either::A { foo: false, .. } => (), | ||
474 | Either::B => (), | ||
475 | } | ||
476 | |||
477 | match a { | ||
478 | Either::A { .. } => (), | ||
479 | Either::B => (), | ||
480 | } | ||
481 | } | ||
482 | "#, | ||
483 | ); | ||
484 | } | ||
485 | |||
486 | #[test] | ||
487 | fn enum_tuple_partial_ellipsis() { | ||
488 | check_diagnostics( | ||
489 | r#" | ||
490 | enum Either { | ||
491 | A(bool, bool, bool, bool), | ||
492 | B, | ||
493 | } | ||
494 | |||
495 | fn main() { | ||
496 | match Either::B { | ||
497 | //^^^^^^^^^ missing match arm | ||
498 | Either::A(true, .., true) => (), | ||
499 | Either::A(true, .., false) => (), | ||
500 | Either::A(false, .., false) => (), | ||
501 | Either::B => (), | ||
502 | } | ||
503 | match Either::B { | ||
504 | //^^^^^^^^^ missing match arm | ||
505 | Either::A(true, .., true) => (), | ||
506 | Either::A(true, .., false) => (), | ||
507 | Either::A(.., true) => (), | ||
508 | Either::B => (), | ||
509 | } | ||
510 | |||
511 | match Either::B { | ||
512 | Either::A(true, .., true) => (), | ||
513 | Either::A(true, .., false) => (), | ||
514 | Either::A(false, .., true) => (), | ||
515 | Either::A(false, .., false) => (), | ||
516 | Either::B => (), | ||
517 | } | ||
518 | match Either::B { | ||
519 | Either::A(true, .., true) => (), | ||
520 | Either::A(true, .., false) => (), | ||
521 | Either::A(.., true) => (), | ||
522 | Either::A(.., false) => (), | ||
523 | Either::B => (), | ||
524 | } | ||
525 | } | ||
526 | "#, | ||
527 | ); | ||
528 | } | ||
529 | |||
530 | #[test] | ||
531 | fn never() { | ||
532 | check_diagnostics( | ||
533 | r#" | ||
534 | enum Never {} | ||
535 | |||
536 | fn enum_(never: Never) { | ||
537 | match never {} | ||
538 | } | ||
539 | fn enum_ref(never: &Never) { | ||
540 | match never {} | ||
541 | //^^^^^ missing match arm | ||
542 | } | ||
543 | fn bang(never: !) { | ||
544 | match never {} | ||
545 | } | ||
546 | "#, | ||
547 | ); | ||
548 | } | ||
549 | |||
550 | #[test] | ||
551 | fn unknown_type() { | ||
552 | check_diagnostics( | ||
553 | r#" | ||
554 | enum Option<T> { Some(T), None } | ||
555 | |||
556 | fn main() { | ||
557 | // `Never` is deliberately not defined so that it's an uninferred type. | ||
558 | match Option::<Never>::None { | ||
559 | None => (), | ||
560 | Some(never) => match never {}, | ||
561 | // ^^^^^^^^^^^ Internal: match check bailed out | ||
562 | } | ||
563 | match Option::<Never>::None { | ||
564 | //^^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
565 | Option::Some(_never) => {}, | ||
566 | } | ||
567 | } | ||
568 | "#, | ||
569 | ); | ||
570 | } | ||
571 | |||
572 | #[test] | ||
573 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | ||
574 | check_diagnostics( | ||
575 | r#" | ||
576 | fn main() { | ||
577 | match (false, true, false) { | ||
578 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
579 | (false, ..) => (), | ||
580 | } | ||
581 | }"#, | ||
582 | ); | ||
583 | } | ||
584 | |||
585 | #[test] | ||
586 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | ||
587 | check_diagnostics( | ||
588 | r#" | ||
589 | fn main() { | ||
590 | match (false, true, false) { | ||
591 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
592 | (.., false) => (), | ||
593 | } | ||
594 | }"#, | ||
595 | ); | ||
596 | } | ||
597 | |||
598 | #[test] | ||
599 | fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() { | ||
600 | check_diagnostics( | ||
601 | r#" | ||
602 | fn main() { | ||
603 | match (false, true, false) { | ||
604 | //^^^^^^^^^^^^^^^^^^^^ missing match arm | ||
605 | (true, .., false) => (), | ||
606 | } | ||
607 | }"#, | ||
608 | ); | ||
609 | } | ||
610 | |||
611 | #[test] | ||
612 | fn record_struct() { | ||
613 | check_diagnostics( | ||
614 | r#"struct Foo { a: bool } | ||
615 | fn main(f: Foo) { | ||
616 | match f {} | ||
617 | //^ missing match arm | ||
618 | match f { Foo { a: true } => () } | ||
619 | //^ missing match arm | ||
620 | match &f { Foo { a: true } => () } | ||
621 | //^^ missing match arm | ||
622 | match f { Foo { a: _ } => () } | ||
623 | match f { | ||
624 | Foo { a: true } => (), | ||
625 | Foo { a: false } => (), | ||
626 | } | ||
627 | match &f { | ||
628 | Foo { a: true } => (), | ||
629 | Foo { a: false } => (), | ||
630 | } | ||
631 | } | ||
632 | "#, | ||
633 | ); | ||
634 | } | ||
635 | |||
636 | #[test] | ||
637 | fn tuple_struct() { | ||
638 | check_diagnostics( | ||
639 | r#"struct Foo(bool); | ||
640 | fn main(f: Foo) { | ||
641 | match f {} | ||
642 | //^ missing match arm | ||
643 | match f { Foo(true) => () } | ||
644 | //^ missing match arm | ||
645 | match f { | ||
646 | Foo(true) => (), | ||
647 | Foo(false) => (), | ||
648 | } | ||
649 | } | ||
650 | "#, | ||
651 | ); | ||
652 | } | ||
653 | |||
654 | #[test] | ||
655 | fn unit_struct() { | ||
656 | check_diagnostics( | ||
657 | r#"struct Foo; | ||
658 | fn main(f: Foo) { | ||
659 | match f {} | ||
660 | //^ missing match arm | ||
661 | match f { Foo => () } | ||
662 | } | ||
663 | "#, | ||
664 | ); | ||
665 | } | ||
666 | |||
667 | #[test] | ||
668 | fn record_struct_ellipsis() { | ||
669 | check_diagnostics( | ||
670 | r#"struct Foo { foo: bool, bar: bool } | ||
671 | fn main(f: Foo) { | ||
672 | match f { Foo { foo: true, .. } => () } | ||
673 | //^ missing match arm | ||
674 | match f { | ||
675 | //^ missing match arm | ||
676 | Foo { foo: true, .. } => (), | ||
677 | Foo { bar: false, .. } => () | ||
678 | } | ||
679 | match f { Foo { .. } => () } | ||
680 | match f { | ||
681 | Foo { foo: true, .. } => (), | ||
682 | Foo { foo: false, .. } => () | ||
683 | } | ||
684 | } | ||
685 | "#, | ||
686 | ); | ||
687 | } | ||
688 | |||
689 | #[test] | ||
690 | fn internal_or() { | ||
691 | check_diagnostics( | ||
692 | r#" | ||
693 | fn main() { | ||
694 | enum Either { A(bool), B } | ||
695 | match Either::B { | ||
696 | //^^^^^^^^^ missing match arm | ||
697 | Either::A(true | false) => (), | ||
698 | } | ||
699 | } | ||
700 | "#, | ||
701 | ); | ||
702 | } | ||
703 | |||
704 | #[test] | ||
705 | fn no_panic_at_unimplemented_subpattern_type() { | ||
706 | check_diagnostics( | ||
707 | r#" | ||
708 | struct S { a: char} | ||
709 | fn main(v: S) { | ||
710 | match v { S{ a } => {} } | ||
711 | match v { S{ a: _x } => {} } | ||
712 | match v { S{ a: 'a' } => {} } | ||
713 | //^^^^^^^^^^^ Internal: match check bailed out | ||
714 | match v { S{..} => {} } | ||
715 | match v { _ => {} } | ||
716 | match v { } | ||
717 | //^ missing match arm | ||
718 | } | ||
719 | "#, | ||
720 | ); | ||
721 | } | ||
722 | |||
723 | #[test] | ||
724 | fn binding() { | ||
725 | check_diagnostics( | ||
726 | r#" | ||
727 | fn main() { | ||
728 | match true { | ||
729 | _x @ true => {} | ||
730 | false => {} | ||
731 | } | ||
732 | match true { _x @ true => {} } | ||
733 | //^^^^ missing match arm | ||
734 | } | ||
735 | "#, | ||
736 | ); | ||
737 | } | ||
738 | |||
739 | #[test] | ||
740 | fn binding_ref_has_correct_type() { | ||
741 | // Asserts `PatKind::Binding(ref _x): bool`, not &bool. | ||
742 | // If that's not true match checking will panic with "incompatible constructors" | ||
743 | // FIXME: make facilities to test this directly like `tests::check_infer(..)` | ||
744 | check_diagnostics( | ||
745 | r#" | ||
746 | enum Foo { A } | ||
747 | fn main() { | ||
748 | // FIXME: this should not bail out but current behavior is such as the old algorithm. | ||
749 | // ExprValidator::validate_match(..) checks types of top level patterns incorrecly. | ||
750 | match Foo::A { | ||
751 | ref _x => {} | ||
752 | // ^^^^^^ Internal: match check bailed out | ||
753 | Foo::A => {} | ||
754 | } | ||
755 | match (true,) { | ||
756 | (ref _x,) => {} | ||
757 | (true,) => {} | ||
758 | } | ||
759 | } | ||
760 | "#, | ||
761 | ); | ||
762 | } | ||
763 | |||
764 | #[test] | ||
765 | fn enum_non_exhaustive() { | ||
766 | check_diagnostics( | ||
767 | r#" | ||
768 | //- /lib.rs crate:lib | ||
769 | #[non_exhaustive] | ||
770 | pub enum E { A, B } | ||
771 | fn _local() { | ||
772 | match E::A { _ => {} } | ||
773 | match E::A { | ||
774 | E::A => {} | ||
775 | E::B => {} | ||
776 | } | ||
777 | match E::A { | ||
778 | E::A | E::B => {} | ||
779 | } | ||
780 | } | ||
781 | |||
782 | //- /main.rs crate:main deps:lib | ||
783 | use lib::E; | ||
784 | fn main() { | ||
785 | match E::A { _ => {} } | ||
786 | match E::A { | ||
787 | //^^^^ missing match arm | ||
788 | E::A => {} | ||
789 | E::B => {} | ||
790 | } | ||
791 | match E::A { | ||
792 | //^^^^ missing match arm | ||
793 | E::A | E::B => {} | ||
794 | } | ||
795 | } | ||
796 | "#, | ||
797 | ); | ||
798 | } | ||
799 | |||
800 | #[test] | ||
801 | fn match_guard() { | ||
802 | check_diagnostics( | ||
803 | r#" | ||
804 | fn main() { | ||
805 | match true { | ||
806 | true if false => {} | ||
807 | true => {} | ||
808 | false => {} | ||
809 | } | ||
810 | match true { | ||
811 | //^^^^ missing match arm | ||
812 | true if false => {} | ||
813 | false => {} | ||
814 | } | ||
815 | } | ||
816 | "#, | ||
817 | ); | ||
818 | } | ||
819 | |||
820 | #[test] | ||
821 | fn pattern_type_is_of_substitution() { | ||
822 | cov_mark::check!(match_check_wildcard_expanded_to_substitutions); | ||
823 | check_diagnostics( | ||
824 | r#" | ||
825 | struct Foo<T>(T); | ||
826 | struct Bar; | ||
827 | fn main() { | ||
828 | match Foo(Bar) { | ||
829 | _ | Foo(Bar) => {} | ||
830 | } | ||
831 | } | ||
832 | "#, | ||
833 | ); | ||
834 | } | ||
835 | |||
836 | #[test] | ||
837 | fn record_struct_no_such_field() { | ||
838 | check_diagnostics( | ||
839 | r#" | ||
840 | struct Foo { } | ||
841 | fn main(f: Foo) { | ||
842 | match f { Foo { bar } => () } | ||
843 | // ^^^^^^^^^^^ Internal: match check bailed out | ||
844 | } | ||
845 | "#, | ||
846 | ); | ||
847 | } | ||
848 | |||
849 | #[test] | ||
850 | fn match_ergonomics_issue_9095() { | ||
851 | check_diagnostics( | ||
852 | r#" | ||
853 | enum Foo<T> { A(T) } | ||
854 | fn main() { | ||
855 | match &Foo::A(true) { | ||
856 | _ => {} | ||
857 | Foo::A(_) => {} | ||
858 | } | ||
859 | } | ||
860 | "#, | ||
861 | ); | ||
862 | } | ||
863 | |||
864 | mod false_negatives { | ||
865 | //! The implementation of match checking here is a work in progress. As we roll this out, we | ||
866 | //! prefer false negatives to false positives (ideally there would be no false positives). This | ||
867 | //! test module should document known false negatives. Eventually we will have a complete | ||
868 | //! implementation of match checking and this module will be empty. | ||
869 | //! | ||
870 | //! The reasons for documenting known false negatives: | ||
871 | //! | ||
872 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. | ||
873 | //! 2. It ensures the code doesn't panic when handling these cases. | ||
874 | use super::*; | ||
875 | |||
876 | #[test] | ||
877 | fn integers() { | ||
878 | // We don't currently check integer exhaustiveness. | ||
879 | check_diagnostics( | ||
880 | r#" | ||
881 | fn main() { | ||
882 | match 5 { | ||
883 | 10 => (), | ||
884 | // ^^ Internal: match check bailed out | ||
885 | 11..20 => (), | ||
886 | } | ||
887 | } | ||
888 | "#, | ||
889 | ); | ||
890 | } | ||
891 | |||
892 | #[test] | ||
893 | fn reference_patterns_at_top_level() { | ||
894 | check_diagnostics( | ||
895 | r#" | ||
896 | fn main() { | ||
897 | match &false { | ||
898 | &true => {} | ||
899 | // ^^^^^ Internal: match check bailed out | ||
900 | } | ||
901 | } | ||
902 | "#, | ||
903 | ); | ||
904 | } | ||
905 | |||
906 | #[test] | ||
907 | fn reference_patterns_in_fields() { | ||
908 | check_diagnostics( | ||
909 | r#" | ||
910 | fn main() { | ||
911 | match (&false,) { | ||
912 | (true,) => {} | ||
913 | // ^^^^^^^ Internal: match check bailed out | ||
914 | } | ||
915 | match (&false,) { | ||
916 | (&true,) => {} | ||
917 | // ^^^^^^^^ Internal: match check bailed out | ||
918 | } | ||
919 | } | ||
920 | "#, | ||
921 | ); | ||
922 | } | ||
923 | } | ||
924 | } | ||