diff options
Diffstat (limited to 'crates/hir_ty/src/diagnostics/match_check.rs')
-rw-r--r-- | crates/hir_ty/src/diagnostics/match_check.rs | 955 |
1 files changed, 11 insertions, 944 deletions
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index a9a99f57a..a30e42699 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs | |||
@@ -100,10 +100,19 @@ impl<'a> PatCtxt<'a> { | |||
100 | } | 100 | } |
101 | 101 | ||
102 | pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat { | 102 | pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat { |
103 | // FIXME: implement pattern adjustments (implicit pattern dereference; "RFC 2005-match-ergonomics") | 103 | // XXX(iDawer): Collecting pattern adjustments feels imprecise to me. |
104 | // When lowering of & and box patterns are implemented this should be tested | ||
105 | // in a manner of `match_ergonomics_issue_9095` test. | ||
106 | // Pattern adjustment is part of RFC 2005-match-ergonomics. | ||
104 | // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089 | 107 | // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089 |
105 | let unadjusted_pat = self.lower_pattern_unadjusted(pat); | 108 | let unadjusted_pat = self.lower_pattern_unadjusted(pat); |
106 | unadjusted_pat | 109 | self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold( |
110 | unadjusted_pat, | ||
111 | |subpattern, ref_ty| Pat { | ||
112 | ty: ref_ty.clone(), | ||
113 | kind: Box::new(PatKind::Deref { subpattern }), | ||
114 | }, | ||
115 | ) | ||
107 | } | 116 | } |
108 | 117 | ||
109 | fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat { | 118 | fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat { |
@@ -355,945 +364,3 @@ impl PatternFoldable for PatKind { | |||
355 | } | 364 | } |
356 | } | 365 | } |
357 | } | 366 | } |
358 | |||
359 | #[cfg(test)] | ||
360 | pub(super) mod tests { | ||
361 | mod report { | ||
362 | use std::any::Any; | ||
363 | |||
364 | use hir_def::{expr::PatId, DefWithBodyId}; | ||
365 | use hir_expand::{HirFileId, InFile}; | ||
366 | use syntax::SyntaxNodePtr; | ||
367 | |||
368 | use crate::{ | ||
369 | db::HirDatabase, | ||
370 | diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink}, | ||
371 | }; | ||
372 | |||
373 | /// In tests, match check bails out loudly. | ||
374 | /// This helps to catch incorrect tests that pass due to false negatives. | ||
375 | pub(crate) fn report_bail_out( | ||
376 | db: &dyn HirDatabase, | ||
377 | def: DefWithBodyId, | ||
378 | pat: PatId, | ||
379 | sink: &mut DiagnosticSink, | ||
380 | ) { | ||
381 | let (_, source_map) = db.body_with_source_map(def); | ||
382 | if let Ok(source_ptr) = source_map.pat_syntax(pat) { | ||
383 | let pat_syntax_ptr = source_ptr.value.either(Into::into, Into::into); | ||
384 | sink.push(BailedOut { file: source_ptr.file_id, pat_syntax_ptr }); | ||
385 | } | ||
386 | } | ||
387 | |||
388 | #[derive(Debug)] | ||
389 | struct BailedOut { | ||
390 | file: HirFileId, | ||
391 | pat_syntax_ptr: SyntaxNodePtr, | ||
392 | } | ||
393 | |||
394 | impl Diagnostic for BailedOut { | ||
395 | fn code(&self) -> DiagnosticCode { | ||
396 | DiagnosticCode("internal:match-check-bailed-out") | ||
397 | } | ||
398 | fn message(&self) -> String { | ||
399 | format!("Internal: match check bailed out") | ||
400 | } | ||
401 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
402 | InFile { file_id: self.file, value: self.pat_syntax_ptr.clone() } | ||
403 | } | ||
404 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
405 | self | ||
406 | } | ||
407 | } | ||
408 | } | ||
409 | |||
410 | use crate::diagnostics::tests::check_diagnostics; | ||
411 | |||
412 | pub(crate) use self::report::report_bail_out; | ||
413 | |||
414 | #[test] | ||
415 | fn empty_tuple() { | ||
416 | check_diagnostics( | ||
417 | r#" | ||
418 | fn main() { | ||
419 | match () { } | ||
420 | //^^ Missing match arm | ||
421 | match (()) { } | ||
422 | //^^^^ Missing match arm | ||
423 | |||
424 | match () { _ => (), } | ||
425 | match () { () => (), } | ||
426 | match (()) { (()) => (), } | ||
427 | } | ||
428 | "#, | ||
429 | ); | ||
430 | } | ||
431 | |||
432 | #[test] | ||
433 | fn tuple_of_two_empty_tuple() { | ||
434 | check_diagnostics( | ||
435 | r#" | ||
436 | fn main() { | ||
437 | match ((), ()) { } | ||
438 | //^^^^^^^^ Missing match arm | ||
439 | |||
440 | match ((), ()) { ((), ()) => (), } | ||
441 | } | ||
442 | "#, | ||
443 | ); | ||
444 | } | ||
445 | |||
446 | #[test] | ||
447 | fn boolean() { | ||
448 | check_diagnostics( | ||
449 | r#" | ||
450 | fn test_main() { | ||
451 | match false { } | ||
452 | //^^^^^ Missing match arm | ||
453 | match false { true => (), } | ||
454 | //^^^^^ Missing match arm | ||
455 | match (false, true) {} | ||
456 | //^^^^^^^^^^^^^ Missing match arm | ||
457 | match (false, true) { (true, true) => (), } | ||
458 | //^^^^^^^^^^^^^ Missing match arm | ||
459 | match (false, true) { | ||
460 | //^^^^^^^^^^^^^ Missing match arm | ||
461 | (false, true) => (), | ||
462 | (false, false) => (), | ||
463 | (true, false) => (), | ||
464 | } | ||
465 | match (false, true) { (true, _x) => (), } | ||
466 | //^^^^^^^^^^^^^ Missing match arm | ||
467 | |||
468 | match false { true => (), false => (), } | ||
469 | match (false, true) { | ||
470 | (false, _) => (), | ||
471 | (true, false) => (), | ||
472 | (_, true) => (), | ||
473 | } | ||
474 | match (false, true) { | ||
475 | (true, true) => (), | ||
476 | (true, false) => (), | ||
477 | (false, true) => (), | ||
478 | (false, false) => (), | ||
479 | } | ||
480 | match (false, true) { | ||
481 | (true, _x) => (), | ||
482 | (false, true) => (), | ||
483 | (false, false) => (), | ||
484 | } | ||
485 | match (false, true, false) { | ||
486 | (false, ..) => (), | ||
487 | (true, ..) => (), | ||
488 | } | ||
489 | match (false, true, false) { | ||
490 | (.., false) => (), | ||
491 | (.., true) => (), | ||
492 | } | ||
493 | match (false, true, false) { (..) => (), } | ||
494 | } | ||
495 | "#, | ||
496 | ); | ||
497 | } | ||
498 | |||
499 | #[test] | ||
500 | fn tuple_of_tuple_and_bools() { | ||
501 | check_diagnostics( | ||
502 | r#" | ||
503 | fn main() { | ||
504 | match (false, ((), false)) {} | ||
505 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
506 | match (false, ((), false)) { (true, ((), true)) => (), } | ||
507 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
508 | match (false, ((), false)) { (true, _) => (), } | ||
509 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
510 | |||
511 | match (false, ((), false)) { | ||
512 | (true, ((), true)) => (), | ||
513 | (true, ((), false)) => (), | ||
514 | (false, ((), true)) => (), | ||
515 | (false, ((), false)) => (), | ||
516 | } | ||
517 | match (false, ((), false)) { | ||
518 | (true, ((), true)) => (), | ||
519 | (true, ((), false)) => (), | ||
520 | (false, _) => (), | ||
521 | } | ||
522 | } | ||
523 | "#, | ||
524 | ); | ||
525 | } | ||
526 | |||
527 | #[test] | ||
528 | fn enums() { | ||
529 | check_diagnostics( | ||
530 | r#" | ||
531 | enum Either { A, B, } | ||
532 | |||
533 | fn main() { | ||
534 | match Either::A { } | ||
535 | //^^^^^^^^^ Missing match arm | ||
536 | match Either::B { Either::A => (), } | ||
537 | //^^^^^^^^^ Missing match arm | ||
538 | |||
539 | match &Either::B { | ||
540 | //^^^^^^^^^^ Missing match arm | ||
541 | Either::A => (), | ||
542 | } | ||
543 | |||
544 | match Either::B { | ||
545 | Either::A => (), Either::B => (), | ||
546 | } | ||
547 | match &Either::B { | ||
548 | Either::A => (), Either::B => (), | ||
549 | } | ||
550 | } | ||
551 | "#, | ||
552 | ); | ||
553 | } | ||
554 | |||
555 | #[test] | ||
556 | fn enum_containing_bool() { | ||
557 | check_diagnostics( | ||
558 | r#" | ||
559 | enum Either { A(bool), B } | ||
560 | |||
561 | fn main() { | ||
562 | match Either::B { } | ||
563 | //^^^^^^^^^ Missing match arm | ||
564 | match Either::B { | ||
565 | //^^^^^^^^^ Missing match arm | ||
566 | Either::A(true) => (), Either::B => () | ||
567 | } | ||
568 | |||
569 | match Either::B { | ||
570 | Either::A(true) => (), | ||
571 | Either::A(false) => (), | ||
572 | Either::B => (), | ||
573 | } | ||
574 | match Either::B { | ||
575 | Either::B => (), | ||
576 | _ => (), | ||
577 | } | ||
578 | match Either::B { | ||
579 | Either::A(_) => (), | ||
580 | Either::B => (), | ||
581 | } | ||
582 | |||
583 | } | ||
584 | "#, | ||
585 | ); | ||
586 | } | ||
587 | |||
588 | #[test] | ||
589 | fn enum_different_sizes() { | ||
590 | check_diagnostics( | ||
591 | r#" | ||
592 | enum Either { A(bool), B(bool, bool) } | ||
593 | |||
594 | fn main() { | ||
595 | match Either::A(false) { | ||
596 | //^^^^^^^^^^^^^^^^ Missing match arm | ||
597 | Either::A(_) => (), | ||
598 | Either::B(false, _) => (), | ||
599 | } | ||
600 | |||
601 | match Either::A(false) { | ||
602 | Either::A(_) => (), | ||
603 | Either::B(true, _) => (), | ||
604 | Either::B(false, _) => (), | ||
605 | } | ||
606 | match Either::A(false) { | ||
607 | Either::A(true) | Either::A(false) => (), | ||
608 | Either::B(true, _) => (), | ||
609 | Either::B(false, _) => (), | ||
610 | } | ||
611 | } | ||
612 | "#, | ||
613 | ); | ||
614 | } | ||
615 | |||
616 | #[test] | ||
617 | fn tuple_of_enum_no_diagnostic() { | ||
618 | check_diagnostics( | ||
619 | r#" | ||
620 | enum Either { A(bool), B(bool, bool) } | ||
621 | enum Either2 { C, D } | ||
622 | |||
623 | fn main() { | ||
624 | match (Either::A(false), Either2::C) { | ||
625 | (Either::A(true), _) | (Either::A(false), _) => (), | ||
626 | (Either::B(true, _), Either2::C) => (), | ||
627 | (Either::B(false, _), Either2::C) => (), | ||
628 | (Either::B(_, _), Either2::D) => (), | ||
629 | } | ||
630 | } | ||
631 | "#, | ||
632 | ); | ||
633 | } | ||
634 | |||
635 | #[test] | ||
636 | fn or_pattern_no_diagnostic() { | ||
637 | check_diagnostics( | ||
638 | r#" | ||
639 | enum Either {A, B} | ||
640 | |||
641 | fn main() { | ||
642 | match (Either::A, Either::B) { | ||
643 | (Either::A | Either::B, _) => (), | ||
644 | } | ||
645 | }"#, | ||
646 | ) | ||
647 | } | ||
648 | |||
649 | #[test] | ||
650 | fn mismatched_types() { | ||
651 | // Match statements with arms that don't match the | ||
652 | // expression pattern do not fire this diagnostic. | ||
653 | check_diagnostics( | ||
654 | r#" | ||
655 | enum Either { A, B } | ||
656 | enum Either2 { C, D } | ||
657 | |||
658 | fn main() { | ||
659 | match Either::A { | ||
660 | Either2::C => (), | ||
661 | // ^^^^^^^^^^ Internal: match check bailed out | ||
662 | Either2::D => (), | ||
663 | } | ||
664 | match (true, false) { | ||
665 | (true, false, true) => (), | ||
666 | // ^^^^^^^^^^^^^^^^^^^ Internal: match check bailed out | ||
667 | (true) => (), | ||
668 | } | ||
669 | match (true, false) { (true,) => {} } | ||
670 | // ^^^^^^^ Internal: match check bailed out | ||
671 | match (0) { () => () } | ||
672 | // ^^ Internal: match check bailed out | ||
673 | match Unresolved::Bar { Unresolved::Baz => () } | ||
674 | } | ||
675 | "#, | ||
676 | ); | ||
677 | } | ||
678 | |||
679 | #[test] | ||
680 | fn mismatched_types_in_or_patterns() { | ||
681 | check_diagnostics( | ||
682 | r#" | ||
683 | fn main() { | ||
684 | match false { true | () => {} } | ||
685 | // ^^^^^^^^^ Internal: match check bailed out | ||
686 | match (false,) { (true | (),) => {} } | ||
687 | // ^^^^^^^^^^^^ Internal: match check bailed out | ||
688 | } | ||
689 | "#, | ||
690 | ); | ||
691 | } | ||
692 | |||
693 | #[test] | ||
694 | fn malformed_match_arm_tuple_enum_missing_pattern() { | ||
695 | // We are testing to be sure we don't panic here when the match | ||
696 | // arm `Either::B` is missing its pattern. | ||
697 | check_diagnostics( | ||
698 | r#" | ||
699 | enum Either { A, B(u32) } | ||
700 | |||
701 | fn main() { | ||
702 | match Either::A { | ||
703 | Either::A => (), | ||
704 | Either::B() => (), | ||
705 | } | ||
706 | } | ||
707 | "#, | ||
708 | ); | ||
709 | } | ||
710 | |||
711 | #[test] | ||
712 | fn malformed_match_arm_extra_fields() { | ||
713 | check_diagnostics( | ||
714 | r#" | ||
715 | enum A { B(isize, isize), C } | ||
716 | fn main() { | ||
717 | match A::B(1, 2) { | ||
718 | A::B(_, _, _) => (), | ||
719 | // ^^^^^^^^^^^^^ Internal: match check bailed out | ||
720 | } | ||
721 | match A::B(1, 2) { | ||
722 | A::C(_) => (), | ||
723 | // ^^^^^^^ Internal: match check bailed out | ||
724 | } | ||
725 | } | ||
726 | "#, | ||
727 | ); | ||
728 | } | ||
729 | |||
730 | #[test] | ||
731 | fn expr_diverges() { | ||
732 | check_diagnostics( | ||
733 | r#" | ||
734 | enum Either { A, B } | ||
735 | |||
736 | fn main() { | ||
737 | match loop {} { | ||
738 | Either::A => (), | ||
739 | // ^^^^^^^^^ Internal: match check bailed out | ||
740 | Either::B => (), | ||
741 | } | ||
742 | match loop {} { | ||
743 | Either::A => (), | ||
744 | // ^^^^^^^^^ Internal: match check bailed out | ||
745 | } | ||
746 | match loop { break Foo::A } { | ||
747 | //^^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
748 | Either::A => (), | ||
749 | } | ||
750 | match loop { break Foo::A } { | ||
751 | Either::A => (), | ||
752 | Either::B => (), | ||
753 | } | ||
754 | } | ||
755 | "#, | ||
756 | ); | ||
757 | } | ||
758 | |||
759 | #[test] | ||
760 | fn expr_partially_diverges() { | ||
761 | check_diagnostics( | ||
762 | r#" | ||
763 | enum Either<T> { A(T), B } | ||
764 | |||
765 | fn foo() -> Either<!> { Either::B } | ||
766 | fn main() -> u32 { | ||
767 | match foo() { | ||
768 | Either::A(val) => val, | ||
769 | Either::B => 0, | ||
770 | } | ||
771 | } | ||
772 | "#, | ||
773 | ); | ||
774 | } | ||
775 | |||
776 | #[test] | ||
777 | fn enum_record() { | ||
778 | check_diagnostics( | ||
779 | r#" | ||
780 | enum Either { A { foo: bool }, B } | ||
781 | |||
782 | fn main() { | ||
783 | let a = Either::A { foo: true }; | ||
784 | match a { } | ||
785 | //^ Missing match arm | ||
786 | match a { Either::A { foo: true } => () } | ||
787 | //^ Missing match arm | ||
788 | match a { | ||
789 | Either::A { } => (), | ||
790 | //^^^^^^^^^ Missing structure fields: | ||
791 | // | - foo | ||
792 | Either::B => (), | ||
793 | } | ||
794 | match a { | ||
795 | //^ Missing match arm | ||
796 | Either::A { } => (), | ||
797 | } //^^^^^^^^^ Missing structure fields: | ||
798 | // | - foo | ||
799 | |||
800 | match a { | ||
801 | Either::A { foo: true } => (), | ||
802 | Either::A { foo: false } => (), | ||
803 | Either::B => (), | ||
804 | } | ||
805 | match a { | ||
806 | Either::A { foo: _ } => (), | ||
807 | Either::B => (), | ||
808 | } | ||
809 | } | ||
810 | "#, | ||
811 | ); | ||
812 | } | ||
813 | |||
814 | #[test] | ||
815 | fn enum_record_fields_out_of_order() { | ||
816 | check_diagnostics( | ||
817 | r#" | ||
818 | enum Either { | ||
819 | A { foo: bool, bar: () }, | ||
820 | B, | ||
821 | } | ||
822 | |||
823 | fn main() { | ||
824 | let a = Either::A { foo: true, bar: () }; | ||
825 | match a { | ||
826 | //^ Missing match arm | ||
827 | Either::A { bar: (), foo: false } => (), | ||
828 | Either::A { foo: true, bar: () } => (), | ||
829 | } | ||
830 | |||
831 | match a { | ||
832 | Either::A { bar: (), foo: false } => (), | ||
833 | Either::A { foo: true, bar: () } => (), | ||
834 | Either::B => (), | ||
835 | } | ||
836 | } | ||
837 | "#, | ||
838 | ); | ||
839 | } | ||
840 | |||
841 | #[test] | ||
842 | fn enum_record_ellipsis() { | ||
843 | check_diagnostics( | ||
844 | r#" | ||
845 | enum Either { | ||
846 | A { foo: bool, bar: bool }, | ||
847 | B, | ||
848 | } | ||
849 | |||
850 | fn main() { | ||
851 | let a = Either::B; | ||
852 | match a { | ||
853 | //^ Missing match arm | ||
854 | Either::A { foo: true, .. } => (), | ||
855 | Either::B => (), | ||
856 | } | ||
857 | match a { | ||
858 | //^ Missing match arm | ||
859 | Either::A { .. } => (), | ||
860 | } | ||
861 | |||
862 | match a { | ||
863 | Either::A { foo: true, .. } => (), | ||
864 | Either::A { foo: false, .. } => (), | ||
865 | Either::B => (), | ||
866 | } | ||
867 | |||
868 | match a { | ||
869 | Either::A { .. } => (), | ||
870 | Either::B => (), | ||
871 | } | ||
872 | } | ||
873 | "#, | ||
874 | ); | ||
875 | } | ||
876 | |||
877 | #[test] | ||
878 | fn enum_tuple_partial_ellipsis() { | ||
879 | check_diagnostics( | ||
880 | r#" | ||
881 | enum Either { | ||
882 | A(bool, bool, bool, bool), | ||
883 | B, | ||
884 | } | ||
885 | |||
886 | fn main() { | ||
887 | match Either::B { | ||
888 | //^^^^^^^^^ Missing match arm | ||
889 | Either::A(true, .., true) => (), | ||
890 | Either::A(true, .., false) => (), | ||
891 | Either::A(false, .., false) => (), | ||
892 | Either::B => (), | ||
893 | } | ||
894 | match Either::B { | ||
895 | //^^^^^^^^^ Missing match arm | ||
896 | Either::A(true, .., true) => (), | ||
897 | Either::A(true, .., false) => (), | ||
898 | Either::A(.., true) => (), | ||
899 | Either::B => (), | ||
900 | } | ||
901 | |||
902 | match Either::B { | ||
903 | Either::A(true, .., true) => (), | ||
904 | Either::A(true, .., false) => (), | ||
905 | Either::A(false, .., true) => (), | ||
906 | Either::A(false, .., false) => (), | ||
907 | Either::B => (), | ||
908 | } | ||
909 | match Either::B { | ||
910 | Either::A(true, .., true) => (), | ||
911 | Either::A(true, .., false) => (), | ||
912 | Either::A(.., true) => (), | ||
913 | Either::A(.., false) => (), | ||
914 | Either::B => (), | ||
915 | } | ||
916 | } | ||
917 | "#, | ||
918 | ); | ||
919 | } | ||
920 | |||
921 | #[test] | ||
922 | fn never() { | ||
923 | check_diagnostics( | ||
924 | r#" | ||
925 | enum Never {} | ||
926 | |||
927 | fn enum_(never: Never) { | ||
928 | match never {} | ||
929 | } | ||
930 | fn enum_ref(never: &Never) { | ||
931 | match never {} | ||
932 | //^^^^^ Missing match arm | ||
933 | } | ||
934 | fn bang(never: !) { | ||
935 | match never {} | ||
936 | } | ||
937 | "#, | ||
938 | ); | ||
939 | } | ||
940 | |||
941 | #[test] | ||
942 | fn unknown_type() { | ||
943 | check_diagnostics( | ||
944 | r#" | ||
945 | enum Option<T> { Some(T), None } | ||
946 | |||
947 | fn main() { | ||
948 | // `Never` is deliberately not defined so that it's an uninferred type. | ||
949 | match Option::<Never>::None { | ||
950 | None => (), | ||
951 | Some(never) => match never {}, | ||
952 | // ^^^^^^^^^^^ Internal: match check bailed out | ||
953 | } | ||
954 | match Option::<Never>::None { | ||
955 | //^^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
956 | Option::Some(_never) => {}, | ||
957 | } | ||
958 | } | ||
959 | "#, | ||
960 | ); | ||
961 | } | ||
962 | |||
963 | #[test] | ||
964 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | ||
965 | check_diagnostics( | ||
966 | r#" | ||
967 | fn main() { | ||
968 | match (false, true, false) { | ||
969 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
970 | (false, ..) => (), | ||
971 | } | ||
972 | }"#, | ||
973 | ); | ||
974 | } | ||
975 | |||
976 | #[test] | ||
977 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | ||
978 | check_diagnostics( | ||
979 | r#" | ||
980 | fn main() { | ||
981 | match (false, true, false) { | ||
982 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
983 | (.., false) => (), | ||
984 | } | ||
985 | }"#, | ||
986 | ); | ||
987 | } | ||
988 | |||
989 | #[test] | ||
990 | fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() { | ||
991 | check_diagnostics( | ||
992 | r#" | ||
993 | fn main() { | ||
994 | match (false, true, false) { | ||
995 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
996 | (true, .., false) => (), | ||
997 | } | ||
998 | }"#, | ||
999 | ); | ||
1000 | } | ||
1001 | |||
1002 | #[test] | ||
1003 | fn record_struct() { | ||
1004 | check_diagnostics( | ||
1005 | r#"struct Foo { a: bool } | ||
1006 | fn main(f: Foo) { | ||
1007 | match f {} | ||
1008 | //^ Missing match arm | ||
1009 | match f { Foo { a: true } => () } | ||
1010 | //^ Missing match arm | ||
1011 | match &f { Foo { a: true } => () } | ||
1012 | //^^ Missing match arm | ||
1013 | match f { Foo { a: _ } => () } | ||
1014 | match f { | ||
1015 | Foo { a: true } => (), | ||
1016 | Foo { a: false } => (), | ||
1017 | } | ||
1018 | match &f { | ||
1019 | Foo { a: true } => (), | ||
1020 | Foo { a: false } => (), | ||
1021 | } | ||
1022 | } | ||
1023 | "#, | ||
1024 | ); | ||
1025 | } | ||
1026 | |||
1027 | #[test] | ||
1028 | fn tuple_struct() { | ||
1029 | check_diagnostics( | ||
1030 | r#"struct Foo(bool); | ||
1031 | fn main(f: Foo) { | ||
1032 | match f {} | ||
1033 | //^ Missing match arm | ||
1034 | match f { Foo(true) => () } | ||
1035 | //^ Missing match arm | ||
1036 | match f { | ||
1037 | Foo(true) => (), | ||
1038 | Foo(false) => (), | ||
1039 | } | ||
1040 | } | ||
1041 | "#, | ||
1042 | ); | ||
1043 | } | ||
1044 | |||
1045 | #[test] | ||
1046 | fn unit_struct() { | ||
1047 | check_diagnostics( | ||
1048 | r#"struct Foo; | ||
1049 | fn main(f: Foo) { | ||
1050 | match f {} | ||
1051 | //^ Missing match arm | ||
1052 | match f { Foo => () } | ||
1053 | } | ||
1054 | "#, | ||
1055 | ); | ||
1056 | } | ||
1057 | |||
1058 | #[test] | ||
1059 | fn record_struct_ellipsis() { | ||
1060 | check_diagnostics( | ||
1061 | r#"struct Foo { foo: bool, bar: bool } | ||
1062 | fn main(f: Foo) { | ||
1063 | match f { Foo { foo: true, .. } => () } | ||
1064 | //^ Missing match arm | ||
1065 | match f { | ||
1066 | //^ Missing match arm | ||
1067 | Foo { foo: true, .. } => (), | ||
1068 | Foo { bar: false, .. } => () | ||
1069 | } | ||
1070 | match f { Foo { .. } => () } | ||
1071 | match f { | ||
1072 | Foo { foo: true, .. } => (), | ||
1073 | Foo { foo: false, .. } => () | ||
1074 | } | ||
1075 | } | ||
1076 | "#, | ||
1077 | ); | ||
1078 | } | ||
1079 | |||
1080 | #[test] | ||
1081 | fn internal_or() { | ||
1082 | check_diagnostics( | ||
1083 | r#" | ||
1084 | fn main() { | ||
1085 | enum Either { A(bool), B } | ||
1086 | match Either::B { | ||
1087 | //^^^^^^^^^ Missing match arm | ||
1088 | Either::A(true | false) => (), | ||
1089 | } | ||
1090 | } | ||
1091 | "#, | ||
1092 | ); | ||
1093 | } | ||
1094 | |||
1095 | #[test] | ||
1096 | fn no_panic_at_unimplemented_subpattern_type() { | ||
1097 | check_diagnostics( | ||
1098 | r#" | ||
1099 | struct S { a: char} | ||
1100 | fn main(v: S) { | ||
1101 | match v { S{ a } => {} } | ||
1102 | match v { S{ a: _x } => {} } | ||
1103 | match v { S{ a: 'a' } => {} } | ||
1104 | //^^^^^^^^^^^ Internal: match check bailed out | ||
1105 | match v { S{..} => {} } | ||
1106 | match v { _ => {} } | ||
1107 | match v { } | ||
1108 | //^ Missing match arm | ||
1109 | } | ||
1110 | "#, | ||
1111 | ); | ||
1112 | } | ||
1113 | |||
1114 | #[test] | ||
1115 | fn binding() { | ||
1116 | check_diagnostics( | ||
1117 | r#" | ||
1118 | fn main() { | ||
1119 | match true { | ||
1120 | _x @ true => {} | ||
1121 | false => {} | ||
1122 | } | ||
1123 | match true { _x @ true => {} } | ||
1124 | //^^^^ Missing match arm | ||
1125 | } | ||
1126 | "#, | ||
1127 | ); | ||
1128 | } | ||
1129 | |||
1130 | #[test] | ||
1131 | fn binding_ref_has_correct_type() { | ||
1132 | // Asserts `PatKind::Binding(ref _x): bool`, not &bool. | ||
1133 | // If that's not true match checking will panic with "incompatible constructors" | ||
1134 | // FIXME: make facilities to test this directly like `tests::check_infer(..)` | ||
1135 | check_diagnostics( | ||
1136 | r#" | ||
1137 | enum Foo { A } | ||
1138 | fn main() { | ||
1139 | // FIXME: this should not bail out but current behavior is such as the old algorithm. | ||
1140 | // ExprValidator::validate_match(..) checks types of top level patterns incorrecly. | ||
1141 | match Foo::A { | ||
1142 | ref _x => {} | ||
1143 | // ^^^^^^ Internal: match check bailed out | ||
1144 | Foo::A => {} | ||
1145 | } | ||
1146 | match (true,) { | ||
1147 | (ref _x,) => {} | ||
1148 | (true,) => {} | ||
1149 | } | ||
1150 | } | ||
1151 | "#, | ||
1152 | ); | ||
1153 | } | ||
1154 | |||
1155 | #[test] | ||
1156 | fn enum_non_exhaustive() { | ||
1157 | check_diagnostics( | ||
1158 | r#" | ||
1159 | //- /lib.rs crate:lib | ||
1160 | #[non_exhaustive] | ||
1161 | pub enum E { A, B } | ||
1162 | fn _local() { | ||
1163 | match E::A { _ => {} } | ||
1164 | match E::A { | ||
1165 | E::A => {} | ||
1166 | E::B => {} | ||
1167 | } | ||
1168 | match E::A { | ||
1169 | E::A | E::B => {} | ||
1170 | } | ||
1171 | } | ||
1172 | |||
1173 | //- /main.rs crate:main deps:lib | ||
1174 | use lib::E; | ||
1175 | fn main() { | ||
1176 | match E::A { _ => {} } | ||
1177 | match E::A { | ||
1178 | //^^^^ Missing match arm | ||
1179 | E::A => {} | ||
1180 | E::B => {} | ||
1181 | } | ||
1182 | match E::A { | ||
1183 | //^^^^ Missing match arm | ||
1184 | E::A | E::B => {} | ||
1185 | } | ||
1186 | } | ||
1187 | "#, | ||
1188 | ); | ||
1189 | } | ||
1190 | |||
1191 | #[test] | ||
1192 | fn match_guard() { | ||
1193 | check_diagnostics( | ||
1194 | r#" | ||
1195 | fn main() { | ||
1196 | match true { | ||
1197 | true if false => {} | ||
1198 | true => {} | ||
1199 | false => {} | ||
1200 | } | ||
1201 | match true { | ||
1202 | //^^^^ Missing match arm | ||
1203 | true if false => {} | ||
1204 | false => {} | ||
1205 | } | ||
1206 | "#, | ||
1207 | ); | ||
1208 | } | ||
1209 | |||
1210 | #[test] | ||
1211 | fn pattern_type_is_of_substitution() { | ||
1212 | cov_mark::check!(match_check_wildcard_expanded_to_substitutions); | ||
1213 | check_diagnostics( | ||
1214 | r#" | ||
1215 | struct Foo<T>(T); | ||
1216 | struct Bar; | ||
1217 | fn main() { | ||
1218 | match Foo(Bar) { | ||
1219 | _ | Foo(Bar) => {} | ||
1220 | } | ||
1221 | } | ||
1222 | "#, | ||
1223 | ); | ||
1224 | } | ||
1225 | |||
1226 | #[test] | ||
1227 | fn record_struct_no_such_field() { | ||
1228 | check_diagnostics( | ||
1229 | r#" | ||
1230 | struct Foo { } | ||
1231 | fn main(f: Foo) { | ||
1232 | match f { Foo { bar } => () } | ||
1233 | // ^^^^^^^^^^^ Internal: match check bailed out | ||
1234 | } | ||
1235 | "#, | ||
1236 | ); | ||
1237 | } | ||
1238 | |||
1239 | mod false_negatives { | ||
1240 | //! The implementation of match checking here is a work in progress. As we roll this out, we | ||
1241 | //! prefer false negatives to false positives (ideally there would be no false positives). This | ||
1242 | //! test module should document known false negatives. Eventually we will have a complete | ||
1243 | //! implementation of match checking and this module will be empty. | ||
1244 | //! | ||
1245 | //! The reasons for documenting known false negatives: | ||
1246 | //! | ||
1247 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. | ||
1248 | //! 2. It ensures the code doesn't panic when handling these cases. | ||
1249 | use super::*; | ||
1250 | |||
1251 | #[test] | ||
1252 | fn integers() { | ||
1253 | // We don't currently check integer exhaustiveness. | ||
1254 | check_diagnostics( | ||
1255 | r#" | ||
1256 | fn main() { | ||
1257 | match 5 { | ||
1258 | 10 => (), | ||
1259 | // ^^ Internal: match check bailed out | ||
1260 | 11..20 => (), | ||
1261 | } | ||
1262 | } | ||
1263 | "#, | ||
1264 | ); | ||
1265 | } | ||
1266 | |||
1267 | #[test] | ||
1268 | fn reference_patterns_at_top_level() { | ||
1269 | check_diagnostics( | ||
1270 | r#" | ||
1271 | fn main() { | ||
1272 | match &false { | ||
1273 | &true => {} | ||
1274 | // ^^^^^ Internal: match check bailed out | ||
1275 | } | ||
1276 | } | ||
1277 | "#, | ||
1278 | ); | ||
1279 | } | ||
1280 | |||
1281 | #[test] | ||
1282 | fn reference_patterns_in_fields() { | ||
1283 | check_diagnostics( | ||
1284 | r#" | ||
1285 | fn main() { | ||
1286 | match (&false,) { | ||
1287 | (true,) => {} | ||
1288 | // ^^^^^^^ Internal: match check bailed out | ||
1289 | } | ||
1290 | match (&false,) { | ||
1291 | (&true,) => {} | ||
1292 | // ^^^^^^^^ Internal: match check bailed out | ||
1293 | } | ||
1294 | } | ||
1295 | "#, | ||
1296 | ); | ||
1297 | } | ||
1298 | } | ||
1299 | } | ||