Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/diagnostics/expr.rs')
-rw-r--r--crates/hir-ty/src/diagnostics/expr.rs51
1 files changed, 46 insertions, 5 deletions
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index 335d95c0cf..e9743b4dfe 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -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()
+ )
+ }
+ }
+ }
+}