From 2a042d61055263e404d9588d39046ed51e96a560 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Sat, 18 Sep 2021 16:52:43 -0400 Subject: [PATCH] Filter unstable and doc hidden variants in usefulness checking Add test cases for unstable variants Add test cases for doc hidden variants Move is_doc_hidden to method on TyCtxt Add unstable variants test to reachable-patterns ui test Rename reachable-patterns -> omitted-patterns --- compiler/rustc_middle/src/ty/mod.rs | 10 ++- .../src/thir/pattern/deconstruct_pat.rs | 85 ++++++++++++------- .../src/thir/pattern/usefulness.rs | 32 +++++-- .../ui/pattern/usefulness/auxiliary/hidden.rs | 6 ++ .../pattern/usefulness/auxiliary/unstable.rs | 12 +++ .../usefulness/doc-hidden-non-exhaustive.rs | 30 +++++++ .../doc-hidden-non-exhaustive.stderr | 54 ++++++++++++ .../usefulness/stable-gated-patterns.rs | 18 ++++ .../usefulness/stable-gated-patterns.stderr | 26 ++++++ .../usefulness/unstable-gated-patterns.rs | 22 +++++ .../usefulness/unstable-gated-patterns.stderr | 17 ++++ .../auxiliary/unstable.rs | 29 +++++++ ...chable-patterns.rs => omitted-patterns.rs} | 66 +++++++------- ...atterns.stderr => omitted-patterns.stderr} | 64 ++++++++------ .../stable-omitted-patterns.rs | 33 +++++++ .../stable-omitted-patterns.stderr | 16 ++++ 16 files changed, 428 insertions(+), 92 deletions(-) create mode 100644 src/test/ui/pattern/usefulness/auxiliary/hidden.rs create mode 100644 src/test/ui/pattern/usefulness/auxiliary/unstable.rs create mode 100644 src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs create mode 100644 src/test/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr create mode 100644 src/test/ui/pattern/usefulness/stable-gated-patterns.rs create mode 100644 src/test/ui/pattern/usefulness/stable-gated-patterns.stderr create mode 100644 src/test/ui/pattern/usefulness/unstable-gated-patterns.rs create mode 100644 src/test/ui/pattern/usefulness/unstable-gated-patterns.stderr create mode 100644 src/test/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs rename src/test/ui/rfc-2008-non-exhaustive/{reachable-patterns.rs => omitted-patterns.rs} (77%) rename src/test/ui/rfc-2008-non-exhaustive/{reachable-patterns.stderr => omitted-patterns.stderr} (77%) create mode 100644 src/test/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.rs create mode 100644 src/test/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr 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_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 @@ pub fn has_attr(self, did: DefId, attr: Symbol) -> bool { 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_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 @@ fn as_slice(&self) -> Option { } } + /// 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 @@ pub(super) fn new<'p>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Self { // 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 @@ pub(super) fn iter_missing<'a, 'p>( 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 @@ pub(super) fn iter_patterns<'a>( /// 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 @@ fn apply_constructor( } 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> = 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::>() 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` + +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 similarity index 77% rename from src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.rs rename to 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 @@ 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 similarity index 77% rename from src/test/ui/rfc-2008-non-exhaustive/reachable-patterns.stderr rename to 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 + -- GitLab