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