about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs10
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs85
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/usefulness.rs32
-rw-r--r--src/test/ui/pattern/usefulness/auxiliary/hidden.rs6
-rw-r--r--src/test/ui/pattern/usefulness/auxiliary/unstable.rs12
-rw-r--r--src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs30
-rw-r--r--src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr54
-rw-r--r--src/test/ui/pattern/usefulness/stable-gated-patterns.rs18
-rw-r--r--src/test/ui/pattern/usefulness/stable-gated-patterns.stderr26
-rw-r--r--src/test/ui/pattern/usefulness/unstable-gated-patterns.rs22
-rw-r--r--src/test/ui/pattern/usefulness/unstable-gated-patterns.stderr17
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs29
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/omitted-patterns.rs (renamed from src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.rs)66
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/omitted-patterns.stderr (renamed from src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr)64
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.rs33
-rw-r--r--src/test/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr16
16 files changed, 428 insertions, 92 deletions
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index e95493acbb7..0eacedc09ee 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -38,7 +38,7 @@ use rustc_macros::HashStable;
 use rustc_query_system::ich::StableHashingContext;
 use rustc_session::cstore::CrateStoreDyn;
 use rustc_span::symbol::{kw, Ident, Symbol};
-use rustc_span::Span;
+use rustc_span::{sym, Span};
 use rustc_target::abi::Align;
 
 use std::cmp::Ordering;
@@ -1900,6 +1900,14 @@ impl<'tcx> TyCtxt<'tcx> {
         self.sess.contains_name(&self.get_attrs(did), attr)
     }
 
+    /// Determines whether an item is annotated with `doc(hidden)`.
+    pub fn is_doc_hidden(self, did: DefId) -> bool {
+        self.get_attrs(did)
+            .iter()
+            .filter_map(|attr| if attr.has_name(sym::doc) { attr.meta_item_list() } else { None })
+            .any(|items| items.iter().any(|item| item.has_name(sym::hidden)))
+    }
+
     /// Returns `true` if this is an `auto trait`.
     pub fn trait_is_auto(self, trait_def_id: DefId) -> bool {
         self.trait_def(trait_def_id).has_auto_impl
diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
index 9e961f7ba5d..368e3957dd0 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
@@ -52,11 +52,11 @@ use rustc_data_structures::captures::Captures;
 use rustc_index::vec::Idx;
 
 use rustc_hir::{HirId, RangeEnd};
-use rustc_middle::mir::interpret::ConstValue;
 use rustc_middle::mir::Field;
 use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange};
 use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::ty::{self, Const, Ty, TyCtxt, VariantDef};
+use rustc_middle::{middle::stability::EvalResult, mir::interpret::ConstValue};
 use rustc_session::lint;
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::{Integer, Size, VariantIdx};
@@ -675,6 +675,36 @@ impl<'tcx> Constructor<'tcx> {
         }
     }
 
+    /// Checks if the `Constructor` is a variant and `TyCtxt::eval_stability` returns
+    /// `EvalResult::Deny { .. }`.
+    ///
+    /// This means that the variant has a stdlib unstable feature marking it.
+    pub(super) fn is_unstable_variant(&self, pcx: PatCtxt<'_, '_, 'tcx>) -> bool {
+        if let Constructor::Variant(idx) = self {
+            if let ty::Adt(adt, _) = pcx.ty.kind() {
+                let variant_def_id = adt.variants[*idx].def_id;
+                // Filter variants that depend on a disabled unstable feature.
+                return matches!(
+                    pcx.cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None),
+                    EvalResult::Deny { .. }
+                );
+            }
+        }
+        false
+    }
+
+    /// Checks if the `Constructor` is a `Constructor::Variant` with a `#[doc(hidden)]`
+    /// attribute.
+    pub(super) fn is_doc_hidden_variant(&self, pcx: PatCtxt<'_, '_, 'tcx>) -> bool {
+        if let Constructor::Variant(idx) = self {
+            if let ty::Adt(adt, _) = pcx.ty.kind() {
+                let variant_def_id = adt.variants[*idx].def_id;
+                return pcx.cx.tcx.is_doc_hidden(variant_def_id);
+            }
+        }
+        false
+    }
+
     fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> VariantIdx {
         match *self {
             Variant(idx) => idx,
@@ -929,36 +959,33 @@ impl<'tcx> SplitWildcard<'tcx> {
                 // witness.
                 let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty);
 
+                let is_exhaustive_pat_feature = cx.tcx.features().exhaustive_patterns;
+
                 // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it
                 // as though it had an "unknown" constructor to avoid exposing its emptiness. The
                 // exception is if the pattern is at the top level, because we want empty matches to be
                 // considered exhaustive.
-                let is_secretly_empty = def.variants.is_empty()
-                    && !cx.tcx.features().exhaustive_patterns
-                    && !pcx.is_top_level;
-
-                if is_secretly_empty {
-                    smallvec![NonExhaustive]
-                } else if is_declared_nonexhaustive {
-                    def.variants
-                        .indices()
-                        .map(|idx| Variant(idx))
-                        .chain(Some(NonExhaustive))
-                        .collect()
-                } else if cx.tcx.features().exhaustive_patterns {
-                    // If `exhaustive_patterns` is enabled, we exclude variants known to be
-                    // uninhabited.
-                    def.variants
-                        .iter_enumerated()
-                        .filter(|(_, v)| {
-                            !v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env)
-                                .contains(cx.tcx, cx.module)
-                        })
-                        .map(|(idx, _)| Variant(idx))
-                        .collect()
-                } else {
-                    def.variants.indices().map(|idx| Variant(idx)).collect()
+                let is_secretly_empty =
+                    def.variants.is_empty() && !is_exhaustive_pat_feature && !pcx.is_top_level;
+
+                let mut ctors: SmallVec<[_; 1]> = def
+                    .variants
+                    .iter_enumerated()
+                    .filter(|(_, v)| {
+                        // If `exhaustive_patterns` is enabled, we exclude variants known to be
+                        // uninhabited.
+                        let is_uninhabited = is_exhaustive_pat_feature
+                            && v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env)
+                                .contains(cx.tcx, cx.module);
+                        !is_uninhabited
+                    })
+                    .map(|(idx, _)| Variant(idx))
+                    .collect();
+
+                if is_secretly_empty || is_declared_nonexhaustive {
+                    ctors.push(NonExhaustive);
                 }
+                ctors
             }
             ty::Char => {
                 smallvec![
@@ -1068,7 +1095,7 @@ impl<'tcx> SplitWildcard<'tcx> {
                     Missing {
                         nonexhaustive_enum_missing_real_variants: self
                             .iter_missing(pcx)
-                            .any(|c| !c.is_non_exhaustive()),
+                            .any(|c| !(c.is_non_exhaustive() || c.is_unstable_variant(pcx))),
                     }
                 } else {
                     Missing { nonexhaustive_enum_missing_real_variants: false }
@@ -1222,9 +1249,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
 
 /// Values and patterns can be represented as a constructor applied to some fields. This represents
 /// a pattern in this form.
-/// This also keeps track of whether the pattern has been foundreachable during analysis. For this
+/// This also keeps track of whether the pattern has been found reachable during analysis. For this
 /// reason we should be careful not to clone patterns for which we care about that. Use
-/// `clone_and_forget_reachability` is you're sure.
+/// `clone_and_forget_reachability` if you're sure.
 pub(crate) struct DeconstructedPat<'p, 'tcx> {
     ctor: Constructor<'tcx>,
     fields: Fields<'p, 'tcx>,
diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
index 43adef3d03b..d959d2f7f6f 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
@@ -585,15 +585,33 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
                     } else {
                         let mut split_wildcard = SplitWildcard::new(pcx);
                         split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
+
+                        // This lets us know if we skipped any variants because they are marked
+                        // `doc(hidden)` or they are unstable feature gate (only stdlib types).
+                        let mut hide_variant_show_wild = false;
                         // Construct for each missing constructor a "wild" version of this
                         // 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(_)`.
-                        split_wildcard
+                        let mut new: Vec<DeconstructedPat<'_, '_>> = split_wildcard
                             .iter_missing(pcx)
-                            .cloned()
-                            .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor))
-                            .collect()
+                            .filter_map(|missing_ctor| {
+                                // Check if this variant is marked `doc(hidden)`
+                                if missing_ctor.is_doc_hidden_variant(pcx)
+                                    || missing_ctor.is_unstable_variant(pcx)
+                                {
+                                    hide_variant_show_wild = true;
+                                    return None;
+                                }
+                                Some(DeconstructedPat::wild_from_ctor(pcx, missing_ctor.clone()))
+                            })
+                            .collect();
+
+                        if hide_variant_show_wild {
+                            new.push(DeconstructedPat::wildcard(pcx.ty));
+                        }
+
+                        new
                     };
 
                     witnesses
@@ -851,8 +869,10 @@ fn is_useful<'p, 'tcx>(
                     split_wildcard
                         .iter_missing(pcx)
                         // Filter out the `NonExhaustive` because we want to list only real
-                        // variants.
-                        .filter(|c| !c.is_non_exhaustive())
+                        // variants. Also remove any unstable feature gated variants.
+                        // Because of how we computed `nonexhaustive_enum_missing_real_variants`,
+                        // this will not return an empty `Vec`.
+                        .filter(|c| !(c.is_non_exhaustive() || c.is_unstable_variant(pcx)))
                         .cloned()
                         .map(|missing_ctor| DeconstructedPat::wild_from_ctor(pcx, missing_ctor))
                         .collect::<Vec<_>>()
diff --git a/src/test/ui/pattern/usefulness/auxiliary/hidden.rs b/src/test/ui/pattern/usefulness/auxiliary/hidden.rs
new file mode 100644
index 00000000000..742b7e82c16
--- /dev/null
+++ b/src/test/ui/pattern/usefulness/auxiliary/hidden.rs
@@ -0,0 +1,6 @@
+pub enum Foo {
+    A,
+    B,
+    #[doc(hidden)]
+    C,
+}
diff --git a/src/test/ui/pattern/usefulness/auxiliary/unstable.rs b/src/test/ui/pattern/usefulness/auxiliary/unstable.rs
new file mode 100644
index 00000000000..3142489c861
--- /dev/null
+++ b/src/test/ui/pattern/usefulness/auxiliary/unstable.rs
@@ -0,0 +1,12 @@
+#![feature(staged_api)]
+#![stable(feature = "stable_test_feature", since = "1.0.0")]
+
+#[stable(feature = "stable_test_feature", since = "1.0.0")]
+pub enum Foo {
+    #[stable(feature = "stable_test_feature", since = "1.0.0")]
+    Stable,
+    #[stable(feature = "stable_test_feature", since = "1.0.0")]
+    Stable2,
+    #[unstable(feature = "unstable_test_feature", issue = "none")]
+    Unstable,
+}
diff --git a/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs
new file mode 100644
index 00000000000..a1dcab09314
--- /dev/null
+++ b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs
@@ -0,0 +1,30 @@
+// aux-build:hidden.rs
+
+extern crate hidden;
+
+use hidden::Foo;
+
+fn main() {
+    match Foo::A {
+        Foo::A => {}
+        Foo::B => {}
+    }
+    //~^^^^ non-exhaustive patterns: `_` not covered
+
+    match Foo::A {
+        Foo::A => {}
+        Foo::C => {}
+    }
+    //~^^^^ non-exhaustive patterns: `B` not covered
+
+    match Foo::A {
+        Foo::A => {}
+    }
+    //~^^^ non-exhaustive patterns: `B` and `_` not covered
+
+    match None {
+        None => {}
+        Some(Foo::A) => {}
+    }
+    //~^^^^ non-exhaustive patterns: `Some(B)` and `Some(_)` not covered
+}
diff --git a/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr
new file mode 100644
index 00000000000..6c9539822b3
--- /dev/null
+++ b/src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr
@@ -0,0 +1,54 @@
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/doc-hidden-non-exhaustive.rs:8:11
+   |
+LL |     match Foo::A {
+   |           ^^^^^^ pattern `_` not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+   = note: the matched value is of type `Foo`
+
+error[E0004]: non-exhaustive patterns: `B` not covered
+  --> $DIR/doc-hidden-non-exhaustive.rs:14:11
+   |
+LL |     match Foo::A {
+   |           ^^^^^^ pattern `B` not covered
+   |
+  ::: $DIR/auxiliary/hidden.rs:3:5
+   |
+LL |     B,
+   |     - not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+   = note: the matched value is of type `Foo`
+
+error[E0004]: non-exhaustive patterns: `B` and `_` not covered
+  --> $DIR/doc-hidden-non-exhaustive.rs:20:11
+   |
+LL |     match Foo::A {
+   |           ^^^^^^ patterns `B` and `_` not covered
+   |
+  ::: $DIR/auxiliary/hidden.rs:3:5
+   |
+LL |     B,
+   |     - not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+   = note: the matched value is of type `Foo`
+
+error[E0004]: non-exhaustive patterns: `Some(B)` and `Some(_)` not covered
+  --> $DIR/doc-hidden-non-exhaustive.rs:25:11
+   |
+LL |     match None {
+   |           ^^^^ patterns `Some(B)` and `Some(_)` not covered
+   |
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+LL |     Some(#[stable(feature = "rust1", since = "1.0.0")] T),
+   |     ---- not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+   = note: the matched value is of type `Option<Foo>`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/src/test/ui/pattern/usefulness/stable-gated-patterns.rs b/src/test/ui/pattern/usefulness/stable-gated-patterns.rs
new file mode 100644
index 00000000000..2e023a3be4a
--- /dev/null
+++ b/src/test/ui/pattern/usefulness/stable-gated-patterns.rs
@@ -0,0 +1,18 @@
+// aux-build:unstable.rs
+
+extern crate unstable;
+
+use unstable::Foo;
+
+fn main() {
+    match Foo::Stable {
+        Foo::Stable => {}
+    }
+    //~^^^ non-exhaustive patterns: `Stable2` and `_` not covered
+
+    match Foo::Stable {
+        Foo::Stable => {}
+        Foo::Stable2 => {}
+    }
+    //~^^^^ non-exhaustive patterns: `_` not covered
+}
diff --git a/src/test/ui/pattern/usefulness/stable-gated-patterns.stderr b/src/test/ui/pattern/usefulness/stable-gated-patterns.stderr
new file mode 100644
index 00000000000..9b42565ac73
--- /dev/null
+++ b/src/test/ui/pattern/usefulness/stable-gated-patterns.stderr
@@ -0,0 +1,26 @@
+error[E0004]: non-exhaustive patterns: `Stable2` and `_` not covered
+  --> $DIR/stable-gated-patterns.rs:8:11
+   |
+LL |     match Foo::Stable {
+   |           ^^^^^^^^^^^ patterns `Stable2` and `_` not covered
+   |
+  ::: $DIR/auxiliary/unstable.rs:9:5
+   |
+LL |     Stable2,
+   |     ------- not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+   = note: the matched value is of type `Foo`
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/stable-gated-patterns.rs:13:11
+   |
+LL |     match Foo::Stable {
+   |           ^^^^^^^^^^^ pattern `_` not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+   = note: the matched value is of type `Foo`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/src/test/ui/pattern/usefulness/unstable-gated-patterns.rs b/src/test/ui/pattern/usefulness/unstable-gated-patterns.rs
new file mode 100644
index 00000000000..b9804b0ffe7
--- /dev/null
+++ b/src/test/ui/pattern/usefulness/unstable-gated-patterns.rs
@@ -0,0 +1,22 @@
+#![feature(unstable_test_feature)]
+
+// aux-build:unstable.rs
+
+extern crate unstable;
+
+use unstable::Foo;
+
+fn main() {
+    match Foo::Stable {
+        Foo::Stable => {}
+        Foo::Stable2 => {}
+    }
+    //~^^^^ non-exhaustive patterns: `Unstable` not covered
+
+    // Ok: all variants are explicitly matched
+    match Foo::Stable {
+        Foo::Stable => {}
+        Foo::Stable2 => {}
+        Foo::Unstable => {}
+    }
+}
diff --git a/src/test/ui/pattern/usefulness/unstable-gated-patterns.stderr b/src/test/ui/pattern/usefulness/unstable-gated-patterns.stderr
new file mode 100644
index 00000000000..f9c0196b765
--- /dev/null
+++ b/src/test/ui/pattern/usefulness/unstable-gated-patterns.stderr
@@ -0,0 +1,17 @@
+error[E0004]: non-exhaustive patterns: `Unstable` not covered
+  --> $DIR/unstable-gated-patterns.rs:10:11
+   |
+LL |     match Foo::Stable {
+   |           ^^^^^^^^^^^ pattern `Unstable` not covered
+   |
+  ::: $DIR/auxiliary/unstable.rs:11:5
+   |
+LL |     Unstable,
+   |     -------- not covered
+   |
+   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
+   = note: the matched value is of type `Foo`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/src/test/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs b/src/test/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs
new file mode 100644
index 00000000000..de9d6f65945
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs
@@ -0,0 +1,29 @@
+#![feature(staged_api)]
+#![stable(feature = "stable_test_feature", since = "1.0.0")]
+
+#[stable(feature = "stable_test_feature", since = "1.0.0")]
+#[non_exhaustive]
+pub enum UnstableEnum {
+    #[stable(feature = "stable_test_feature", since = "1.0.0")]
+    Stable,
+    #[stable(feature = "stable_test_feature", since = "1.0.0")]
+    Stable2,
+    #[unstable(feature = "unstable_test_feature", issue = "none")]
+    Unstable,
+}
+
+#[stable(feature = "stable_test_feature", since = "1.0.0")]
+#[non_exhaustive]
+pub enum OnlyUnstableEnum {
+    #[unstable(feature = "unstable_test_feature", issue = "none")]
+    Unstable,
+    #[unstable(feature = "unstable_test_feature", issue = "none")]
+    Unstable2,
+}
+
+impl OnlyUnstableEnum {
+    #[stable(feature = "stable_test_feature", since = "1.0.0")]
+    pub fn new() -> Self {
+        Self::Unstable
+    }
+}
diff --git a/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.rs b/src/test/ui/rfc-2008-non-exhaustive/omitted-patterns.rs
index ce1b5c7c377..fe9734fdc08 100644
--- a/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.rs
+++ b/src/test/ui/rfc-2008-non-exhaustive/omitted-patterns.rs
@@ -1,10 +1,11 @@
 // Test that the `non_exhaustive_omitted_patterns` lint is triggered correctly.
 
-#![feature(non_exhaustive_omitted_patterns_lint)]
+#![feature(non_exhaustive_omitted_patterns_lint, unstable_test_feature)]
 
 // aux-build:enums.rs
 extern crate enums;
-
+// aux-build:unstable.rs
+extern crate unstable;
 // aux-build:structs.rs
 extern crate structs;
 
@@ -12,6 +13,7 @@ use enums::{
     EmptyNonExhaustiveEnum, NestedNonExhaustive, NonExhaustiveEnum, NonExhaustiveSingleVariant,
     VariantNonExhaustive,
 };
+use unstable::{UnstableEnum, OnlyUnstableEnum};
 use structs::{FunctionalRecord, MixedVisFields, NestedStruct, NormalStruct};
 
 #[non_exhaustive]
@@ -94,35 +96,6 @@ fn main() {
     //~^^ some variants are not matched explicitly
     //~^^^^^ some variants are not matched explicitly
 
-    // The io::ErrorKind has many `unstable` fields how do they interact with this
-    // lint
-    #[deny(non_exhaustive_omitted_patterns)]
-    match std::io::ErrorKind::Other {
-        std::io::ErrorKind::NotFound => {}
-        std::io::ErrorKind::PermissionDenied => {}
-        std::io::ErrorKind::ConnectionRefused => {}
-        std::io::ErrorKind::ConnectionReset => {}
-        std::io::ErrorKind::ConnectionAborted => {}
-        std::io::ErrorKind::NotConnected => {}
-        std::io::ErrorKind::AddrInUse => {}
-        std::io::ErrorKind::AddrNotAvailable => {}
-        std::io::ErrorKind::BrokenPipe => {}
-        std::io::ErrorKind::AlreadyExists => {}
-        std::io::ErrorKind::WouldBlock => {}
-        std::io::ErrorKind::InvalidInput => {}
-        std::io::ErrorKind::InvalidData => {}
-        std::io::ErrorKind::TimedOut => {}
-        std::io::ErrorKind::WriteZero => {}
-        std::io::ErrorKind::Interrupted => {}
-        std::io::ErrorKind::Other => {}
-        std::io::ErrorKind::UnexpectedEof => {}
-        std::io::ErrorKind::Unsupported => {}
-        std::io::ErrorKind::OutOfMemory => {}
-        // All stable variants are above and unstable in `_`
-        _ => {}
-    }
-    //~^^ some variants are not matched explicitly
-
     #[warn(non_exhaustive_omitted_patterns)]
     match VariantNonExhaustive::Baz(1, 2) {
         VariantNonExhaustive::Baz(_, _) => {}
@@ -163,4 +136,35 @@ fn main() {
     // Ok: we don't lint on `if let` expressions
     #[deny(non_exhaustive_omitted_patterns)]
     if let NonExhaustiveEnum::Tuple(_) = non_enum {}
+
+    match UnstableEnum::Stable {
+        UnstableEnum::Stable => {}
+        UnstableEnum::Stable2 => {}
+        #[deny(non_exhaustive_omitted_patterns)]
+        _ => {}
+    }
+    //~^^ some variants are not matched explicitly
+
+    #[deny(non_exhaustive_omitted_patterns)]
+    match UnstableEnum::Stable {
+        UnstableEnum::Stable => {}
+        UnstableEnum::Stable2 => {}
+        UnstableEnum::Unstable => {}
+        _ => {}
+    }
+
+    // Ok: the feature is on and both variants are matched
+    #[deny(non_exhaustive_omitted_patterns)]
+    match OnlyUnstableEnum::Unstable {
+        OnlyUnstableEnum::Unstable => {}
+        OnlyUnstableEnum::Unstable2 => {}
+        _ => {}
+    }
+
+    #[deny(non_exhaustive_omitted_patterns)]
+    match OnlyUnstableEnum::Unstable {
+        OnlyUnstableEnum::Unstable => {}
+        _ => {}
+    }
+    //~^^ some variants are not matched explicitly
 }
diff --git a/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/omitted-patterns.stderr
index 5b21e0402b1..30f3f88ad91 100644
--- a/src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr
+++ b/src/test/ui/rfc-2008-non-exhaustive/omitted-patterns.stderr
@@ -1,11 +1,11 @@
 warning: some fields are not explicitly listed
-  --> $DIR/reachable-patterns.rs:129:9
+  --> $DIR/omitted-patterns.rs:102:9
    |
 LL |         VariantNonExhaustive::Bar { x, .. } => {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `y` not listed
    |
 note: the lint level is defined here
-  --> $DIR/reachable-patterns.rs:126:12
+  --> $DIR/omitted-patterns.rs:99:12
    |
 LL |     #[warn(non_exhaustive_omitted_patterns)]
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -13,13 +13,13 @@ LL |     #[warn(non_exhaustive_omitted_patterns)]
    = note: the pattern is of type `VariantNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found
 
 warning: some fields are not explicitly listed
-  --> $DIR/reachable-patterns.rs:134:9
+  --> $DIR/omitted-patterns.rs:107:9
    |
 LL |     let FunctionalRecord { first_field, second_field, .. } = FunctionalRecord::default();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `third_field` not listed
    |
 note: the lint level is defined here
-  --> $DIR/reachable-patterns.rs:133:12
+  --> $DIR/omitted-patterns.rs:106:12
    |
 LL |     #[warn(non_exhaustive_omitted_patterns)]
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -27,13 +27,13 @@ LL |     #[warn(non_exhaustive_omitted_patterns)]
    = note: the pattern is of type `FunctionalRecord` and the `non_exhaustive_omitted_patterns` attribute was found
 
 warning: some fields are not explicitly listed
-  --> $DIR/reachable-patterns.rs:142:29
+  --> $DIR/omitted-patterns.rs:115:29
    |
 LL |     let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default();
    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `second_field` not listed
    |
 note: the lint level is defined here
-  --> $DIR/reachable-patterns.rs:141:12
+  --> $DIR/omitted-patterns.rs:114:12
    |
 LL |     #[warn(non_exhaustive_omitted_patterns)]
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -41,7 +41,7 @@ LL |     #[warn(non_exhaustive_omitted_patterns)]
    = note: the pattern is of type `NormalStruct` and the `non_exhaustive_omitted_patterns` attribute was found
 
 warning: some fields are not explicitly listed
-  --> $DIR/reachable-patterns.rs:142:9
+  --> $DIR/omitted-patterns.rs:115:9
    |
 LL |     let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `foo` not listed
@@ -50,13 +50,13 @@ LL |     let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = Nested
    = note: the pattern is of type `NestedStruct` and the `non_exhaustive_omitted_patterns` attribute was found
 
 error: some variants are not matched explicitly
-  --> $DIR/reachable-patterns.rs:56:9
+  --> $DIR/omitted-patterns.rs:58:9
    |
 LL |         _ => {}
    |         ^ pattern `Struct { .. }` not covered
    |
 note: the lint level is defined here
-  --> $DIR/reachable-patterns.rs:55:16
+  --> $DIR/omitted-patterns.rs:57:16
    |
 LL |         #[deny(non_exhaustive_omitted_patterns)]
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -64,13 +64,13 @@ LL |         #[deny(non_exhaustive_omitted_patterns)]
    = note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
 
 error: some variants are not matched explicitly
-  --> $DIR/reachable-patterns.rs:63:9
+  --> $DIR/omitted-patterns.rs:65:9
    |
 LL |         _ => {}
    |         ^ pattern `Tuple(_)` not covered
    |
 note: the lint level is defined here
-  --> $DIR/reachable-patterns.rs:62:16
+  --> $DIR/omitted-patterns.rs:64:16
    |
 LL |         #[deny(non_exhaustive_omitted_patterns)]
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -78,13 +78,13 @@ LL |         #[deny(non_exhaustive_omitted_patterns)]
    = note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
 
 error: some variants are not matched explicitly
-  --> $DIR/reachable-patterns.rs:73:9
+  --> $DIR/omitted-patterns.rs:75:9
    |
 LL |         _ => {}
    |         ^ pattern `Unit` not covered
    |
 note: the lint level is defined here
-  --> $DIR/reachable-patterns.rs:72:16
+  --> $DIR/omitted-patterns.rs:74:16
    |
 LL |         #[deny(non_exhaustive_omitted_patterns)]
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -92,13 +92,13 @@ LL |         #[deny(non_exhaustive_omitted_patterns)]
    = note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
 
 error: some variants are not matched explicitly
-  --> $DIR/reachable-patterns.rs:90:32
+  --> $DIR/omitted-patterns.rs:92:32
    |
 LL |         NestedNonExhaustive::A(_) => {}
    |                                ^ patterns `Tuple(_)` and `Struct { .. }` not covered
    |
 note: the lint level is defined here
-  --> $DIR/reachable-patterns.rs:87:12
+  --> $DIR/omitted-patterns.rs:89:12
    |
 LL |     #[deny(non_exhaustive_omitted_patterns)]
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -106,7 +106,7 @@ LL |     #[deny(non_exhaustive_omitted_patterns)]
    = note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
 
 error: some variants are not matched explicitly
-  --> $DIR/reachable-patterns.rs:92:9
+  --> $DIR/omitted-patterns.rs:94:9
    |
 LL |         _ => {}
    |         ^ pattern `C` not covered
@@ -115,32 +115,46 @@ LL |         _ => {}
    = note: the matched value is of type `NestedNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found
 
 error: some variants are not matched explicitly
-  --> $DIR/reachable-patterns.rs:122:9
+  --> $DIR/omitted-patterns.rs:132:9
    |
 LL |         _ => {}
-   |         ^ patterns `HostUnreachable`, `NetworkUnreachable`, `NetworkDown` and 18 more not covered
+   |         ^ pattern `A(_)` not covered
    |
 note: the lint level is defined here
-  --> $DIR/reachable-patterns.rs:99:12
+  --> $DIR/omitted-patterns.rs:130:12
    |
 LL |     #[deny(non_exhaustive_omitted_patterns)]
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: ensure that all variants are matched explicitly by adding the suggested match arms
-   = note: the matched value is of type `ErrorKind` and the `non_exhaustive_omitted_patterns` attribute was found
+   = note: the matched value is of type `NonExhaustiveSingleVariant` and the `non_exhaustive_omitted_patterns` attribute was found
 
 error: some variants are not matched explicitly
-  --> $DIR/reachable-patterns.rs:159:9
+  --> $DIR/omitted-patterns.rs:144:9
    |
 LL |         _ => {}
-   |         ^ pattern `A(_)` not covered
+   |         ^ pattern `Unstable` not covered
+   |
+note: the lint level is defined here
+  --> $DIR/omitted-patterns.rs:143:16
+   |
+LL |         #[deny(non_exhaustive_omitted_patterns)]
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = help: ensure that all variants are matched explicitly by adding the suggested match arms
+   = note: the matched value is of type `UnstableEnum` and the `non_exhaustive_omitted_patterns` attribute was found
+
+error: some variants are not matched explicitly
+  --> $DIR/omitted-patterns.rs:167:9
+   |
+LL |         _ => {}
+   |         ^ pattern `Unstable2` not covered
    |
 note: the lint level is defined here
-  --> $DIR/reachable-patterns.rs:157:12
+  --> $DIR/omitted-patterns.rs:164:12
    |
 LL |     #[deny(non_exhaustive_omitted_patterns)]
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: ensure that all variants are matched explicitly by adding the suggested match arms
-   = note: the matched value is of type `NonExhaustiveSingleVariant` and the `non_exhaustive_omitted_patterns` attribute was found
+   = note: the matched value is of type `OnlyUnstableEnum` and the `non_exhaustive_omitted_patterns` attribute was found
 
-error: aborting due to 7 previous errors; 4 warnings emitted
+error: aborting due to 8 previous errors; 4 warnings emitted
 
diff --git a/src/test/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.rs b/src/test/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.rs
new file mode 100644
index 00000000000..9621d28f8e2
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.rs
@@ -0,0 +1,33 @@
+// Test that the `non_exhaustive_omitted_patterns` lint is triggered correctly with variants
+// marked stable and unstable.
+
+#![feature(non_exhaustive_omitted_patterns_lint)]
+
+// aux-build:unstable.rs
+extern crate unstable;
+
+use unstable::{UnstableEnum, OnlyUnstableEnum};
+
+fn main() {
+    // OK: this matches all the stable variants
+    match UnstableEnum::Stable {
+        UnstableEnum::Stable => {}
+        UnstableEnum::Stable2 => {}
+        #[deny(non_exhaustive_omitted_patterns)]
+        _ => {}
+    }
+
+    match UnstableEnum::Stable {
+        UnstableEnum::Stable => {}
+        #[deny(non_exhaustive_omitted_patterns)]
+        _ => {}
+    }
+    //~^^ some variants are not matched explicitly
+
+    // Ok: although this is a bit odd, we don't have anything to report
+    // since there is no stable variants and the feature is off
+    #[deny(non_exhaustive_omitted_patterns)]
+    match OnlyUnstableEnum::new() {
+        _ => {}
+    }
+}
diff --git a/src/test/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr
new file mode 100644
index 00000000000..b9a281974fa
--- /dev/null
+++ b/src/test/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr
@@ -0,0 +1,16 @@
+error: some variants are not matched explicitly
+  --> $DIR/stable-omitted-patterns.rs:23:9
+   |
+LL |         _ => {}
+   |         ^ pattern `Stable2` not covered
+   |
+note: the lint level is defined here
+  --> $DIR/stable-omitted-patterns.rs:22:16
+   |
+LL |         #[deny(non_exhaustive_omitted_patterns)]
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = help: ensure that all variants are matched explicitly by adding the suggested match arms
+   = note: the matched value is of type `UnstableEnum` and the `non_exhaustive_omitted_patterns` attribute was found
+
+error: aborting due to previous error
+