about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_middle/src/query/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs4
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs25
-rw-r--r--tests/ui/closures/2229_closure_analysis/bad-pattern.rs23
-rw-r--r--tests/ui/closures/2229_closure_analysis/bad-pattern.stderr113
5 files changed, 154 insertions, 13 deletions
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 6443c30e822..ded5c6aa3b3 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1022,7 +1022,7 @@ rustc_queries! {
         desc { "converting literal to mir constant" }
     }
 
-    query check_match(key: LocalDefId) {
+    query check_match(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> {
         desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { true }
     }
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 3f006765a71..20d381eddb1 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -42,7 +42,9 @@ fn mir_build(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> {
     // Ensure unsafeck and abstract const building is ran before we steal the THIR.
     tcx.ensure_with_value().thir_check_unsafety(def);
     tcx.ensure_with_value().thir_abstract_const(def);
-    tcx.ensure_with_value().check_match(def);
+    if let Err(e) = tcx.check_match(def) {
+        return construct_error(tcx, def, e);
+    }
 
     let body = match tcx.thir_body(def) {
         Err(error_reported) => construct_error(tcx, def, error_reported),
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 2b52d70af2a..c3517912cf9 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -26,8 +26,8 @@ use rustc_session::Session;
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::Span;
 
-pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) {
-    let Ok((thir, expr)) = tcx.thir_body(def_id) else { return };
+pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
+    let (thir, expr) = tcx.thir_body(def_id)?;
     let thir = thir.borrow();
     let pattern_arena = TypedArena::default();
     let mut visitor = MatchVisitor {
@@ -37,13 +37,16 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) {
         lint_level: tcx.hir().local_def_id_to_hir_id(def_id),
         let_source: LetSource::None,
         pattern_arena: &pattern_arena,
+        error: Ok(()),
     };
     visitor.visit_expr(&thir[expr]);
+
     for param in thir.params.iter() {
         if let Some(box ref pattern) = param.pat {
             visitor.check_irrefutable(pattern, "function argument", None);
         }
     }
+    visitor.error
 }
 
 fn create_e0004(
@@ -77,6 +80,7 @@ struct MatchVisitor<'a, 'p, 'tcx> {
     lint_level: HirId,
     let_source: LetSource,
     pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
+    error: Result<(), ErrorGuaranteed>,
 }
 
 impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
@@ -276,9 +280,9 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
                 let [pat_field] = &subpatterns[..] else { bug!() };
                 self.check_irrefutable(&pat_field.pattern, "`for` loop binding", None);
             } else {
-                non_exhaustive_match(
+                self.error = Err(non_exhaustive_match(
                     &cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span,
-                );
+                ));
             }
         }
     }
@@ -409,7 +413,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
     }
 
     #[instrument(level = "trace", skip(self))]
-    fn check_irrefutable(&self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
+    fn check_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
         let mut cx = self.new_cx(self.lint_level, false);
 
         let pattern = self.lower_pattern(&mut cx, pat);
@@ -478,7 +482,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
             AdtDefinedHere { adt_def_span, ty, variants }
         };
 
-        self.tcx.sess.emit_err(PatternNotCovered {
+        self.error = Err(self.tcx.sess.emit_err(PatternNotCovered {
             span: pat.span,
             origin,
             uncovered: Uncovered::new(pat.span, &cx, witnesses),
@@ -489,7 +493,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
             let_suggestion,
             misc_suggestion,
             adt_defined_here,
-        });
+        }));
     }
 }
 
@@ -631,7 +635,7 @@ fn non_exhaustive_match<'p, 'tcx>(
     witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
     arms: &[ArmId],
     expr_span: Span,
-) {
+) -> ErrorGuaranteed {
     let is_empty_match = arms.is_empty();
     let non_empty_enum = match scrut_ty.kind() {
         ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(),
@@ -643,13 +647,12 @@ fn non_exhaustive_match<'p, 'tcx>(
     let pattern;
     let patterns_len;
     if is_empty_match && !non_empty_enum {
-        cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
+        return cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
             cx,
             expr_span,
             span: sp,
             ty: scrut_ty,
         });
-        return;
     } else {
         // FIXME: migration of this diagnostic will require list support
         let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
@@ -800,7 +803,7 @@ fn non_exhaustive_match<'p, 'tcx>(
     } else {
         err.help(&msg);
     }
-    err.emit();
+    err.emit()
 }
 
 pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
diff --git a/tests/ui/closures/2229_closure_analysis/bad-pattern.rs b/tests/ui/closures/2229_closure_analysis/bad-pattern.rs
new file mode 100644
index 00000000000..a7bf9b67d45
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/bad-pattern.rs
@@ -0,0 +1,23 @@
+// regression test for #108683
+// edition:2021
+
+enum Refutable {
+    A,
+    B,
+}
+
+fn example(v1: u32, v2: [u32; 4], v3: Refutable) {
+    const PAT: u32 = 0;
+    let v4 = &v2[..];
+    || {
+        let 0 = v1; //~ ERROR refutable pattern in local binding
+        let (0 | 1) = v1; //~ ERROR refutable pattern in local binding
+        let 1.. = v1; //~ ERROR refutable pattern in local binding
+        let [0, 0, 0, 0] = v2; //~ ERROR refutable pattern in local binding
+        let [0] = v4; //~ ERROR refutable pattern in local binding
+        let Refutable::A = v3; //~ ERROR refutable pattern in local binding
+        let PAT = v1; //~ ERROR refutable pattern in local binding
+    };
+}
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/bad-pattern.stderr b/tests/ui/closures/2229_closure_analysis/bad-pattern.stderr
new file mode 100644
index 00000000000..ca8c2a16d32
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/bad-pattern.stderr
@@ -0,0 +1,113 @@
+error[E0005]: refutable pattern in local binding
+  --> $DIR/bad-pattern.rs:13:13
+   |
+LL |         let 0 = v1;
+   |             ^ pattern `1_u32..=u32::MAX` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `u32`
+help: you might want to use `if let` to ignore the variant that isn't matched
+   |
+LL |         if let 0 = v1 { todo!() };
+   |         ++            +++++++++++
+help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
+   |
+LL |         let _0 = v1;
+   |             +
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/bad-pattern.rs:14:14
+   |
+LL |         let (0 | 1) = v1;
+   |              ^^^^^ pattern `2_u32..=u32::MAX` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `u32`
+help: you might want to use `if let` to ignore the variant that isn't matched
+   |
+LL |         if let (0 | 1) = v1 { todo!() };
+   |         ++                  +++++++++++
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/bad-pattern.rs:15:13
+   |
+LL |         let 1.. = v1;
+   |             ^^^ pattern `0_u32` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `u32`
+help: you might want to use `if let` to ignore the variant that isn't matched
+   |
+LL |         if let 1.. = v1 { todo!() };
+   |         ++              +++++++++++
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/bad-pattern.rs:16:13
+   |
+LL |         let [0, 0, 0, 0] = v2;
+   |             ^^^^^^^^^^^^ pattern `[1_u32..=u32::MAX, _, _, _]` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `[u32; 4]`
+help: you might want to use `if let` to ignore the variant that isn't matched
+   |
+LL |         if let [0, 0, 0, 0] = v2 { todo!() };
+   |         ++                       +++++++++++
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/bad-pattern.rs:17:13
+   |
+LL |         let [0] = v4;
+   |             ^^^ patterns `&[]` and `&[_, _, ..]` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `&[u32]`
+help: you might want to use `if let` to ignore the variants that aren't matched
+   |
+LL |         if let [0] = v4 { todo!() };
+   |         ++              +++++++++++
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/bad-pattern.rs:18:13
+   |
+LL |         let Refutable::A = v3;
+   |             ^^^^^^^^^^^^ pattern `Refutable::B` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+note: `Refutable` defined here
+  --> $DIR/bad-pattern.rs:4:6
+   |
+LL | enum Refutable {
+   |      ^^^^^^^^^
+LL |     A,
+LL |     B,
+   |     - not covered
+   = note: the matched value is of type `Refutable`
+help: you might want to use `if let` to ignore the variant that isn't matched
+   |
+LL |         if let Refutable::A = v3 { todo!() };
+   |         ++                       +++++++++++
+
+error[E0005]: refutable pattern in local binding
+  --> $DIR/bad-pattern.rs:19:13
+   |
+LL |         let PAT = v1;
+   |             ^^^
+   |             |
+   |             pattern `1_u32..=u32::MAX` not covered
+   |             missing patterns are not covered because `PAT` is interpreted as a constant pattern, not a new variable
+   |             help: introduce a variable instead: `PAT_var`
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `u32`
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0005`.