1
Fork 0

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

View file

@ -9,9 +9,11 @@ fn main() {
if let Some(ref mut y @ ref mut z) = x {} if let Some(ref mut y @ ref mut z) = x {}
//~^ ERROR: mutable more than once //~^ ERROR: mutable more than once
if let Some(ref mut y @ ref mut z) = x && true {} 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 {} while let Some(ref mut y @ ref mut z) = x {}
//~^ ERROR: mutable more than once //~^ ERROR: mutable more than once
while let Some(ref mut y @ ref mut z) = x && true {} while let Some(ref mut y @ ref mut z) = x && true {}
//~^ ERROR: mutable more than once
match x { match x {
ref mut y @ ref mut z => {} //~ ERROR: mutable more than once 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 | value is mutably borrowed by `y` here
error: cannot borrow value as mutable more than once at a time 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 {} LL | while let Some(ref mut y @ ref mut z) = x {}
| ^^^^^^^^^ --------- value is mutably borrowed by `z` here | ^^^^^^^^^ --------- 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 | value is mutably borrowed by `y` here
error: cannot borrow value as mutable more than once at a time 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 => {} LL | ref mut y @ ref mut z => {}
| ^^^^^^^^^ --------- value is mutably borrowed by `z` here | ^^^^^^^^^ --------- 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 | value is mutably borrowed by `y` here
error: cannot borrow value as mutable more than once at a time 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 => {} LL | () if let Some(ref mut y @ ref mut z) = x => {}
| ^^^^^^^^^ --------- value is mutably borrowed by `z` here | ^^^^^^^^^ --------- value is mutably borrowed by `z` here
| | | |
| value is mutably borrowed by `y` here | value is mutably borrowed by `y` here
error: aborting due to 6 previous errors error: aborting due to 8 previous errors