Generally optimize diagnostics performance

This commit is contained in:
Lukas Wirth 2024-04-15 20:38:54 +02:00
parent 94e38261b3
commit 531a270d91
15 changed files with 115 additions and 85 deletions

View file

@ -191,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;
}
@ -534,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))
}
}
}