From 49e016169fc8413e2734a655cbd55ebba2907b76 Mon Sep 17 00:00:00 2001
From: Dawer <7803845+iDawer@users.noreply.github.com>
Date: Mon, 10 May 2021 13:22:13 +0500
Subject: Check pattern types.
---
crates/hir_ty/src/diagnostics/expr.rs | 77 ++++++++++++++--------
crates/hir_ty/src/diagnostics/pattern.rs | 36 ++++++++++
.../hir_ty/src/diagnostics/pattern/usefulness.rs | 2 +-
3 files changed, 85 insertions(+), 30 deletions(-)
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 929c4a9cc..e4e9ab5c0 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -379,32 +379,58 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
let (body, source_map): (Arc
, Arc) =
db.body_with_source_map(self.owner);
- let _match_expr_ty = if infer.type_of_expr[match_expr].is_unknown() {
+ let match_expr_ty = if infer.type_of_expr[match_expr].is_unknown() {
return;
} else {
&infer.type_of_expr[match_expr]
};
- // eprintln!("ExprValidator::validate_match2({:?})", _match_expr_ty.kind(&Interner));
let pattern_arena = RefCell::new(PatternArena::new());
- let mut have_errors = false;
- let m_arms: Vec<_> = arms
- .iter()
- .map(|arm| usefulness::MatchArm {
- pat: self.lower_pattern(
- arm.pat,
- &mut pattern_arena.borrow_mut(),
- db,
- &body,
- &mut have_errors,
- ),
- has_guard: arm.guard.is_some(),
- })
- .collect();
-
- // Bail out early if lowering failed.
- if have_errors {
+ let mut m_arms = Vec::new();
+ let mut has_lowering_errors = false;
+ for arm in arms {
+ if let Some(pat_ty) = infer.type_of_pat.get(arm.pat) {
+ // We only include patterns whose type matches the type
+ // of the match expression. If we had a InvalidMatchArmPattern
+ // diagnostic or similar we could raise that in an else
+ // block here.
+ //
+ // When comparing the types, we also have to consider that rustc
+ // will automatically de-reference the match expression type if
+ // necessary.
+ //
+ // FIXME we should use the type checker for this.
+ if pat_ty == match_expr_ty
+ || match_expr_ty
+ .as_reference()
+ .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty)
+ .unwrap_or(false)
+ {
+ // If we had a NotUsefulMatchArm diagnostic, we could
+ // check the usefulness of each pattern as we added it
+ // to the matrix here.
+ let m_arm = usefulness::MatchArm {
+ pat: self.lower_pattern(
+ arm.pat,
+ &mut pattern_arena.borrow_mut(),
+ db,
+ &body,
+ &mut has_lowering_errors,
+ ),
+ has_guard: arm.guard.is_some(),
+ };
+ m_arms.push(m_arm);
+ if !has_lowering_errors {
+ continue;
+ }
+ }
+ }
+
+ // If we can't resolve the type of a pattern, or the pattern type doesn't
+ // fit the match expression, we skip this diagnostic. Skipping the entire
+ // diagnostic rather than just not including this match arm is preferred
+ // to avoid the chance of false positives.
return;
}
@@ -418,18 +444,11 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
};
let report = usefulness::compute_match_usefulness(&cx, &m_arms);
- // TODO Report unreacheble arms
- // let mut catchall = None;
- // for (arm_index, (arm, is_useful)) in report.arm_usefulness.iter().enumerate() {
- // match is_useful{
- // Unreachable => {
- // }
- // Reachable(_) => {}
- // }
- // }
+ // FIXME Report unreacheble arms
+ // https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200-L201
let witnesses = report.non_exhaustiveness_witnesses;
- eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses);
+ // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses);
if !witnesses.is_empty() {
if let Ok(source_ptr) = source_map.expr_syntax(id) {
let root = source_ptr.file_syntax(db.upcast());
diff --git a/crates/hir_ty/src/diagnostics/pattern.rs b/crates/hir_ty/src/diagnostics/pattern.rs
index d98fb0867..38e4b53b7 100644
--- a/crates/hir_ty/src/diagnostics/pattern.rs
+++ b/crates/hir_ty/src/diagnostics/pattern.rs
@@ -511,4 +511,40 @@ fn main() {
"#,
);
}
+
+ /// These failing tests are narrowed down from "hir_ty::diagnostics::match_check::tests"
+ // TODO fix
+ mod failing {
+ use super::*;
+
+ #[test]
+ fn never() {
+ check_diagnostics(
+ r#"
+enum Never {}
+
+fn enum_ref(never: &Never) {
+ match never {}
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn unknown_type() {
+ check_diagnostics(
+ r#"
+enum Option { Some(T), None }
+
+fn main() {
+ // `Never` is deliberately not defined so that it's an uninferred type.
+ match Option::::None {
+ None => {}
+ Some(never) => {}
+ }
+}
+"#,
+ );
+ }
+ }
}
diff --git a/crates/hir_ty/src/diagnostics/pattern/usefulness.rs b/crates/hir_ty/src/diagnostics/pattern/usefulness.rs
index ef2be7530..01a7fb0d9 100644
--- a/crates/hir_ty/src/diagnostics/pattern/usefulness.rs
+++ b/crates/hir_ty/src/diagnostics/pattern/usefulness.rs
@@ -28,7 +28,7 @@ pub(crate) struct MatchCheckCtx<'a> {
pub(crate) body: Arc,
pub(crate) infer: &'a InferenceResult,
pub(crate) db: &'a dyn HirDatabase,
- /// Patterns from self.body.pats plus generated by the check.
+ /// Lowered patterns from self.body.pats plus generated by the check.
pub(crate) pattern_arena: &'a RefCell,
}
--
cgit v1.2.3