Rollup merge of #118822 - Nadrieril:librarify, r=compiler-errors
Extract exhaustiveness into its own crate It now makes sense to extract exhaustiveness into its own crate! This was much-requested by rust-analyzer (they currently maintain by hand a copy of the algorithm), and I hope this can serve other projects e.g. clippy. This is the churny PR: it exclusively moves code around. It's not yet useable outside of rustc but I wanted the churny parts to be out of the way. r? `@compiler-errors`
This commit is contained in:
commit
dd0887c75c
19 changed files with 2614 additions and 2460 deletions
|
@ -1,15 +1,12 @@
|
|||
use crate::{
|
||||
fluent_generated as fluent,
|
||||
thir::pattern::{deconstruct_pat::WitnessPat, MatchCheckCtxt},
|
||||
};
|
||||
use crate::fluent_generated as fluent;
|
||||
use rustc_errors::DiagnosticArgValue;
|
||||
use rustc_errors::{
|
||||
error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
|
||||
Handler, IntoDiagnostic, MultiSpan, SubdiagnosticMessage,
|
||||
};
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::thir::Pat;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_pattern_analysis::{cx::MatchCheckCtxt, errors::Uncovered};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::Span;
|
||||
|
||||
|
@ -812,94 +809,6 @@ pub struct NonPartialEqMatch<'tcx> {
|
|||
pub non_peq_ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_build_overlapping_range_endpoints)]
|
||||
#[note]
|
||||
pub struct OverlappingRangeEndpoints<'tcx> {
|
||||
#[label(mir_build_range)]
|
||||
pub range: Span,
|
||||
#[subdiagnostic]
|
||||
pub overlap: Vec<Overlap<'tcx>>,
|
||||
}
|
||||
|
||||
pub struct Overlap<'tcx> {
|
||||
pub span: Span,
|
||||
pub range: Pat<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> AddToDiagnostic for Overlap<'tcx> {
|
||||
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
|
||||
where
|
||||
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
|
||||
{
|
||||
let Overlap { span, range } = self;
|
||||
|
||||
// FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]`
|
||||
// does not support `#[subdiagnostic(eager)]`...
|
||||
let message = format!("this range overlaps on `{range}`...");
|
||||
diag.span_label(span, message);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_build_non_exhaustive_omitted_pattern)]
|
||||
#[help]
|
||||
#[note]
|
||||
pub(crate) struct NonExhaustiveOmittedPattern<'tcx> {
|
||||
pub scrut_ty: Ty<'tcx>,
|
||||
#[subdiagnostic]
|
||||
pub uncovered: Uncovered<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_build_non_exhaustive_omitted_pattern_lint_on_arm)]
|
||||
#[help]
|
||||
pub(crate) struct NonExhaustiveOmittedPatternLintOnArm {
|
||||
#[label]
|
||||
pub lint_span: Span,
|
||||
#[suggestion(code = "#[{lint_level}({lint_name})]\n", applicability = "maybe-incorrect")]
|
||||
pub suggest_lint_on_match: Option<Span>,
|
||||
pub lint_level: &'static str,
|
||||
pub lint_name: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[label(mir_build_uncovered)]
|
||||
pub(crate) struct Uncovered<'tcx> {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
count: usize,
|
||||
witness_1: Pat<'tcx>,
|
||||
witness_2: Pat<'tcx>,
|
||||
witness_3: Pat<'tcx>,
|
||||
remainder: usize,
|
||||
}
|
||||
|
||||
impl<'tcx> Uncovered<'tcx> {
|
||||
pub fn new<'p>(
|
||||
span: Span,
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
witnesses: Vec<WitnessPat<'tcx>>,
|
||||
) -> Self {
|
||||
let witness_1 = witnesses.get(0).unwrap().to_diagnostic_pat(cx);
|
||||
Self {
|
||||
span,
|
||||
count: witnesses.len(),
|
||||
// Substitute dummy values if witnesses is smaller than 3. These will never be read.
|
||||
witness_2: witnesses
|
||||
.get(1)
|
||||
.map(|w| w.to_diagnostic_pat(cx))
|
||||
.unwrap_or_else(|| witness_1.clone()),
|
||||
witness_3: witnesses
|
||||
.get(2)
|
||||
.map(|w| w.to_diagnostic_pat(cx))
|
||||
.unwrap_or_else(|| witness_1.clone()),
|
||||
witness_1,
|
||||
remainder: witnesses.len().saturating_sub(3),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(mir_build_pattern_not_covered, code = "E0005")]
|
||||
pub(crate) struct PatternNotCovered<'s, 'tcx> {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use super::deconstruct_pat::{Constructor, DeconstructedPat, WitnessPat};
|
||||
use super::usefulness::{
|
||||
compute_match_usefulness, MatchArm, MatchCheckCtxt, Usefulness, UsefulnessReport,
|
||||
};
|
||||
use rustc_pattern_analysis::constructor::Constructor;
|
||||
use rustc_pattern_analysis::cx::MatchCheckCtxt;
|
||||
use rustc_pattern_analysis::errors::Uncovered;
|
||||
use rustc_pattern_analysis::pat::{DeconstructedPat, WitnessPat};
|
||||
use rustc_pattern_analysis::usefulness::{Usefulness, UsefulnessReport};
|
||||
use rustc_pattern_analysis::{analyze_match, MatchArm};
|
||||
|
||||
use crate::errors::*;
|
||||
|
||||
|
@ -284,7 +286,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
|
|||
check_borrow_conflicts_in_at_patterns(self, pat);
|
||||
check_for_bindings_named_same_as_variants(self, pat, refutable);
|
||||
});
|
||||
Ok(cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, pat)))
|
||||
Ok(cx.pattern_arena.alloc(cx.lower_pat(pat)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,7 +435,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
|
|||
}
|
||||
|
||||
let scrut_ty = scrut.ty;
|
||||
let report = compute_match_usefulness(&cx, &tarms, scrut_ty);
|
||||
let report = analyze_match(&cx, &tarms, scrut_ty);
|
||||
|
||||
match source {
|
||||
// Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
|
||||
|
@ -547,7 +549,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
|
|||
let cx = self.new_cx(refutability, None, scrut, pat.span);
|
||||
let pat = self.lower_pattern(&cx, pat)?;
|
||||
let arms = [MatchArm { pat, hir_id: self.lint_level, has_guard: false }];
|
||||
let report = compute_match_usefulness(&cx, &arms, pat.ty());
|
||||
let report = analyze_match(&cx, &arms, pat.ty());
|
||||
Ok((cx, report))
|
||||
}
|
||||
|
||||
|
@ -924,7 +926,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
|
|||
pattern = if witnesses.len() < 4 {
|
||||
witnesses
|
||||
.iter()
|
||||
.map(|witness| witness.to_diagnostic_pat(cx).to_string())
|
||||
.map(|witness| cx.hoist_witness_pat(witness).to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(" | ")
|
||||
} else {
|
||||
|
@ -948,7 +950,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
|
|||
if !is_empty_match {
|
||||
let mut non_exhaustive_tys = FxHashSet::default();
|
||||
// Look at the first witness.
|
||||
collect_non_exhaustive_tys(cx.tcx, &witnesses[0], &mut non_exhaustive_tys);
|
||||
collect_non_exhaustive_tys(cx, &witnesses[0], &mut non_exhaustive_tys);
|
||||
|
||||
for ty in non_exhaustive_tys {
|
||||
if ty.is_ptr_sized_integral() {
|
||||
|
@ -1083,13 +1085,13 @@ fn joined_uncovered_patterns<'p, 'tcx>(
|
|||
witnesses: &[WitnessPat<'tcx>],
|
||||
) -> String {
|
||||
const LIMIT: usize = 3;
|
||||
let pat_to_str = |pat: &WitnessPat<'tcx>| pat.to_diagnostic_pat(cx).to_string();
|
||||
let pat_to_str = |pat: &WitnessPat<'tcx>| cx.hoist_witness_pat(pat).to_string();
|
||||
match witnesses {
|
||||
[] => bug!(),
|
||||
[witness] => format!("`{}`", witness.to_diagnostic_pat(cx)),
|
||||
[witness] => format!("`{}`", cx.hoist_witness_pat(witness)),
|
||||
[head @ .., tail] if head.len() < LIMIT => {
|
||||
let head: Vec<_> = head.iter().map(pat_to_str).collect();
|
||||
format!("`{}` and `{}`", head.join("`, `"), tail.to_diagnostic_pat(cx))
|
||||
format!("`{}` and `{}`", head.join("`, `"), cx.hoist_witness_pat(tail))
|
||||
}
|
||||
_ => {
|
||||
let (head, tail) = witnesses.split_at(LIMIT);
|
||||
|
@ -1100,7 +1102,7 @@ fn joined_uncovered_patterns<'p, 'tcx>(
|
|||
}
|
||||
|
||||
fn collect_non_exhaustive_tys<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
cx: &MatchCheckCtxt<'_, 'tcx>,
|
||||
pat: &WitnessPat<'tcx>,
|
||||
non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
|
||||
) {
|
||||
|
@ -1108,13 +1110,13 @@ fn collect_non_exhaustive_tys<'tcx>(
|
|||
non_exhaustive_tys.insert(pat.ty());
|
||||
}
|
||||
if let Constructor::IntRange(range) = pat.ctor() {
|
||||
if range.is_beyond_boundaries(pat.ty(), tcx) {
|
||||
if cx.is_range_beyond_boundaries(range, pat.ty()) {
|
||||
// The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
|
||||
non_exhaustive_tys.insert(pat.ty());
|
||||
}
|
||||
}
|
||||
pat.iter_fields()
|
||||
.for_each(|field_pat| collect_non_exhaustive_tys(tcx, field_pat, non_exhaustive_tys))
|
||||
.for_each(|field_pat| collect_non_exhaustive_tys(cx, field_pat, non_exhaustive_tys))
|
||||
}
|
||||
|
||||
fn report_adt_defined_here<'tcx>(
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,11 +2,8 @@
|
|||
|
||||
mod check_match;
|
||||
mod const_to_pat;
|
||||
pub(crate) mod deconstruct_pat;
|
||||
mod usefulness;
|
||||
|
||||
pub(crate) use self::check_match::check_match;
|
||||
pub(crate) use self::usefulness::MatchCheckCtxt;
|
||||
|
||||
use crate::errors::*;
|
||||
use crate::thir::util::UserAnnotatedTyHelpers;
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue