|
305 | 305 | //! stay as a full constant and become an `Opaque` pattern. These `Opaque` patterns do not participate
|
306 | 306 | //! in exhaustiveness, specialization or overlap checking.
|
307 | 307 |
|
308 |
| -use super::deconstruct_pat::{Constructor, ConstructorSet, DeconstructedPat, Fields}; |
309 |
| -use crate::errors::{NonExhaustiveOmittedPattern, Uncovered}; |
| 308 | +use super::deconstruct_pat::{Constructor, ConstructorSet, DeconstructedPat, Fields, IntRange}; |
| 309 | +use crate::errors::{NonExhaustiveOmittedPattern, Overlap, OverlappingRangeEndpoints, Uncovered}; |
310 | 310 |
|
311 | 311 | use rustc_arena::TypedArena;
|
312 | 312 | use rustc_data_structures::captures::Captures;
|
313 | 313 | use rustc_data_structures::stack::ensure_sufficient_stack;
|
314 | 314 | use rustc_hir::def_id::DefId;
|
315 | 315 | use rustc_hir::HirId;
|
316 | 316 | use rustc_middle::ty::{self, Ty, TyCtxt};
|
317 |
| -use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; |
| 317 | +use rustc_session::lint::builtin::{NON_EXHAUSTIVE_OMITTED_PATTERNS, OVERLAPPING_RANGE_ENDPOINTS}; |
318 | 318 | use rustc_span::{Span, DUMMY_SP};
|
319 | 319 |
|
320 | 320 | use smallvec::{smallvec, SmallVec};
|
@@ -732,6 +732,68 @@ impl<'p, 'tcx> WitnessMatrix<'p, 'tcx> {
|
732 | 732 | }
|
733 | 733 | }
|
734 | 734 |
|
| 735 | +fn lint_overlapping_range_endpoints<'a, 'p: 'a, 'tcx: 'a>( |
| 736 | + pcx: &PatCtxt<'_, 'p, 'tcx>, |
| 737 | + overlap_range: &IntRange, |
| 738 | + rows: impl Iterator<Item = &'a PatStack<'p, 'tcx>>, |
| 739 | +) { |
| 740 | + let endpoint: u128 = overlap_range.boundaries().0; |
| 741 | + let rows = rows.filter_map(|row| Some((row.head().ctor().as_int_range()?, row))); |
| 742 | + let gathered_overlaps = IntRange::gather_endpoint_overlaps(endpoint, rows); |
| 743 | + if !gathered_overlaps.is_empty() { |
| 744 | + let overlap_as_pat = overlap_range.to_pat(pcx.cx.tcx, pcx.ty); |
| 745 | + for (base_row, overlaps) in gathered_overlaps { |
| 746 | + let overlaps: Vec<_> = overlaps |
| 747 | + .into_iter() |
| 748 | + .filter(|overlapped_row| !overlapped_row.is_under_guard) |
| 749 | + .map(|overlapped_row| Overlap { |
| 750 | + range: overlap_as_pat.clone(), |
| 751 | + span: overlapped_row.head().span(), |
| 752 | + }) |
| 753 | + .collect(); |
| 754 | + if !overlaps.is_empty() { |
| 755 | + let arm_span = base_row.head().span(); |
| 756 | + pcx.cx.tcx.emit_spanned_lint( |
| 757 | + OVERLAPPING_RANGE_ENDPOINTS, |
| 758 | + base_row.arm_hir_id, |
| 759 | + arm_span, |
| 760 | + OverlappingRangeEndpoints { overlap: overlaps, range: arm_span }, |
| 761 | + ); |
| 762 | + } |
| 763 | + } |
| 764 | + } |
| 765 | +} |
| 766 | + |
| 767 | +fn lint_non_exhaustive_omitted_patterns<'a, 'p: 'a, 'tcx: 'a>( |
| 768 | + pcx: &PatCtxt<'_, 'p, 'tcx>, |
| 769 | + missing_ctors: &[Constructor<'tcx>], |
| 770 | + lint_row: &'a PatStack<'p, 'tcx>, |
| 771 | +) { |
| 772 | + let patterns = missing_ctors |
| 773 | + .iter() |
| 774 | + // We only list real variants. |
| 775 | + .filter(|c| c.as_variant().is_some()) |
| 776 | + .cloned() |
| 777 | + .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor)) |
| 778 | + .collect::<Vec<_>>(); |
| 779 | + |
| 780 | + if !patterns.is_empty() { |
| 781 | + let wildcard_span = lint_row.head().span(); |
| 782 | + // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` |
| 783 | + // is not exhaustive enough. |
| 784 | + // NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`. |
| 785 | + pcx.cx.tcx.emit_spanned_lint( |
| 786 | + NON_EXHAUSTIVE_OMITTED_PATTERNS, |
| 787 | + lint_row.arm_hir_id, |
| 788 | + wildcard_span, |
| 789 | + NonExhaustiveOmittedPattern { |
| 790 | + scrut_ty: pcx.ty, |
| 791 | + uncovered: Uncovered::new(wildcard_span, pcx.cx, patterns), |
| 792 | + }, |
| 793 | + ); |
| 794 | + } |
| 795 | +} |
| 796 | + |
735 | 797 | /// Algorithm from <http://moscova.inria.fr/~maranget/papers/warn/index.html>.
|
736 | 798 | /// The algorithm from the paper has been modified to correctly handle empty
|
737 | 799 | /// types. The changes are:
|
@@ -812,63 +874,41 @@ fn compute_usefulness<'p, 'tcx>(
|
812 | 874 | witnesses.apply_constructor(pcx, &missing_ctors, &ctor, report_when_all_missing);
|
813 | 875 | ret.extend(witnesses);
|
814 | 876 |
|
815 |
| - // Lint on likely incorrect range patterns (#63987) |
| 877 | + // Lint on likely incorrect range patterns (#63987). |
816 | 878 | if spec_matrix.rows().len() >= 2 && matches!(ctor_set, ConstructorSet::Integers { .. }) {
|
817 | 879 | if let Constructor::IntRange(overlap_range) = &ctor {
|
818 | 880 | // If two ranges overlap on their boundaries, that boundary will be found as a singleton
|
819 | 881 | // range after splitting.
|
820 |
| - // We limit to a single column for now, see `lint_overlapping_range_endpoints`. |
| 882 | + // |
| 883 | + // FIXME: for now, only check for overlapping ranges on non-nested (column_count == |
| 884 | + // 1) range patterns. Otherwise with the current logic the following is detected as |
| 885 | + // overlapping: |
| 886 | + // ``` |
| 887 | + // match (0u8, true) { |
| 888 | + // (0 ..= 125, false) => {} |
| 889 | + // (125 ..= 255, true) => {} |
| 890 | + // _ => {} |
| 891 | + // } |
| 892 | + // ``` |
821 | 893 | if overlap_range.is_singleton() && matrix.column_count() == 1 {
|
822 |
| - overlap_range.lint_overlapping_range_endpoints( |
823 |
| - pcx, |
824 |
| - spec_matrix.rows().map(|child_row| &matrix.rows[child_row.parent_row]).map( |
825 |
| - |parent_row| { |
826 |
| - ( |
827 |
| - parent_row.head(), |
828 |
| - parent_row.is_under_guard, |
829 |
| - parent_row.arm_hir_id, |
830 |
| - ) |
831 |
| - }, |
832 |
| - ), |
833 |
| - matrix.column_count(), |
834 |
| - ); |
| 894 | + let parent_rows = |
| 895 | + spec_matrix.rows().map(|child_row| &matrix.rows[child_row.parent_row]); |
| 896 | + lint_overlapping_range_endpoints(pcx, overlap_range, parent_rows); |
835 | 897 | }
|
836 | 898 | }
|
837 | 899 | }
|
838 | 900 |
|
839 |
| - // When all the conditions are met we have a match with a `non_exhaustive` enum |
840 |
| - // that has the potential to trigger the `non_exhaustive_omitted_patterns` lint. |
| 901 | + // Lint on incomplete non_exhaustive match (#89554). |
841 | 902 | if cx.refutable
|
842 | 903 | && (matches!(&ctor, Constructor::Missing)
|
843 | 904 | || (matches!(&ctor, Constructor::Wildcard) && is_top_level))
|
844 | 905 | && matches!(&ctor_set, ConstructorSet::Variants { non_exhaustive: true, .. })
|
845 | 906 | && spec_matrix.rows().len() != 0
|
846 | 907 | {
|
847 |
| - let patterns = missing_ctors |
848 |
| - .iter() |
849 |
| - // We only list real variants. |
850 |
| - .filter(|c| !(c.is_non_exhaustive() || c.is_wildcard())) |
851 |
| - .cloned() |
852 |
| - .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor)) |
853 |
| - .collect::<Vec<_>>(); |
854 |
| - |
855 |
| - if !patterns.is_empty() { |
856 |
| - let first_spec_row = spec_matrix.rows().next().unwrap(); |
857 |
| - let first_wildcard_row = &matrix.rows[first_spec_row.parent_row]; |
858 |
| - let wildcard_span = first_wildcard_row.head().span(); |
859 |
| - // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` |
860 |
| - // is not exhaustive enough. |
861 |
| - // NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`. |
862 |
| - cx.tcx.emit_spanned_lint( |
863 |
| - NON_EXHAUSTIVE_OMITTED_PATTERNS, |
864 |
| - first_wildcard_row.arm_hir_id, |
865 |
| - wildcard_span, |
866 |
| - NonExhaustiveOmittedPattern { |
867 |
| - scrut_ty: pcx.ty, |
868 |
| - uncovered: Uncovered::new(wildcard_span, pcx.cx, patterns), |
869 |
| - }, |
870 |
| - ); |
871 |
| - } |
| 908 | + let mut parent_rows = |
| 909 | + spec_matrix.rows().map(|child_row| &matrix.rows[child_row.parent_row]); |
| 910 | + let first_parent_row = parent_rows.next().unwrap(); |
| 911 | + lint_non_exhaustive_omitted_patterns(pcx, &missing_ctors, first_parent_row); |
872 | 912 | }
|
873 | 913 |
|
874 | 914 | for child_row in spec_matrix.rows() {
|
|
0 commit comments