Always do all the pattern checks
This commit is contained in:
parent
b60f08a66d
commit
c19856929d
3 changed files with 82 additions and 64 deletions
|
@ -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 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 {
|
if got_error {
|
||||||
LintLevel::Explicit(hir_id) => hir_id,
|
return;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,43 +661,41 @@ 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,
|
mutability: Mutability::Not,
|
||||||
mutability: Mutability::Not,
|
subpattern: None,
|
||||||
subpattern: None,
|
ty,
|
||||||
ty,
|
..
|
||||||
..
|
} = pat.kind
|
||||||
} = p.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
|
.variants()
|
||||||
.variants()
|
.iter()
|
||||||
.iter()
|
.any(|variant| variant.name == name && variant.ctor_kind() == Some(CtorKind::Const))
|
||||||
.any(|variant| variant.name == name && variant.ctor_kind() == Some(CtorKind::Const))
|
{
|
||||||
{
|
let variant_count = edef.variants().len();
|
||||||
let variant_count = edef.variants().len();
|
let ty_path = with_no_trimmed_paths!(cx.tcx.def_path_str(edef.did()));
|
||||||
let ty_path = with_no_trimmed_paths!(cx.tcx.def_path_str(edef.did()));
|
cx.tcx.emit_spanned_lint(
|
||||||
cx.tcx.emit_spanned_lint(
|
BINDINGS_WITH_VARIANT_NAME,
|
||||||
BINDINGS_WITH_VARIANT_NAME,
|
cx.lint_level,
|
||||||
cx.lint_level,
|
pat.span,
|
||||||
p.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(pat.span)
|
||||||
Some(p.span)
|
} else {
|
||||||
} else {
|
None
|
||||||
None
|
|
||||||
},
|
|
||||||
ty_path,
|
|
||||||
name,
|
|
||||||
},
|
},
|
||||||
)
|
ty_path,
|
||||||
}
|
name,
|
||||||
});
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn irrefutable_let_patterns(
|
fn irrefutable_let_patterns(
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue