Display witnesses of non-exhaustive match

Reporting format follows rustc and shows at most three witnesses.
This commit is contained in:
iDawer 2022-06-20 15:48:09 +05:00
parent ad6810e90b
commit 4ff9bedbed
6 changed files with 320 additions and 80 deletions

View file

@ -4,9 +4,10 @@
use std::sync::Arc;
use hir_def::{path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule};
use hir_def::{path::path, resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, HasModule};
use hir_expand::name;
use itertools::Either;
use itertools::Itertools;
use rustc_hash::FxHashSet;
use typed_arena::Arena;
@ -17,7 +18,8 @@ use crate::{
deconstruct_pat::DeconstructedPat,
usefulness::{compute_match_usefulness, MatchCheckCtx},
},
InferenceResult, TyExt,
display::HirDisplay,
InferenceResult, Ty, TyExt,
};
pub(crate) use hir_def::{
@ -37,6 +39,7 @@ pub enum BodyValidationDiagnostic {
},
MissingMatchArms {
match_expr: ExprId,
uncovered_patterns: String,
},
}
@ -211,10 +214,11 @@ impl ExprValidator {
// https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200
let witnesses = report.non_exhaustiveness_witnesses;
// FIXME Report witnesses
// eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses);
if !witnesses.is_empty() {
self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms { match_expr: id });
self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms {
match_expr: id,
uncovered_patterns: missing_match_arms(&cx, match_expr_ty, witnesses, arms),
});
}
}
@ -367,3 +371,40 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul
walk(pat, body, infer, &mut has_type_mismatches);
!has_type_mismatches
}
fn missing_match_arms<'p>(
cx: &MatchCheckCtx<'_, 'p>,
scrut_ty: &Ty,
witnesses: Vec<DeconstructedPat<'p>>,
arms: &[MatchArm],
) -> String {
let non_empty_enum = match scrut_ty.as_adt() {
Some((AdtId::EnumId(e), _)) => !cx.db.enum_data(e).variants.is_empty(),
_ => false,
};
if arms.is_empty() && !non_empty_enum {
format!("type `{}` is non-empty", scrut_ty.display(cx.db))
} else {
const LIMIT: usize = 3;
match &*witnesses {
[witness] => format!("`{}` not covered", witness.to_pat(&cx).display(cx.db)),
[head @ .., tail] if head.len() < LIMIT => {
let head: Vec<_> = head.iter().map(|w| w.to_pat(cx)).collect();
format!(
"`{}` and `{}` not covered",
head.iter().map(|p| p.display(cx.db)).join("`, `"),
tail.to_pat(&cx).display(cx.db)
)
}
_ => {
let (head, tail) = witnesses.split_at(LIMIT);
let head: Vec<_> = head.iter().map(|w| w.to_pat(cx)).collect();
format!(
"`{}` and {} more not covered",
head.iter().map(|p| p.display(cx.db)).join("`, `"),
tail.len()
)
}
}
}
}