Always do all the pattern checks

This commit is contained in:
Nadrieril 2023-10-29 05:15:36 +01:00
parent b60f08a66d
commit c19856929d
3 changed files with 82 additions and 64 deletions

View File

@ -58,7 +58,7 @@ fn create_e0004(
struct_span_err!(sess, sp, E0004, "{}", &error_message)
}
#[derive(PartialEq)]
#[derive(Copy, Clone, PartialEq)]
enum RefutableFlag {
Irrefutable,
Refutable,
@ -197,32 +197,36 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
self.let_source = old_let_source;
}
fn with_lint_level(&mut self, new_lint_level: LintLevel, f: impl FnOnce(&mut Self)) {
fn with_lint_level<T>(
&mut self,
new_lint_level: LintLevel,
f: impl FnOnce(&mut Self) -> T,
) -> T {
if let LintLevel::Explicit(hir_id) = new_lint_level {
let old_lint_level = self.lint_level;
self.lint_level = hir_id;
f(self);
let ret = f(self);
self.lint_level = old_lint_level;
ret
} else {
f(self);
f(self)
}
}
fn check_patterns(&self, pat: &Pat<'tcx>, rf: RefutableFlag) {
pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
check_for_bindings_named_same_as_variants(self, pat, rf);
}
fn lower_pattern(
&mut self,
cx: &MatchCheckCtxt<'p, 'tcx>,
pattern: &Pat<'tcx>,
pat: &Pat<'tcx>,
) -> Result<&'p DeconstructedPat<'p, 'tcx>, ErrorGuaranteed> {
if let Err(err) = pattern.pat_error_reported() {
if let Err(err) = pat.pat_error_reported() {
self.error = Err(err);
Err(err)
} else {
Ok(cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, pattern)))
// Check the pattern for some things unrelated to exhaustiveness.
let refutable = if cx.refutable { Refutable } else { Irrefutable };
pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
pat.walk_always(|pat| check_for_bindings_named_same_as_variants(self, pat, refutable));
Ok(cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, pat)))
}
}
@ -241,7 +245,6 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
if let LetSource::None = source {
return;
}
self.check_patterns(pat, Refutable);
let mut cx = self.new_cx(self.lint_level, true);
let Ok(tpat) = self.lower_pattern(&cx, pat) else { return };
self.check_let_reachability(&mut cx, self.lint_level, source, tpat, span);
@ -258,18 +261,18 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
let mut tarms = Vec::with_capacity(arms.len());
for &arm in arms {
// Check the arm for some things unrelated to exhaustiveness.
let arm = &self.thir.arms[arm];
self.with_lint_level(arm.lint_level, |this| {
this.check_patterns(&arm.pattern, Refutable);
let got_error = self.with_lint_level(arm.lint_level, |this| {
let Ok(pat) = this.lower_pattern(&mut cx, &arm.pattern) else {
return true;
};
let arm = MatchArm { pat, hir_id: this.lint_level, has_guard: arm.guard.is_some() };
tarms.push(arm);
false
});
let hir_id = match arm.lint_level {
LintLevel::Explicit(hir_id) => hir_id,
LintLevel::Inherited => self.lint_level,
};
let Ok(pat) = self.lower_pattern(&mut cx, &arm.pattern) else { return };
let arm = MatchArm { pat, hir_id, has_guard: arm.guard.is_some() };
tarms.push(arm);
if got_error {
return;
}
}
let scrut = &self.thir[scrut];
@ -458,7 +461,6 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
let witnesses = report.non_exhaustiveness_witnesses;
if witnesses.is_empty() {
// The pattern is irrefutable.
self.check_patterns(pat, Irrefutable);
return;
}
@ -659,43 +661,41 @@ fn check_for_bindings_named_same_as_variants(
pat: &Pat<'_>,
rf: RefutableFlag,
) {
pat.walk_always(|p| {
if let PatKind::Binding {
name,
mode: BindingMode::ByValue,
mutability: Mutability::Not,
subpattern: None,
ty,
..
} = p.kind
&& let ty::Adt(edef, _) = ty.peel_refs().kind()
&& edef.is_enum()
&& edef
.variants()
.iter()
.any(|variant| variant.name == name && variant.ctor_kind() == Some(CtorKind::Const))
{
let variant_count = edef.variants().len();
let ty_path = with_no_trimmed_paths!(cx.tcx.def_path_str(edef.did()));
cx.tcx.emit_spanned_lint(
BINDINGS_WITH_VARIANT_NAME,
cx.lint_level,
p.span,
BindingsWithVariantName {
// If this is an irrefutable pattern, and there's > 1 variant,
// then we can't actually match on this. Applying the below
// suggestion would produce code that breaks on `check_irrefutable`.
suggestion: if rf == Refutable || variant_count == 1 {
Some(p.span)
} else {
None
},
ty_path,
name,
if let PatKind::Binding {
name,
mode: BindingMode::ByValue,
mutability: Mutability::Not,
subpattern: None,
ty,
..
} = pat.kind
&& let ty::Adt(edef, _) = ty.peel_refs().kind()
&& edef.is_enum()
&& edef
.variants()
.iter()
.any(|variant| variant.name == name && variant.ctor_kind() == Some(CtorKind::Const))
{
let variant_count = edef.variants().len();
let ty_path = with_no_trimmed_paths!(cx.tcx.def_path_str(edef.did()));
cx.tcx.emit_spanned_lint(
BINDINGS_WITH_VARIANT_NAME,
cx.lint_level,
pat.span,
BindingsWithVariantName {
// If this is an irrefutable pattern, and there's > 1 variant,
// then we can't actually match on this. Applying the below
// suggestion would produce code that breaks on `check_irrefutable`.
suggestion: if rf == Refutable || variant_count == 1 {
Some(pat.span)
} else {
None
},
)
}
});
ty_path,
name,
},
)
}
}
fn irrefutable_let_patterns(

View File

@ -9,9 +9,11 @@ fn main() {
if let Some(ref mut y @ ref mut z) = x {}
//~^ ERROR: mutable more than once
if let Some(ref mut y @ ref mut z) = x && true {}
//~^ ERROR: mutable more than once
while let Some(ref mut y @ ref mut z) = x {}
//~^ ERROR: mutable more than once
while let Some(ref mut y @ ref mut z) = x && true {}
//~^ ERROR: mutable more than once
match x {
ref mut y @ ref mut z => {} //~ ERROR: mutable more than once
}

View File

@ -23,7 +23,15 @@ LL | if let Some(ref mut y @ ref mut z) = x {}
| value is mutably borrowed by `y` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/conflicting_bindings.rs:12:20
--> $DIR/conflicting_bindings.rs:11:17
|
LL | if let Some(ref mut y @ ref mut z) = x && true {}
| ^^^^^^^^^ --------- value is mutably borrowed by `z` here
| |
| value is mutably borrowed by `y` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/conflicting_bindings.rs:13:20
|
LL | while let Some(ref mut y @ ref mut z) = x {}
| ^^^^^^^^^ --------- value is mutably borrowed by `z` here
@ -31,7 +39,15 @@ LL | while let Some(ref mut y @ ref mut z) = x {}
| value is mutably borrowed by `y` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/conflicting_bindings.rs:16:9
--> $DIR/conflicting_bindings.rs:15:20
|
LL | while let Some(ref mut y @ ref mut z) = x && true {}
| ^^^^^^^^^ --------- value is mutably borrowed by `z` here
| |
| value is mutably borrowed by `y` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/conflicting_bindings.rs:18:9
|
LL | ref mut y @ ref mut z => {}
| ^^^^^^^^^ --------- value is mutably borrowed by `z` here
@ -39,12 +55,12 @@ LL | ref mut y @ ref mut z => {}
| value is mutably borrowed by `y` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/conflicting_bindings.rs:19:24
--> $DIR/conflicting_bindings.rs:21:24
|
LL | () if let Some(ref mut y @ ref mut z) = x => {}
| ^^^^^^^^^ --------- value is mutably borrowed by `z` here
| |
| value is mutably borrowed by `y` here
error: aborting due to 6 previous errors
error: aborting due to 8 previous errors