Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/diagnostics/expr.rs')
| -rw-r--r-- | crates/hir-ty/src/diagnostics/expr.rs | 93 |
1 files changed, 52 insertions, 41 deletions
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index 20b0da441d..a5a42c52af 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs @@ -11,6 +11,7 @@ use hir_def::{ItemContainerId, Lookup}; use hir_expand::name; use itertools::Itertools; use rustc_hash::FxHashSet; +use rustc_pattern_analysis::constructor::Constructor; use syntax::{ast, AstNode}; use tracing::debug; use triomphe::Arc; @@ -190,45 +191,45 @@ impl ExprValidator { let pattern_arena = Arena::new(); let mut m_arms = Vec::with_capacity(arms.len()); let mut has_lowering_errors = false; + // Note: Skipping the entire diagnostic rather than just not including a faulty match arm is + // preferred to avoid the chance of false positives. for arm in arms { - if let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) { - // We only include patterns whose type matches the type - // of the scrutinee expression. If we had an 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 scrutinee expression type if - // necessary. - // - // FIXME we should use the type checker for this. - if (pat_ty == scrut_ty - || scrut_ty - .as_reference() - .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty) - .unwrap_or(false)) - && types_of_subpatterns_do_match(arm.pat, &self.body, &self.infer) - { - // If we had a NotUsefulMatchArm diagnostic, we could - // check the usefulness of each pattern as we added it - // to the matrix here. - let pat = self.lower_pattern(&cx, arm.pat, db, &mut has_lowering_errors); - let m_arm = pat_analysis::MatchArm { - pat: pattern_arena.alloc(pat), - has_guard: arm.guard.is_some(), - arm_data: (), - }; - m_arms.push(m_arm); - if !has_lowering_errors { - continue; - } + let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) else { + return; + }; + + // We only include patterns whose type matches the type + // of the scrutinee expression. If we had an 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 scrutinee expression type if + // necessary. + // + // FIXME we should use the type checker for this. + if (pat_ty == scrut_ty + || scrut_ty + .as_reference() + .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty) + .unwrap_or(false)) + && types_of_subpatterns_do_match(arm.pat, &self.body, &self.infer) + { + // If we had a NotUsefulMatchArm diagnostic, we could + // check the usefulness of each pattern as we added it + // to the matrix here. + let pat = self.lower_pattern(&cx, arm.pat, db, &mut has_lowering_errors); + let m_arm = pat_analysis::MatchArm { + pat: pattern_arena.alloc(pat), + has_guard: arm.guard.is_some(), + arm_data: (), + }; + 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. + // If the pattern type doesn't fit the match expression, we skip this diagnostic. cov_mark::hit!(validate_match_bailed_out); return; } @@ -266,15 +267,17 @@ impl ExprValidator { let mut have_errors = false; let deconstructed_pat = self.lower_pattern(&cx, pat, db, &mut have_errors); + + // optimization, wildcard trivially hold + if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) { + continue; + } + let match_arm = rustc_pattern_analysis::MatchArm { pat: pattern_arena.alloc(deconstructed_pat), has_guard: false, arm_data: (), }; - if have_errors { - continue; - } - let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) { Ok(v) => v, Err(e) => { @@ -531,8 +534,16 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul fn walk(pat: PatId, body: &Body, infer: &InferenceResult, has_type_mismatches: &mut bool) { match infer.type_mismatch_for_pat(pat) { Some(_) => *has_type_mismatches = true, + None if *has_type_mismatches => (), None => { - body[pat].walk_child_pats(|subpat| walk(subpat, body, infer, has_type_mismatches)) + let pat = &body[pat]; + if let Pat::ConstBlock(expr) | Pat::Lit(expr) = *pat { + *has_type_mismatches |= infer.type_mismatch_for_expr(expr).is_some(); + if *has_type_mismatches { + return; + } + } + pat.walk_child_pats(|subpat| walk(subpat, body, infer, has_type_mismatches)) } } } |