Replace the old match checking algorithm

This commit is contained in:
Dawer 2021-05-11 17:18:16 +05:00
parent 894b4c64ff
commit e84efc4a46
7 changed files with 355 additions and 1971 deletions

View File

@ -1,8 +1,6 @@
//! Type inference-based diagnostics. //! Type inference-based diagnostics.
mod expr; mod expr;
#[allow(unused)] //todo
mod match_check; mod match_check;
mod pattern;
mod unsafe_check; mod unsafe_check;
mod decl_check; mod decl_check;

View File

@ -4,7 +4,9 @@
use std::{cell::RefCell, sync::Arc}; use std::{cell::RefCell, sync::Arc};
use hir_def::{expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId}; use hir_def::{
expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule,
};
use hir_expand::name; use hir_expand::name;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use syntax::{ast, AstPtr}; use syntax::{ast, AstPtr};
@ -12,7 +14,10 @@ use syntax::{ast, AstPtr};
use crate::{ use crate::{
db::HirDatabase, db::HirDatabase,
diagnostics::{ diagnostics::{
match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, match_check::{
self,
usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena},
},
MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
MissingPatFields, RemoveThisSemicolon, MissingPatFields, RemoveThisSemicolon,
}, },
@ -26,13 +31,7 @@ pub(crate) use hir_def::{
LocalFieldId, VariantId, LocalFieldId, VariantId,
}; };
use super::{ use super::ReplaceFilterMapNextWithFindMap;
pattern::{
self,
usefulness::{expand_pattern, PatternArena},
},
ReplaceFilterMapNextWithFindMap,
};
pub(super) struct ExprValidator<'a, 'b: 'a> { pub(super) struct ExprValidator<'a, 'b: 'a> {
owner: DefWithBodyId, owner: DefWithBodyId,
@ -68,7 +67,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
match expr { match expr {
Expr::Match { expr, arms } => { Expr::Match { expr, arms } => {
self.validate_match2(id, *expr, arms, db, self.infer.clone()); self.validate_match(id, *expr, arms, db, self.infer.clone());
} }
Expr::Call { .. } | Expr::MethodCall { .. } => { Expr::Call { .. } | Expr::MethodCall { .. } => {
self.validate_call(db, id, expr); self.validate_call(db, id, expr);
@ -283,7 +282,6 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
} }
} }
#[allow(dead_code)]
fn validate_match( fn validate_match(
&mut self, &mut self,
id: ExprId, id: ExprId,
@ -301,90 +299,6 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
&infer.type_of_expr[match_expr] &infer.type_of_expr[match_expr]
}; };
let cx = MatchCheckCtx { match_expr, body, infer: infer.clone(), db };
let pats = arms.iter().map(|arm| arm.pat);
let mut seen = Matrix::empty();
for pat in pats {
if let Some(pat_ty) = infer.type_of_pat.get(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))
&& types_of_subpatterns_do_match(pat, &cx.body, &infer)
{
// If we had a NotUsefulMatchArm diagnostic, we could
// check the usefulness of each pattern as we added it
// to the matrix here.
let v = PatStack::from_pattern(pat);
seen.push(&cx, v);
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;
}
match is_useful(&cx, &seen, &PatStack::from_wild()) {
Ok(Usefulness::Useful) => (),
// if a wildcard pattern is not useful, then all patterns are covered
Ok(Usefulness::NotUseful) => return,
// this path is for unimplemented checks, so we err on the side of not
// reporting any errors
_ => return,
}
if let Ok(source_ptr) = source_map.expr_syntax(id) {
let root = source_ptr.file_syntax(db.upcast());
if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) {
if let (Some(match_expr), Some(arms)) =
(match_expr.expr(), match_expr.match_arm_list())
{
self.sink.push(MissingMatchArms {
file: source_ptr.file_id,
match_expr: AstPtr::new(&match_expr),
arms: AstPtr::new(&arms),
})
}
}
}
}
fn validate_match2(
&mut self,
id: ExprId,
match_expr: ExprId,
arms: &[MatchArm],
db: &dyn HirDatabase,
infer: Arc<InferenceResult>,
) {
use crate::diagnostics::pattern::usefulness;
use hir_def::HasModule;
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() {
return;
} else {
&infer.type_of_expr[match_expr]
};
let pattern_arena = RefCell::new(PatternArena::new()); let pattern_arena = RefCell::new(PatternArena::new());
let mut m_arms = Vec::new(); let mut m_arms = Vec::new();
@ -401,16 +315,17 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
// necessary. // necessary.
// //
// FIXME we should use the type checker for this. // FIXME we should use the type checker for this.
if pat_ty == match_expr_ty if (pat_ty == match_expr_ty
|| match_expr_ty || match_expr_ty
.as_reference() .as_reference()
.map(|(match_expr_ty, ..)| match_expr_ty == pat_ty) .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty)
.unwrap_or(false) .unwrap_or(false))
&& types_of_subpatterns_do_match(arm.pat, &body, &infer)
{ {
// If we had a NotUsefulMatchArm diagnostic, we could // If we had a NotUsefulMatchArm diagnostic, we could
// check the usefulness of each pattern as we added it // check the usefulness of each pattern as we added it
// to the matrix here. // to the matrix here.
let m_arm = usefulness::MatchArm { let m_arm = match_check::MatchArm {
pat: self.lower_pattern( pat: self.lower_pattern(
arm.pat, arm.pat,
&mut pattern_arena.borrow_mut(), &mut pattern_arena.borrow_mut(),
@ -434,14 +349,14 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
return; return;
} }
let cx = usefulness::MatchCheckCtx { let cx = MatchCheckCtx {
module: self.owner.module(db.upcast()), module: self.owner.module(db.upcast()),
match_expr, match_expr,
infer: &infer, infer: &infer,
db, db,
pattern_arena: &pattern_arena, pattern_arena: &pattern_arena,
}; };
let report = usefulness::compute_match_usefulness(&cx, &m_arms); let report = compute_match_usefulness(&cx, &m_arms);
// FIXME Report unreacheble arms // FIXME Report unreacheble arms
// https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200-L201 // https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200-L201
@ -473,8 +388,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
db: &dyn HirDatabase, db: &dyn HirDatabase,
body: &Body, body: &Body,
have_errors: &mut bool, have_errors: &mut bool,
) -> pattern::PatId { ) -> match_check::PatId {
let mut patcx = pattern::PatCtxt::new(db, &self.infer, body); let mut patcx = match_check::PatCtxt::new(db, &self.infer, body);
let pattern = patcx.lower_pattern(pat); let pattern = patcx.lower_pattern(pat);
let pattern = pattern_arena.alloc(expand_pattern(pattern)); let pattern = pattern_arena.alloc(expand_pattern(pattern));
if !patcx.errors.is_empty() { if !patcx.errors.is_empty() {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff