1
Fork 0

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:
Matthias Krüger 2023-12-11 20:46:50 +01:00 committed by GitHub
commit dd0887c75c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 2614 additions and 2460 deletions

View file

@ -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> {

View file

@ -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

View file

@ -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