mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 13:51:31 +00:00
handle non matching enum pattern types
This commit is contained in:
parent
b87b7a088f
commit
43dfd89493
2 changed files with 57 additions and 41 deletions
|
@ -250,17 +250,13 @@ pub enum Usefulness {
|
||||||
|
|
||||||
pub struct MatchCheckCtx<'a> {
|
pub struct MatchCheckCtx<'a> {
|
||||||
pub body: Arc<Body>,
|
pub body: Arc<Body>,
|
||||||
pub match_expr: &'a Expr,
|
|
||||||
pub infer: Arc<InferenceResult>,
|
pub infer: Arc<InferenceResult>,
|
||||||
pub db: &'a dyn HirDatabase,
|
pub db: &'a dyn HirDatabase,
|
||||||
}
|
}
|
||||||
|
|
||||||
// see src/librustc_mir_build/hair/pattern/_match.rs
|
/// Given a set of patterns `matrix`, and pattern to consider `v`, determines
|
||||||
// It seems the rustc version of this method is able to assume that all the match arm
|
/// whether `v` is useful. A pattern is useful if it covers cases which were
|
||||||
// patterns are valid (they are valid given a particular match expression), but I
|
/// not previously covered.
|
||||||
// don't think we can make that assumption here. How should that be handled?
|
|
||||||
//
|
|
||||||
// Perhaps check that validity before passing the patterns into this method?
|
|
||||||
pub(crate) fn is_useful(
|
pub(crate) fn is_useful(
|
||||||
cx: &MatchCheckCtx,
|
cx: &MatchCheckCtx,
|
||||||
matrix: &Matrix,
|
matrix: &Matrix,
|
||||||
|
@ -517,6 +513,19 @@ mod tests {
|
||||||
check_diagnostic_with_no_fix(content);
|
check_diagnostic_with_no_fix(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_tuple_wild() {
|
||||||
|
let content = r"
|
||||||
|
fn test_fn() {
|
||||||
|
match () {
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
check_no_diagnostic(content);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_tuple_no_diagnostic() {
|
fn empty_tuple_no_diagnostic() {
|
||||||
let content = r"
|
let content = r"
|
||||||
|
@ -976,21 +985,6 @@ mod tests {
|
||||||
|
|
||||||
check_no_diagnostic(content);
|
check_no_diagnostic(content);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod false_negatives {
|
|
||||||
//! The implementation of match checking here is a work in progress. As we roll this out, we
|
|
||||||
//! prefer false negatives to false positives (ideally there would be no false positives). This
|
|
||||||
//! test module should document known false negatives. Eventually we will have a complete
|
|
||||||
//! implementation of match checking and this module will be empty.
|
|
||||||
//!
|
|
||||||
//! The reasons for documenting known false negatives:
|
|
||||||
//!
|
|
||||||
//! 1. It acts as a backlog of work that can be done to improve the behavior of the system.
|
|
||||||
//! 2. It ensures the code doesn't panic when handling these cases.
|
|
||||||
|
|
||||||
use super::tests::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mismatched_types() {
|
fn mismatched_types() {
|
||||||
|
@ -1011,10 +1005,8 @@ mod false_negatives {
|
||||||
}
|
}
|
||||||
";
|
";
|
||||||
|
|
||||||
// This is a false negative.
|
// Match arms with the incorrect type are filtered out.
|
||||||
// We don't currently check that the match arms actually
|
check_diagnostic_with_no_fix(content);
|
||||||
// match the type of the match expression.
|
|
||||||
check_no_diagnostic(content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1028,14 +1020,24 @@ mod false_negatives {
|
||||||
}
|
}
|
||||||
";
|
";
|
||||||
|
|
||||||
// This is a false negative.
|
// Match arms with the incorrect type are filtered out.
|
||||||
// We don't currently check that the match arms actually
|
check_diagnostic_with_no_fix(content);
|
||||||
// match the type of the match expression. This test
|
|
||||||
// checks to ensure we don't panic when the code we are
|
|
||||||
// checking is malformed in such a way that the arity of the
|
|
||||||
// constructors doesn't match.
|
|
||||||
check_no_diagnostic(content);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod false_negatives {
|
||||||
|
//! The implementation of match checking here is a work in progress. As we roll this out, we
|
||||||
|
//! prefer false negatives to false positives (ideally there would be no false positives). This
|
||||||
|
//! test module should document known false negatives. Eventually we will have a complete
|
||||||
|
//! implementation of match checking and this module will be empty.
|
||||||
|
//!
|
||||||
|
//! The reasons for documenting known false negatives:
|
||||||
|
//!
|
||||||
|
//! 1. It acts as a backlog of work that can be done to improve the behavior of the system.
|
||||||
|
//! 2. It ensures the code doesn't panic when handling these cases.
|
||||||
|
|
||||||
|
use super::tests::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn integers() {
|
fn integers() {
|
||||||
|
|
|
@ -67,7 +67,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
|
||||||
fn validate_match(
|
fn validate_match(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: ExprId,
|
id: ExprId,
|
||||||
expr: ExprId,
|
match_expr: ExprId,
|
||||||
arms: &[MatchArm],
|
arms: &[MatchArm],
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
infer: Arc<InferenceResult>,
|
infer: Arc<InferenceResult>,
|
||||||
|
@ -75,19 +75,33 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
|
||||||
let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) =
|
let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) =
|
||||||
db.body_with_source_map(self.func.into());
|
db.body_with_source_map(self.func.into());
|
||||||
|
|
||||||
let match_expr: &hir_def::expr::Expr = &body[expr];
|
let match_expr_ty = match infer.type_of_expr.get(match_expr) {
|
||||||
|
Some(ty) => ty,
|
||||||
|
// If we can't resolve the type of the match expression
|
||||||
|
// we cannot perform exhaustiveness checks.
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
let cx = MatchCheckCtx { body: body.clone(), match_expr, infer, db };
|
let cx = MatchCheckCtx { body, infer: infer.clone(), db };
|
||||||
let pats = arms.iter().map(|arm| arm.pat);
|
let pats = arms.iter().map(|arm| arm.pat);
|
||||||
|
|
||||||
let mut seen = Matrix::empty();
|
let mut seen = Matrix::empty();
|
||||||
for pat in pats {
|
for pat in pats {
|
||||||
|
// We skip any patterns whose type we cannot resolve.
|
||||||
|
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.
|
||||||
|
if pat_ty == match_expr_ty {
|
||||||
// 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 v = PatStack::from_pattern(pat);
|
let v = PatStack::from_pattern(pat);
|
||||||
seen.push(&cx, v);
|
seen.push(&cx, v);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match is_useful(&cx, &seen, &PatStack::from_wild()) {
|
match is_useful(&cx, &seen, &PatStack::from_wild()) {
|
||||||
Ok(Usefulness::Useful) => (),
|
Ok(Usefulness::Useful) => (),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue