about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSebastian Toh <sebas.tsj.98@gmail.com>2023-08-28 14:25:52 +0800
committerSebastian Toh <sebas.tsj.98@gmail.com>2023-08-28 14:50:32 +0800
commit43dd8613a308745a9f75690bb5365f8b8ca05926 (patch)
treeb17c962e071d3bef0d2aae2188f6c4a5f465a459
parenta293619caad49bb0e8d9a4d417bedf50f31d1b06 (diff)
downloadrust-43dd8613a308745a9f75690bb5365f8b8ca05926.tar.gz
rust-43dd8613a308745a9f75690bb5365f8b8ca05926.zip
Add note when matching on nested non-exhaustive enums
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs11
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/usefulness.rs49
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr3
-rw-r--r--tests/ui/match/match_non_exhaustive.stderr3
-rw-r--r--tests/ui/pattern/usefulness/auxiliary/non-exhaustive.rs2
-rw-r--r--tests/ui/pattern/usefulness/nested-non-exhaustive-enums.rs18
-rw-r--r--tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr22
-rw-r--r--tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr5
8 files changed, 78 insertions, 35 deletions
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 cce2a8c5259..38c24e0ab5e 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -720,15 +720,8 @@ fn non_exhaustive_match<'p, 'tcx>(
         };
     };
 
-    let is_variant_list_non_exhaustive = matches!(scrut_ty.kind(),
-        ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local());
-
     adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
-    err.note(format!(
-        "the matched value is of type `{}`{}",
-        scrut_ty,
-        if is_variant_list_non_exhaustive { ", which is marked as non-exhaustive" } else { "" }
-    ));
+    err.note(format!("the matched value is of type `{}`", scrut_ty));
 
     if !is_empty_match && witnesses.len() == 1 {
         let mut non_exhaustive_tys = FxHashSet::default();
@@ -750,6 +743,8 @@ fn non_exhaustive_match<'p, 'tcx>(
                 err.note(format!(
                     "`{ty}` cannot be matched exhaustively, so a wildcard `_` is necessary",
                 ));
+            } else if cx.is_foreign_non_exhaustive_enum(ty) {
+                err.note(format!("`{ty}` is marked as non-exhaustive"));
             }
         }
     }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
index 08cfe98bb68..21031e8ba9d 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
@@ -618,10 +618,15 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
                 let new_witnesses = if let Constructor::Missing { .. } = ctor {
                     // We got the special `Missing` constructor, so each of the missing constructors
                     // gives a new pattern that is not caught by the match. We list those patterns.
-                    let new_patterns = if pcx.is_non_exhaustive {
-                        // Here we don't want the user to try to list all variants, we want them to add
-                        // a wildcard, so we only suggest that.
-                        vec![DeconstructedPat::wildcard(pcx.ty, pcx.span)]
+                    if pcx.is_non_exhaustive {
+                        witnesses
+                            .into_iter()
+                            // Here we don't want the user to try to list all variants, we want them to add
+                            // a wildcard, so we only suggest that.
+                            .map(|witness| {
+                                witness.apply_constructor(pcx, &Constructor::NonExhaustive)
+                            })
+                            .collect()
                     } else {
                         let mut split_wildcard = SplitWildcard::new(pcx);
                         split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
@@ -633,7 +638,7 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
                         // constructor, that matches everything that can be built with
                         // it. For example, if `ctor` is a `Constructor::Variant` for
                         // `Option::Some`, we get the pattern `Some(_)`.
-                        let mut new: Vec<DeconstructedPat<'_, '_>> = split_wildcard
+                        let mut new_patterns: Vec<DeconstructedPat<'_, '_>> = split_wildcard
                             .iter_missing(pcx)
                             .filter_map(|missing_ctor| {
                                 // Check if this variant is marked `doc(hidden)`
@@ -648,27 +653,25 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
                             .collect();
 
                         if hide_variant_show_wild {
-                            new.push(DeconstructedPat::wildcard(pcx.ty, pcx.span));
+                            new_patterns.push(DeconstructedPat::wildcard(pcx.ty, pcx.span));
                         }
 
-                        new
-                    };
-
-                    witnesses
-                        .into_iter()
-                        .flat_map(|witness| {
-                            new_patterns.iter().map(move |pat| {
-                                Witness(
-                                    witness
-                                        .0
-                                        .iter()
-                                        .chain(once(pat))
-                                        .map(DeconstructedPat::clone_and_forget_reachability)
-                                        .collect(),
-                                )
+                        witnesses
+                            .into_iter()
+                            .flat_map(|witness| {
+                                new_patterns.iter().map(move |pat| {
+                                    Witness(
+                                        witness
+                                            .0
+                                            .iter()
+                                            .chain(once(pat))
+                                            .map(DeconstructedPat::clone_and_forget_reachability)
+                                            .collect(),
+                                    )
+                                })
                             })
-                        })
-                        .collect()
+                            .collect()
+                    }
                 } else {
                     witnesses
                         .into_iter()
diff --git a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr
index 3a5fad15421..fddd769c3df 100644
--- a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr
+++ b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr
@@ -45,7 +45,8 @@ note: `E2` defined here
    |
 LL | pub enum E2 { A, B }
    | ^^^^^^^^^^^
-   = note: the matched value is of type `E2`, which is marked as non-exhaustive
+   = note: the matched value is of type `E2`
+   = note: `E2` is marked as non-exhaustive
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
 LL |     let _e = || { match e2 { E2::A => (), E2::B => (), _ => todo!() } };
diff --git a/tests/ui/match/match_non_exhaustive.stderr b/tests/ui/match/match_non_exhaustive.stderr
index 46ee8d5179e..d07e284e299 100644
--- a/tests/ui/match/match_non_exhaustive.stderr
+++ b/tests/ui/match/match_non_exhaustive.stderr
@@ -45,7 +45,8 @@ note: `E2` defined here
    |
 LL | pub enum E2 { A, B }
    | ^^^^^^^^^^^
-   = note: the matched value is of type `E2`, which is marked as non-exhaustive
+   = note: the matched value is of type `E2`
+   = note: `E2` is marked as non-exhaustive
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
 LL |     match e2 { E2::A => (), E2::B => (), _ => todo!() };
diff --git a/tests/ui/pattern/usefulness/auxiliary/non-exhaustive.rs b/tests/ui/pattern/usefulness/auxiliary/non-exhaustive.rs
new file mode 100644
index 00000000000..6f459b8268f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/auxiliary/non-exhaustive.rs
@@ -0,0 +1,2 @@
+#[non_exhaustive]
+pub enum NonExhaustiveEnum { A, B }
diff --git a/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.rs b/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.rs
new file mode 100644
index 00000000000..3a8a74d1fd6
--- /dev/null
+++ b/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.rs
@@ -0,0 +1,18 @@
+// aux-build:non-exhaustive.rs
+
+extern crate non_exhaustive;
+
+use non_exhaustive::NonExhaustiveEnum;
+
+fn main() {
+    match Some(NonExhaustiveEnum::A) {
+        //~^ ERROR non-exhaustive patterns: `Some(_)` not covered [E0004]
+        //~| NOTE pattern `Some(_)` not covered
+        //~| NOTE `Option<NonExhaustiveEnum>` defined here
+        //~| NOTE the matched value is of type `Option<NonExhaustiveEnum>`
+        //~| NOTE `NonExhaustiveEnum` is marked as non-exhaustive
+        Some(NonExhaustiveEnum::A) => {}
+        Some(NonExhaustiveEnum::B) => {}
+        None => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr b/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr
new file mode 100644
index 00000000000..ae81f307fde
--- /dev/null
+++ b/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr
@@ -0,0 +1,22 @@
+error[E0004]: non-exhaustive patterns: `Some(_)` not covered
+  --> $DIR/nested-non-exhaustive-enums.rs:8:11
+   |
+LL |     match Some(NonExhaustiveEnum::A) {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Some(_)` not covered
+   |
+note: `Option<NonExhaustiveEnum>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<NonExhaustiveEnum>`
+   = note: `NonExhaustiveEnum` is marked as non-exhaustive
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         None => {},
+LL +         Some(_) => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr
index 872cb9b8bc6..50209e18bd1 100644
--- a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr
+++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr
@@ -28,7 +28,8 @@ note: `NonExhaustiveEnum` defined here
    |
 LL | pub enum NonExhaustiveEnum {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: the matched value is of type `NonExhaustiveEnum`, which is marked as non-exhaustive
+   = note: the matched value is of type `NonExhaustiveEnum`
+   = note: `NonExhaustiveEnum` is marked as non-exhaustive
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
 LL ~         NonExhaustiveEnum::Struct { .. } => "third",
@@ -46,7 +47,7 @@ note: `NonExhaustiveEnum` defined here
    |
 LL | pub enum NonExhaustiveEnum {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: the matched value is of type `NonExhaustiveEnum`, which is marked as non-exhaustive
+   = note: the matched value is of type `NonExhaustiveEnum`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
 LL ~     match enum_unit {