Skip to content

Commit 8b66f49

Browse files
committed
Lint overlapping ranges directly from exhaustiveness
1 parent 1ead476 commit 8b66f49

File tree

4 files changed

+49
-59
lines changed

4 files changed

+49
-59
lines changed

compiler/rustc_pattern_analysis/src/lib.rs

+14-11
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,9 @@ use rustc_middle::ty::Ty;
2727
#[cfg(feature = "rustc")]
2828
use rustc_span::ErrorGuaranteed;
2929

30-
use crate::constructor::{Constructor, ConstructorSet};
30+
use crate::constructor::{Constructor, ConstructorSet, IntRange};
3131
#[cfg(feature = "rustc")]
32-
use crate::lints::{
33-
lint_nonexhaustive_missing_variants, lint_overlapping_range_endpoints, PatternColumn,
34-
};
32+
use crate::lints::{lint_nonexhaustive_missing_variants, PatternColumn};
3533
use crate::pat::DeconstructedPat;
3634
#[cfg(feature = "rustc")]
3735
use crate::rustc::RustcMatchCheckCtxt;
@@ -77,6 +75,17 @@ pub trait TypeCx: Sized + fmt::Debug {
7775

7876
/// Raise a bug.
7977
fn bug(&self, fmt: fmt::Arguments<'_>) -> !;
78+
79+
/// Lint that the range `pat` overlapped with all the ranges in `overlaps_with`, where the range
80+
/// they overlapped over is `overlaps_on`. We only detect singleton overlaps.
81+
/// The default implementation does nothing.
82+
fn lint_overlapping_range_endpoints(
83+
&self,
84+
_pat: &DeconstructedPat<'_, Self>,
85+
_overlaps_on: IntRange,
86+
_overlaps_with: &[&DeconstructedPat<'_, Self>],
87+
) {
88+
}
8089
}
8190

8291
/// Context that provides information global to a match.
@@ -111,16 +120,10 @@ pub fn analyze_match<'p, 'tcx>(
111120

112121
let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity)?;
113122

114-
let pat_column = PatternColumn::new(arms);
115-
116-
// Lint ranges that overlap on their endpoints, which is likely a mistake.
117-
if !report.overlapping_range_endpoints.is_empty() {
118-
lint_overlapping_range_endpoints(cx, &report.overlapping_range_endpoints);
119-
}
120-
121123
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
122124
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
123125
if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() {
126+
let pat_column = PatternColumn::new(arms);
124127
lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty)?;
125128
}
126129

compiler/rustc_pattern_analysis/src/lints.rs

+3-29
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
use rustc_session::lint;
21
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
32
use rustc_span::ErrorGuaranteed;
43

5-
use crate::errors::{
6-
self, NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Uncovered,
7-
};
4+
use crate::errors::{NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Uncovered};
85
use crate::pat::PatOrWild;
96
use crate::rustc::{
10-
self, Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy,
11-
RustcMatchCheckCtxt, SplitConstructorSet, WitnessPat,
7+
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, RustcMatchCheckCtxt,
8+
SplitConstructorSet, WitnessPat,
129
};
1310

1411
/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
@@ -196,26 +193,3 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
196193
}
197194
Ok(())
198195
}
199-
200-
pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
201-
cx: MatchCtxt<'a, 'p, 'tcx>,
202-
overlapping_range_endpoints: &[rustc::OverlappingRanges<'p, 'tcx>],
203-
) {
204-
let rcx = cx.tycx;
205-
for overlap in overlapping_range_endpoints {
206-
let overlap_as_pat = rcx.hoist_pat_range(&overlap.overlaps_on, overlap.pat.ty());
207-
let overlaps: Vec<_> = overlap
208-
.overlaps_with
209-
.iter()
210-
.map(|pat| pat.data().unwrap().span)
211-
.map(|span| errors::Overlap { range: overlap_as_pat.clone(), span })
212-
.collect();
213-
let pat_span = overlap.pat.data().unwrap().span;
214-
rcx.tcx.emit_spanned_lint(
215-
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
216-
rcx.match_lint_level,
217-
pat_span,
218-
errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span },
219-
);
220-
}
221-
}

compiler/rustc_pattern_analysis/src/rustc.rs

+27-8
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,26 @@
1+
use smallvec::SmallVec;
12
use std::fmt;
23
use std::iter::once;
34

45
use rustc_arena::{DroplessArena, TypedArena};
56
use rustc_data_structures::captures::Captures;
67
use rustc_hir::def_id::DefId;
78
use rustc_hir::HirId;
8-
use rustc_index::Idx;
9-
use rustc_index::IndexVec;
9+
use rustc_index::{Idx, IndexVec};
1010
use rustc_middle::middle::stability::EvalResult;
1111
use rustc_middle::mir::interpret::Scalar;
1212
use rustc_middle::mir::{self, Const};
1313
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary};
1414
use rustc_middle::ty::layout::IntegerExt;
15-
use rustc_middle::ty::TypeVisitableExt;
16-
use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, VariantDef};
17-
use rustc_span::ErrorGuaranteed;
18-
use rustc_span::{Span, DUMMY_SP};
15+
use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef};
16+
use rustc_session::lint;
17+
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
1918
use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT};
20-
use smallvec::SmallVec;
2119

2220
use crate::constructor::{
2321
IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility,
2422
};
25-
use crate::TypeCx;
23+
use crate::{errors, TypeCx};
2624

2725
use crate::constructor::Constructor::*;
2826

@@ -991,6 +989,27 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
991989
fn bug(&self, fmt: fmt::Arguments<'_>) -> ! {
992990
span_bug!(self.scrut_span, "{}", fmt)
993991
}
992+
993+
fn lint_overlapping_range_endpoints(
994+
&self,
995+
pat: &crate::pat::DeconstructedPat<'_, Self>,
996+
overlaps_on: IntRange,
997+
overlaps_with: &[&crate::pat::DeconstructedPat<'_, Self>],
998+
) {
999+
let overlap_as_pat = self.hoist_pat_range(&overlaps_on, pat.ty());
1000+
let overlaps: Vec<_> = overlaps_with
1001+
.iter()
1002+
.map(|pat| pat.data().unwrap().span)
1003+
.map(|span| errors::Overlap { range: overlap_as_pat.clone(), span })
1004+
.collect();
1005+
let pat_span = pat.data().unwrap().span;
1006+
self.tcx.emit_spanned_lint(
1007+
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
1008+
self.match_lint_level,
1009+
pat_span,
1010+
errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span },
1011+
);
1012+
}
9941013
}
9951014

9961015
/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.

compiler/rustc_pattern_analysis/src/usefulness.rs

+5-11
Original file line numberDiff line numberDiff line change
@@ -1340,10 +1340,11 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
13401340
/// We can however get false negatives because exhaustiveness does not explore all cases. See the
13411341
/// section on relevancy at the top of the file.
13421342
fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
1343+
mcx: MatchCtxt<'_, Cx>,
13431344
overlap_range: IntRange,
13441345
matrix: &Matrix<'p, Cx>,
13451346
specialized_matrix: &Matrix<'p, Cx>,
1346-
overlapping_range_endpoints: &mut Vec<OverlappingRanges<'p, Cx>>,
1347+
_overlapping_range_endpoints: &mut Vec<OverlappingRanges<'p, Cx>>,
13471348
) {
13481349
let overlap = overlap_range.lo;
13491350
// Ranges that look like `lo..=overlap`.
@@ -1373,11 +1374,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
13731374
.map(|&(_, pat)| pat)
13741375
.collect();
13751376
if !overlaps_with.is_empty() {
1376-
overlapping_range_endpoints.push(OverlappingRanges {
1377-
pat,
1378-
overlaps_on: overlap_range,
1379-
overlaps_with,
1380-
});
1377+
mcx.tycx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with);
13811378
}
13821379
}
13831380
suffixes.push((child_row_id, pat))
@@ -1393,11 +1390,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
13931390
.map(|&(_, pat)| pat)
13941391
.collect();
13951392
if !overlaps_with.is_empty() {
1396-
overlapping_range_endpoints.push(OverlappingRanges {
1397-
pat,
1398-
overlaps_on: overlap_range,
1399-
overlaps_with,
1400-
});
1393+
mcx.tycx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with);
14011394
}
14021395
}
14031396
prefixes.push((child_row_id, pat))
@@ -1538,6 +1531,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
15381531
&& spec_matrix.rows.iter().any(|row| !row.intersects.is_empty())
15391532
{
15401533
collect_overlapping_range_endpoints(
1534+
mcx,
15411535
overlap_range,
15421536
matrix,
15431537
&spec_matrix,

0 commit comments

Comments
 (0)