Skip to content

Commit b5728f1

Browse files
committed
Extract the two annoying lints into their own functions
1 parent 7925e12 commit b5728f1

File tree

2 files changed

+109
-108
lines changed

2 files changed

+109
-108
lines changed

compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs

+24-63
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,13 @@ use smallvec::SmallVec;
5252

5353
use rustc_data_structures::captures::Captures;
5454
use rustc_data_structures::fx::FxHashSet;
55-
use rustc_hir::{HirId, RangeEnd};
55+
use rustc_hir::RangeEnd;
5656
use rustc_index::Idx;
5757
use rustc_middle::middle::stability::EvalResult;
5858
use rustc_middle::mir;
5959
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange};
6060
use rustc_middle::ty::layout::IntegerExt;
6161
use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
62-
use rustc_session::lint;
6362
use rustc_span::{Span, DUMMY_SP};
6463
use rustc_target::abi::{FieldIdx, Integer, Size, VariantIdx, FIRST_VARIANT};
6564

@@ -68,7 +67,6 @@ use self::SliceKind::*;
6867

6968
use super::compare_const_vals;
7069
use super::usefulness::{MatchCheckCtxt, PatCtxt};
71-
use crate::errors::{Overlap, OverlappingRangeEndpoints};
7270

7371
/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
7472
fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
@@ -112,7 +110,7 @@ impl IntRange {
112110
self.range.start() == self.range.end()
113111
}
114112

115-
fn boundaries(&self) -> (u128, u128) {
113+
pub(super) fn boundaries(&self) -> (u128, u128) {
116114
(*self.range.start(), *self.range.end())
117115
}
118116

@@ -188,7 +186,7 @@ impl IntRange {
188186
}
189187

190188
/// Only used for displaying the range properly.
191-
fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> {
189+
pub(super) fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> {
192190
let (lo, hi) = self.boundaries();
193191

194192
let bias = IntRange::signed_bias(tcx, ty);
@@ -211,69 +209,36 @@ impl IntRange {
211209
Pat { ty, span: DUMMY_SP, kind }
212210
}
213211

214-
/// Lint on likely incorrect range patterns (#63987)
215-
pub(super) fn lint_overlapping_range_endpoints<'a, 'p: 'a, 'tcx: 'a>(
216-
&self,
217-
pcx: &PatCtxt<'_, 'p, 'tcx>,
218-
pats: impl Iterator<Item = (&'a DeconstructedPat<'p, 'tcx>, bool, HirId)>,
219-
column_count: usize,
220-
) {
221-
// FIXME: for now, only check for overlapping ranges on non-nested range patterns. Otherwise
222-
// with the current logic the following is detected as overlapping:
223-
// ```
224-
// match (0u8, true) {
225-
// (0 ..= 125, false) => {}
226-
// (125 ..= 255, true) => {}
227-
// _ => {}
228-
// }
229-
// ```
230-
if !self.is_singleton() || column_count != 1 {
231-
return;
232-
}
233-
234-
let overlap: u128 = self.boundaries().0;
235-
// Spans of ranges that start or end with the overlap.
212+
/// Given a list of ranges, gather all the ranges that have `endpoint` as an endpoint, and match
213+
/// them with ranges that came before and had `endpoint` as their other endpoint.
214+
/// In other words, for each range we gather the ranges above it that it intersects exactly on
215+
/// `endpoint`.
216+
pub(super) fn gather_endpoint_overlaps<'a, T: Clone>(
217+
endpoint: u128,
218+
ranges: impl Iterator<Item = (&'a IntRange, T)>,
219+
) -> Vec<(T, SmallVec<[T; 1]>)> {
220+
// Spans of ranges that start or end with the endpoint.
236221
let mut prefixes: SmallVec<[_; 1]> = Default::default();
237222
let mut suffixes: SmallVec<[_; 1]> = Default::default();
238-
// Iterate on rows that contained `overlap`.
239-
for (range, this_span, is_under_guard, arm_hir_id) in
240-
pats.filter_map(|(pat, under_guard, arm_hir_id)| {
241-
Some((pat.ctor().as_int_range()?, pat.span(), under_guard, arm_hir_id))
242-
})
243-
{
223+
let mut gathered_overlaps: Vec<_> = Vec::new();
224+
// Iterate on rows that contained `endpoint` as an endpoint.
225+
for (range, arm_data) in ranges {
244226
if range.is_singleton() {
245227
continue;
246228
}
247-
let mut overlaps: SmallVec<[_; 1]> = Default::default();
248-
if *range.range.start() == overlap {
229+
if *range.range.start() == endpoint {
249230
if !prefixes.is_empty() {
250-
overlaps = prefixes.clone();
231+
gathered_overlaps.push((arm_data.clone(), prefixes.clone()));
251232
}
252-
if !is_under_guard {
253-
suffixes.push(this_span)
254-
}
255-
} else if *range.range.end() == overlap {
233+
suffixes.push(arm_data)
234+
} else if *range.range.end() == endpoint {
256235
if !suffixes.is_empty() {
257-
overlaps = suffixes.clone();
258-
}
259-
if !is_under_guard {
260-
prefixes.push(this_span)
236+
gathered_overlaps.push((arm_data.clone(), suffixes.clone()));
261237
}
262-
}
263-
if !overlaps.is_empty() {
264-
let overlap_as_pat = self.to_pat(pcx.cx.tcx, pcx.ty);
265-
let overlaps: Vec<_> = overlaps
266-
.into_iter()
267-
.map(|span| Overlap { range: overlap_as_pat.clone(), span })
268-
.collect();
269-
pcx.cx.tcx.emit_spanned_lint(
270-
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
271-
arm_hir_id,
272-
this_span,
273-
OverlappingRangeEndpoints { overlap: overlaps, range: this_span },
274-
);
238+
prefixes.push(arm_data)
275239
}
276240
}
241+
gathered_overlaps
277242
}
278243
}
279244

@@ -658,21 +623,17 @@ pub(super) enum Constructor<'tcx> {
658623
}
659624

660625
impl<'tcx> Constructor<'tcx> {
661-
pub(super) fn is_wildcard(&self) -> bool {
662-
matches!(self, Wildcard)
663-
}
664-
665626
pub(super) fn is_non_exhaustive(&self) -> bool {
666627
matches!(self, NonExhaustive)
667628
}
668629

669-
fn as_variant(&self) -> Option<VariantIdx> {
630+
pub(super) fn as_variant(&self) -> Option<VariantIdx> {
670631
match self {
671632
Variant(i) => Some(*i),
672633
_ => None,
673634
}
674635
}
675-
fn as_int_range(&self) -> Option<&IntRange> {
636+
pub(super) fn as_int_range(&self) -> Option<&IntRange> {
676637
match self {
677638
IntRange(range) => Some(range),
678639
_ => None,

compiler/rustc_mir_build/src/thir/pattern/usefulness.rs

+85-45
Original file line numberDiff line numberDiff line change
@@ -305,16 +305,16 @@
305305
//! stay as a full constant and become an `Opaque` pattern. These `Opaque` patterns do not participate
306306
//! in exhaustiveness, specialization or overlap checking.
307307
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};
310310

311311
use rustc_arena::TypedArena;
312312
use rustc_data_structures::captures::Captures;
313313
use rustc_data_structures::stack::ensure_sufficient_stack;
314314
use rustc_hir::def_id::DefId;
315315
use rustc_hir::HirId;
316316
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};
318318
use rustc_span::{Span, DUMMY_SP};
319319

320320
use smallvec::{smallvec, SmallVec};
@@ -732,6 +732,68 @@ impl<'p, 'tcx> WitnessMatrix<'p, 'tcx> {
732732
}
733733
}
734734

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+
735797
/// Algorithm from <http://moscova.inria.fr/~maranget/papers/warn/index.html>.
736798
/// The algorithm from the paper has been modified to correctly handle empty
737799
/// types. The changes are:
@@ -812,63 +874,41 @@ fn compute_usefulness<'p, 'tcx>(
812874
witnesses.apply_constructor(pcx, &missing_ctors, &ctor, report_when_all_missing);
813875
ret.extend(witnesses);
814876

815-
// Lint on likely incorrect range patterns (#63987)
877+
// Lint on likely incorrect range patterns (#63987).
816878
if spec_matrix.rows().len() >= 2 && matches!(ctor_set, ConstructorSet::Integers { .. }) {
817879
if let Constructor::IntRange(overlap_range) = &ctor {
818880
// If two ranges overlap on their boundaries, that boundary will be found as a singleton
819881
// 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+
// ```
821893
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);
835897
}
836898
}
837899
}
838900

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).
841902
if cx.refutable
842903
&& (matches!(&ctor, Constructor::Missing)
843904
|| (matches!(&ctor, Constructor::Wildcard) && is_top_level))
844905
&& matches!(&ctor_set, ConstructorSet::Variants { non_exhaustive: true, .. })
845906
&& spec_matrix.rows().len() != 0
846907
{
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);
872912
}
873913

874914
for child_row in spec_matrix.rows() {

0 commit comments

Comments
 (0)