Lint overlapping ranges directly from exhaustiveness

This commit is contained in:
Nadrieril 2024-01-15 19:17:28 +01:00
parent 1ead4761e9
commit 8b66f497eb
4 changed files with 49 additions and 59 deletions

View File

@ -27,11 +27,9 @@ use rustc_middle::ty::Ty;
#[cfg(feature = "rustc")]
use rustc_span::ErrorGuaranteed;
use crate::constructor::{Constructor, ConstructorSet};
use crate::constructor::{Constructor, ConstructorSet, IntRange};
#[cfg(feature = "rustc")]
use crate::lints::{
lint_nonexhaustive_missing_variants, lint_overlapping_range_endpoints, PatternColumn,
};
use crate::lints::{lint_nonexhaustive_missing_variants, PatternColumn};
use crate::pat::DeconstructedPat;
#[cfg(feature = "rustc")]
use crate::rustc::RustcMatchCheckCtxt;
@ -77,6 +75,17 @@ pub trait TypeCx: Sized + fmt::Debug {
/// Raise a bug.
fn bug(&self, fmt: fmt::Arguments<'_>) -> !;
/// Lint that the range `pat` overlapped with all the ranges in `overlaps_with`, where the range
/// they overlapped over is `overlaps_on`. We only detect singleton overlaps.
/// The default implementation does nothing.
fn lint_overlapping_range_endpoints(
&self,
_pat: &DeconstructedPat<'_, Self>,
_overlaps_on: IntRange,
_overlaps_with: &[&DeconstructedPat<'_, Self>],
) {
}
}
/// Context that provides information global to a match.
@ -111,16 +120,10 @@ pub fn analyze_match<'p, 'tcx>(
let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity)?;
let pat_column = PatternColumn::new(arms);
// Lint ranges that overlap on their endpoints, which is likely a mistake.
if !report.overlapping_range_endpoints.is_empty() {
lint_overlapping_range_endpoints(cx, &report.overlapping_range_endpoints);
}
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() {
let pat_column = PatternColumn::new(arms);
lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty)?;
}

View File

@ -1,14 +1,11 @@
use rustc_session::lint;
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_span::ErrorGuaranteed;
use crate::errors::{
self, NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Uncovered,
};
use crate::errors::{NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Uncovered};
use crate::pat::PatOrWild;
use crate::rustc::{
self, Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy,
RustcMatchCheckCtxt, SplitConstructorSet, WitnessPat,
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, RustcMatchCheckCtxt,
SplitConstructorSet, WitnessPat,
};
/// 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>(
}
Ok(())
}
pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
cx: MatchCtxt<'a, 'p, 'tcx>,
overlapping_range_endpoints: &[rustc::OverlappingRanges<'p, 'tcx>],
) {
let rcx = cx.tycx;
for overlap in overlapping_range_endpoints {
let overlap_as_pat = rcx.hoist_pat_range(&overlap.overlaps_on, overlap.pat.ty());
let overlaps: Vec<_> = overlap
.overlaps_with
.iter()
.map(|pat| pat.data().unwrap().span)
.map(|span| errors::Overlap { range: overlap_as_pat.clone(), span })
.collect();
let pat_span = overlap.pat.data().unwrap().span;
rcx.tcx.emit_spanned_lint(
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
rcx.match_lint_level,
pat_span,
errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span },
);
}
}

View File

@ -1,3 +1,4 @@
use smallvec::SmallVec;
use std::fmt;
use std::iter::once;
@ -5,24 +6,21 @@ use rustc_arena::{DroplessArena, TypedArena};
use rustc_data_structures::captures::Captures;
use rustc_hir::def_id::DefId;
use rustc_hir::HirId;
use rustc_index::Idx;
use rustc_index::IndexVec;
use rustc_index::{Idx, IndexVec};
use rustc_middle::middle::stability::EvalResult;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::{self, Const};
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary};
use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::TypeVisitableExt;
use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, VariantDef};
use rustc_span::ErrorGuaranteed;
use rustc_span::{Span, DUMMY_SP};
use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef};
use rustc_session::lint;
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT};
use smallvec::SmallVec;
use crate::constructor::{
IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility,
};
use crate::TypeCx;
use crate::{errors, TypeCx};
use crate::constructor::Constructor::*;
@ -991,6 +989,27 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
fn bug(&self, fmt: fmt::Arguments<'_>) -> ! {
span_bug!(self.scrut_span, "{}", fmt)
}
fn lint_overlapping_range_endpoints(
&self,
pat: &crate::pat::DeconstructedPat<'_, Self>,
overlaps_on: IntRange,
overlaps_with: &[&crate::pat::DeconstructedPat<'_, Self>],
) {
let overlap_as_pat = self.hoist_pat_range(&overlaps_on, pat.ty());
let overlaps: Vec<_> = overlaps_with
.iter()
.map(|pat| pat.data().unwrap().span)
.map(|span| errors::Overlap { range: overlap_as_pat.clone(), span })
.collect();
let pat_span = pat.data().unwrap().span;
self.tcx.emit_spanned_lint(
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
self.match_lint_level,
pat_span,
errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span },
);
}
}
/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.

View File

@ -1340,10 +1340,11 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
/// We can however get false negatives because exhaustiveness does not explore all cases. See the
/// section on relevancy at the top of the file.
fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
mcx: MatchCtxt<'_, Cx>,
overlap_range: IntRange,
matrix: &Matrix<'p, Cx>,
specialized_matrix: &Matrix<'p, Cx>,
overlapping_range_endpoints: &mut Vec<OverlappingRanges<'p, Cx>>,
_overlapping_range_endpoints: &mut Vec<OverlappingRanges<'p, Cx>>,
) {
let overlap = overlap_range.lo;
// Ranges that look like `lo..=overlap`.
@ -1373,11 +1374,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
.map(|&(_, pat)| pat)
.collect();
if !overlaps_with.is_empty() {
overlapping_range_endpoints.push(OverlappingRanges {
pat,
overlaps_on: overlap_range,
overlaps_with,
});
mcx.tycx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with);
}
}
suffixes.push((child_row_id, pat))
@ -1393,11 +1390,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
.map(|&(_, pat)| pat)
.collect();
if !overlaps_with.is_empty() {
overlapping_range_endpoints.push(OverlappingRanges {
pat,
overlaps_on: overlap_range,
overlaps_with,
});
mcx.tycx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with);
}
}
prefixes.push((child_row_id, pat))
@ -1538,6 +1531,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
&& spec_matrix.rows.iter().any(|row| !row.intersects.is_empty())
{
collect_overlapping_range_endpoints(
mcx,
overlap_range,
matrix,
&spec_matrix,