Check pattern types.

This commit is contained in:
Dawer 2021-05-10 13:22:13 +05:00
parent 9b841a9a04
commit 49e016169f
3 changed files with 84 additions and 29 deletions

View File

@ -379,32 +379,58 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) =
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();
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;
}
}
}
// Bail out early if lowering failed.
if have_errors {
// 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());

View File

@ -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<T> { Some(T), None }
fn main() {
// `Never` is deliberately not defined so that it's an uninferred type.
match Option::<Never>::None {
None => {}
Some(never) => {}
}
}
"#,
);
}
}
}

View File

@ -28,7 +28,7 @@ pub(crate) struct MatchCheckCtx<'a> {
pub(crate) body: Arc<Body>,
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<PatternArena>,
}