Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2b5ddf3

Browse files
committedSep 16, 2021
Auto merge of #86809 - DevinR528:reachable-pat, r=Nadrieril
Add non_exhaustive_omitted_patterns lint related to rfc-2008-non_exhaustive Fixes: #84332 This PR adds `non_exhaustive_omitted_patterns`, an allow by default lint that is triggered when a `non_exhaustive` type is missing explicit patterns. The warning or deny attribute can be put above the wildcard `_` pattern on enums or on the expression for enums or structs. The lint is capable of warning about multiple types within the same pattern. This lint will not be triggered for `if let ..` patterns. ```rust // crate A #[non_exhaustive] pub struct Foo { a: u8, b: usize, } #[non_exhaustive] pub enum Bar { A(Foo), B, } // crate B #[deny(non_exhaustive_omitted_patterns)] // here match Bar::B { Bar::B => {} #[deny(non_exhaustive_omitted_patterns)] // or here _ => {} } #[warn(non_exhaustive_omitted_patterns)] // only here let Foo { a, .. } = Foo::default(); #[deny(non_exhaustive_omitted_patterns)] match Bar::B { // triggers for Bar::B, and Foo.b Bar::A(Foo { a, .. }) => {} // if the attribute was here only Bar::B would cause a warning _ => {} } ```
2 parents 34327f6 + 33a06b7 commit 2b5ddf3

File tree

10 files changed

+626
-68
lines changed

10 files changed

+626
-68
lines changed
 

‎compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3010,6 +3010,7 @@ declare_lint_pass! {
30103010
UNSUPPORTED_CALLING_CONVENTIONS,
30113011
BREAK_WITH_LABEL_AND_LOOP,
30123012
UNUSED_ATTRIBUTES,
3013+
NON_EXHAUSTIVE_OMITTED_PATTERNS,
30133014
]
30143015
}
30153016

@@ -3416,3 +3417,56 @@ declare_lint! {
34163417
Warn,
34173418
"`break` expression with label and unlabeled loop as value expression"
34183419
}
3420+
3421+
declare_lint! {
3422+
/// The `non_exhaustive_omitted_patterns` lint detects when a wildcard (`_` or `..`) in a
3423+
/// pattern for a `#[non_exhaustive]` struct or enum is reachable.
3424+
///
3425+
/// ### Example
3426+
///
3427+
/// ```rust,ignore (needs separate crate)
3428+
/// // crate A
3429+
/// #[non_exhaustive]
3430+
/// pub enum Bar {
3431+
/// A,
3432+
/// B, // added variant in non breaking change
3433+
/// }
3434+
///
3435+
/// // in crate B
3436+
/// match Bar::A {
3437+
/// Bar::A => {},
3438+
/// #[warn(non_exhaustive_omitted_patterns)]
3439+
/// _ => {},
3440+
/// }
3441+
/// ```
3442+
///
3443+
/// This will produce:
3444+
///
3445+
/// ```text
3446+
/// warning: reachable patterns not covered of non exhaustive enum
3447+
/// --> $DIR/reachable-patterns.rs:70:9
3448+
/// |
3449+
/// LL | _ => {}
3450+
/// | ^ pattern `B` not covered
3451+
/// |
3452+
/// note: the lint level is defined here
3453+
/// --> $DIR/reachable-patterns.rs:69:16
3454+
/// |
3455+
/// LL | #[warn(non_exhaustive_omitted_patterns)]
3456+
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3457+
/// = help: ensure that all possible cases are being handled by adding the suggested match arms
3458+
/// = note: the matched value is of type `Bar` and the `non_exhaustive_omitted_patterns` attribute was found
3459+
/// ```
3460+
///
3461+
/// ### Explanation
3462+
///
3463+
/// Structs and enums tagged with `#[non_exhaustive]` force the user to add a
3464+
/// (potentially redundant) wildcard when pattern-matching, to allow for future
3465+
/// addition of fields or variants. The `non_exhaustive_omitted_patterns` lint
3466+
/// detects when such a wildcard happens to actually catch some fields/variants.
3467+
/// In other words, when the match without the wildcard would not be exhaustive.
3468+
/// This lets the user be informed if new fields/variants were added.
3469+
pub NON_EXHAUSTIVE_OMITTED_PATTERNS,
3470+
Allow,
3471+
"detect when patterns of types marked `non_exhaustive` are missed",
3472+
}

‎compiler/rustc_mir_build/src/thir/pattern/check_match.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
1414
use rustc_hir::{HirId, Pat};
1515
use rustc_middle::thir::PatKind;
1616
use rustc_middle::ty::{self, Ty, TyCtxt};
17-
use rustc_session::lint::builtin::BINDINGS_WITH_VARIANT_NAME;
18-
use rustc_session::lint::builtin::{IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS};
17+
use rustc_session::lint::builtin::{
18+
BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
19+
};
1920
use rustc_session::Session;
2021
use rustc_span::{DesugaringKind, ExpnKind, Span};
2122
use std::slice;
@@ -559,7 +560,7 @@ fn non_exhaustive_match<'p, 'tcx>(
559560
err.emit();
560561
}
561562

562-
fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String {
563+
crate fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String {
563564
const LIMIT: usize = 3;
564565
match witnesses {
565566
[] => bug!(),
@@ -576,7 +577,7 @@ fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String {
576577
}
577578
}
578579

579-
fn pattern_not_covered_label(witnesses: &[super::Pat<'_>], joined_patterns: &str) -> String {
580+
crate fn pattern_not_covered_label(witnesses: &[super::Pat<'_>], joined_patterns: &str) -> String {
580581
format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
581582
}
582583

‎compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -606,8 +606,9 @@ pub(super) enum Constructor<'tcx> {
606606
/// for those types for which we cannot list constructors explicitly, like `f64` and `str`.
607607
NonExhaustive,
608608
/// Stands for constructors that are not seen in the matrix, as explained in the documentation
609-
/// for [`SplitWildcard`].
610-
Missing,
609+
/// for [`SplitWildcard`]. The carried `bool` is used for the `non_exhaustive_omitted_patterns`
610+
/// lint.
611+
Missing { nonexhaustive_enum_missing_real_variants: bool },
611612
/// Wildcard pattern.
612613
Wildcard,
613614
}
@@ -617,6 +618,10 @@ impl<'tcx> Constructor<'tcx> {
617618
matches!(self, Wildcard)
618619
}
619620

621+
pub(super) fn is_non_exhaustive(&self) -> bool {
622+
matches!(self, NonExhaustive)
623+
}
624+
620625
fn as_int_range(&self) -> Option<&IntRange> {
621626
match self {
622627
IntRange(range) => Some(range),
@@ -756,7 +761,7 @@ impl<'tcx> Constructor<'tcx> {
756761
// Wildcards cover anything
757762
(_, Wildcard) => true,
758763
// The missing ctors are not covered by anything in the matrix except wildcards.
759-
(Missing | Wildcard, _) => false,
764+
(Missing { .. } | Wildcard, _) => false,
760765

761766
(Single, Single) => true,
762767
(Variant(self_id), Variant(other_id)) => self_id == other_id,
@@ -829,7 +834,7 @@ impl<'tcx> Constructor<'tcx> {
829834
.any(|other| slice.is_covered_by(other)),
830835
// This constructor is never covered by anything else
831836
NonExhaustive => false,
832-
Str(..) | FloatRange(..) | Opaque | Missing | Wildcard => {
837+
Str(..) | FloatRange(..) | Opaque | Missing { .. } | Wildcard => {
833838
span_bug!(pcx.span, "found unexpected ctor in all_ctors: {:?}", self)
834839
}
835840
}
@@ -919,8 +924,14 @@ impl<'tcx> SplitWildcard<'tcx> {
919924
&& !cx.tcx.features().exhaustive_patterns
920925
&& !pcx.is_top_level;
921926

922-
if is_secretly_empty || is_declared_nonexhaustive {
927+
if is_secretly_empty {
923928
smallvec![NonExhaustive]
929+
} else if is_declared_nonexhaustive {
930+
def.variants
931+
.indices()
932+
.map(|idx| Variant(idx))
933+
.chain(Some(NonExhaustive))
934+
.collect()
924935
} else if cx.tcx.features().exhaustive_patterns {
925936
// If `exhaustive_patterns` is enabled, we exclude variants known to be
926937
// uninhabited.
@@ -975,6 +986,7 @@ impl<'tcx> SplitWildcard<'tcx> {
975986
// This type is one for which we cannot list constructors, like `str` or `f64`.
976987
_ => smallvec![NonExhaustive],
977988
};
989+
978990
SplitWildcard { matrix_ctors: Vec::new(), all_ctors }
979991
}
980992

@@ -1039,7 +1051,17 @@ impl<'tcx> SplitWildcard<'tcx> {
10391051
// sometimes prefer reporting the list of constructors instead of just `_`.
10401052
let report_when_all_missing = pcx.is_top_level && !IntRange::is_integral(pcx.ty);
10411053
let ctor = if !self.matrix_ctors.is_empty() || report_when_all_missing {
1042-
Missing
1054+
if pcx.is_non_exhaustive {
1055+
Missing {
1056+
nonexhaustive_enum_missing_real_variants: self
1057+
.iter_missing(pcx)
1058+
.filter(|c| !c.is_non_exhaustive())
1059+
.next()
1060+
.is_some(),
1061+
}
1062+
} else {
1063+
Missing { nonexhaustive_enum_missing_real_variants: false }
1064+
}
10431065
} else {
10441066
Wildcard
10451067
};
@@ -1176,7 +1198,12 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
11761198
}
11771199
_ => bug!("bad slice pattern {:?} {:?}", constructor, ty),
11781200
},
1179-
Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Missing
1201+
Str(..)
1202+
| FloatRange(..)
1203+
| IntRange(..)
1204+
| NonExhaustive
1205+
| Opaque
1206+
| Missing { .. }
11801207
| Wildcard => Fields::Slice(&[]),
11811208
};
11821209
debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
@@ -1189,15 +1216,18 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
11891216
/// This is roughly the inverse of `specialize_constructor`.
11901217
///
11911218
/// Examples:
1192-
/// `ctor`: `Constructor::Single`
1193-
/// `ty`: `Foo(u32, u32, u32)`
1194-
/// `self`: `[10, 20, _]`
1219+
///
1220+
/// ```text
1221+
/// ctor: `Constructor::Single`
1222+
/// ty: `Foo(u32, u32, u32)`
1223+
/// self: `[10, 20, _]`
11951224
/// returns `Foo(10, 20, _)`
11961225
///
1197-
/// `ctor`: `Constructor::Variant(Option::Some)`
1198-
/// `ty`: `Option<bool>`
1199-
/// `self`: `[false]`
1226+
/// ctor: `Constructor::Variant(Option::Some)`
1227+
/// ty: `Option<bool>`
1228+
/// self: `[false]`
12001229
/// returns `Some(false)`
1230+
/// ```
12011231
pub(super) fn apply(self, pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Pat<'tcx> {
12021232
let subpatterns_and_indices = self.patterns_and_indices();
12031233
let mut subpatterns = subpatterns_and_indices.iter().map(|&(_, p)| p).cloned();
@@ -1265,7 +1295,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
12651295
NonExhaustive => PatKind::Wild,
12661296
Wildcard => return Pat::wildcard_from_ty(pcx.ty),
12671297
Opaque => bug!("we should not try to apply an opaque constructor"),
1268-
Missing => bug!(
1298+
Missing { .. } => bug!(
12691299
"trying to apply the `Missing` constructor; this should have been done in `apply_constructors`"
12701300
),
12711301
};

‎compiler/rustc_mir_build/src/thir/pattern/usefulness.rs

Lines changed: 121 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -280,20 +280,23 @@
280280
//! The details are not necessary to understand this file, so we explain them in
281281
//! [`super::deconstruct_pat`]. Splitting is done by the [`Constructor::split`] function.
282282
283+
use self::ArmType::*;
283284
use self::Usefulness::*;
284-
use self::WitnessPreference::*;
285285

286+
use super::check_match::{joined_uncovered_patterns, pattern_not_covered_label};
286287
use super::deconstruct_pat::{Constructor, Fields, SplitWildcard};
287288
use super::{PatternFoldable, PatternFolder};
288289

289290
use rustc_data_structures::captures::Captures;
290291
use rustc_data_structures::fx::FxHashMap;
291292

293+
use hir::def_id::DefId;
294+
use hir::HirId;
292295
use rustc_arena::TypedArena;
293-
use rustc_hir::def_id::DefId;
294-
use rustc_hir::HirId;
296+
use rustc_hir as hir;
295297
use rustc_middle::thir::{Pat, PatKind};
296298
use rustc_middle::ty::{self, Ty, TyCtxt};
299+
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
297300
use rustc_span::Span;
298301

299302
use smallvec::{smallvec, SmallVec};
@@ -343,6 +346,8 @@ pub(super) struct PatCtxt<'a, 'p, 'tcx> {
343346
/// Whether the current pattern is the whole pattern as found in a match arm, or if it's a
344347
/// subpattern.
345348
pub(super) is_top_level: bool,
349+
/// Wether the current pattern is from a `non_exhaustive` enum.
350+
pub(super) is_non_exhaustive: bool,
346351
}
347352

348353
impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> {
@@ -862,7 +867,7 @@ impl<'p, 'tcx> SubPatSet<'p, 'tcx> {
862867
/// of potential unreachable sub-patterns (in the presence of or-patterns). When checking
863868
/// exhaustiveness of a whole match, we use the `WithWitnesses` variant, which carries a list of
864869
/// witnesses of non-exhaustiveness when there are any.
865-
/// Which variant to use is dictated by `WitnessPreference`.
870+
/// Which variant to use is dictated by `ArmType`.
866871
#[derive(Clone, Debug)]
867872
enum Usefulness<'p, 'tcx> {
868873
/// Carries a set of subpatterns that have been found to be reachable. If empty, this indicates
@@ -877,16 +882,24 @@ enum Usefulness<'p, 'tcx> {
877882
}
878883

879884
impl<'p, 'tcx> Usefulness<'p, 'tcx> {
880-
fn new_useful(preference: WitnessPreference) -> Self {
885+
fn new_useful(preference: ArmType) -> Self {
881886
match preference {
882-
ConstructWitness => WithWitnesses(vec![Witness(vec![])]),
883-
LeaveOutWitness => NoWitnesses(SubPatSet::full()),
887+
FakeExtraWildcard => WithWitnesses(vec![Witness(vec![])]),
888+
RealArm => NoWitnesses(SubPatSet::full()),
884889
}
885890
}
886-
fn new_not_useful(preference: WitnessPreference) -> Self {
891+
892+
fn new_not_useful(preference: ArmType) -> Self {
887893
match preference {
888-
ConstructWitness => WithWitnesses(vec![]),
889-
LeaveOutWitness => NoWitnesses(SubPatSet::empty()),
894+
FakeExtraWildcard => WithWitnesses(vec![]),
895+
RealArm => NoWitnesses(SubPatSet::empty()),
896+
}
897+
}
898+
899+
fn is_useful(&self) -> bool {
900+
match self {
901+
Usefulness::NoWitnesses(set) => !set.is_empty(),
902+
Usefulness::WithWitnesses(witnesses) => !witnesses.is_empty(),
890903
}
891904
}
892905

@@ -903,7 +916,7 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
903916

904917
/// When trying several branches and each returns a `Usefulness`, we need to combine the
905918
/// results together.
906-
fn merge(pref: WitnessPreference, usefulnesses: impl Iterator<Item = Self>) -> Self {
919+
fn merge(pref: ArmType, usefulnesses: impl Iterator<Item = Self>) -> Self {
907920
let mut ret = Self::new_not_useful(pref);
908921
for u in usefulnesses {
909922
ret.extend(u);
@@ -926,7 +939,7 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
926939
}
927940
}
928941

929-
/// After calculating usefulness after a specialization, call this to recontruct a usefulness
942+
/// After calculating usefulness after a specialization, call this to reconstruct a usefulness
930943
/// that makes sense for the matrix pre-specialization. This new usefulness can then be merged
931944
/// with the results of specializing with the other constructors.
932945
fn apply_constructor(
@@ -939,19 +952,31 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
939952
match self {
940953
WithWitnesses(witnesses) if witnesses.is_empty() => WithWitnesses(witnesses),
941954
WithWitnesses(witnesses) => {
942-
let new_witnesses = if matches!(ctor, Constructor::Missing) {
943-
let mut split_wildcard = SplitWildcard::new(pcx);
944-
split_wildcard.split(pcx, matrix.head_ctors(pcx.cx));
945-
// Construct for each missing constructor a "wild" version of this
946-
// constructor, that matches everything that can be built with
947-
// it. For example, if `ctor` is a `Constructor::Variant` for
948-
// `Option::Some`, we get the pattern `Some(_)`.
949-
let new_patterns: Vec<_> = split_wildcard
950-
.iter_missing(pcx)
951-
.map(|missing_ctor| {
952-
Fields::wildcards(pcx, missing_ctor).apply(pcx, missing_ctor)
953-
})
954-
.collect();
955+
let new_witnesses = if let Constructor::Missing { .. } = ctor {
956+
// We got the special `Missing` constructor, so each of the missing constructors
957+
// gives a new pattern that is not caught by the match. We list those patterns.
958+
let new_patterns = if pcx.is_non_exhaustive {
959+
// Here we don't want the user to try to list all variants, we want them to add
960+
// a wildcard, so we only suggest that.
961+
vec![
962+
Fields::wildcards(pcx, &Constructor::NonExhaustive)
963+
.apply(pcx, &Constructor::NonExhaustive),
964+
]
965+
} else {
966+
let mut split_wildcard = SplitWildcard::new(pcx);
967+
split_wildcard.split(pcx, matrix.head_ctors(pcx.cx));
968+
// Construct for each missing constructor a "wild" version of this
969+
// constructor, that matches everything that can be built with
970+
// it. For example, if `ctor` is a `Constructor::Variant` for
971+
// `Option::Some`, we get the pattern `Some(_)`.
972+
split_wildcard
973+
.iter_missing(pcx)
974+
.map(|missing_ctor| {
975+
Fields::wildcards(pcx, missing_ctor).apply(pcx, missing_ctor)
976+
})
977+
.collect()
978+
};
979+
955980
witnesses
956981
.into_iter()
957982
.flat_map(|witness| {
@@ -976,9 +1001,9 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
9761001
}
9771002

9781003
#[derive(Copy, Clone, Debug)]
979-
enum WitnessPreference {
980-
ConstructWitness,
981-
LeaveOutWitness,
1004+
enum ArmType {
1005+
FakeExtraWildcard,
1006+
RealArm,
9821007
}
9831008

9841009
/// A witness of non-exhaustiveness for error reporting, represented
@@ -1056,6 +1081,32 @@ impl<'tcx> Witness<'tcx> {
10561081
}
10571082
}
10581083

1084+
/// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
1085+
/// is not exhaustive enough.
1086+
///
1087+
/// NB: The partner lint for structs lives in `compiler/rustc_typeck/src/check/pat.rs`.
1088+
fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>(
1089+
cx: &MatchCheckCtxt<'p, 'tcx>,
1090+
scrut_ty: Ty<'tcx>,
1091+
sp: Span,
1092+
hir_id: HirId,
1093+
witnesses: Vec<Pat<'tcx>>,
1094+
) {
1095+
let joined_patterns = joined_uncovered_patterns(&witnesses);
1096+
cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, |build| {
1097+
let mut lint = build.build("some variants are not matched explicitly");
1098+
lint.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
1099+
lint.help(
1100+
"ensure that all variants are matched explicitly by adding the suggested match arms",
1101+
);
1102+
lint.note(&format!(
1103+
"the matched value is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found",
1104+
scrut_ty,
1105+
));
1106+
lint.emit();
1107+
});
1108+
}
1109+
10591110
/// Algorithm from <http://moscova.inria.fr/~maranget/papers/warn/index.html>.
10601111
/// The algorithm from the paper has been modified to correctly handle empty
10611112
/// types. The changes are:
@@ -1086,7 +1137,7 @@ fn is_useful<'p, 'tcx>(
10861137
cx: &MatchCheckCtxt<'p, 'tcx>,
10871138
matrix: &Matrix<'p, 'tcx>,
10881139
v: &PatStack<'p, 'tcx>,
1089-
witness_preference: WitnessPreference,
1140+
witness_preference: ArmType,
10901141
hir_id: HirId,
10911142
is_under_guard: bool,
10921143
is_top_level: bool,
@@ -1113,7 +1164,8 @@ fn is_useful<'p, 'tcx>(
11131164

11141165
// FIXME(Nadrieril): Hack to work around type normalization issues (see #72476).
11151166
let ty = matrix.heads().next().map_or(v.head().ty, |r| r.ty);
1116-
let pcx = PatCtxt { cx, ty, span: v.head().span, is_top_level };
1167+
let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty);
1168+
let pcx = PatCtxt { cx, ty, span: v.head().span, is_top_level, is_non_exhaustive };
11171169

11181170
// If the first pattern is an or-pattern, expand it.
11191171
let ret = if is_or_pat(v.head()) {
@@ -1148,6 +1200,7 @@ fn is_useful<'p, 'tcx>(
11481200
}
11491201
// We split the head constructor of `v`.
11501202
let split_ctors = v_ctor.split(pcx, matrix.head_ctors(cx));
1203+
let is_non_exhaustive_and_wild = is_non_exhaustive && v_ctor.is_wildcard();
11511204
// For each constructor, we compute whether there's a value that starts with it that would
11521205
// witness the usefulness of `v`.
11531206
let start_matrix = &matrix;
@@ -1160,10 +1213,46 @@ fn is_useful<'p, 'tcx>(
11601213
let v = v.pop_head_constructor(&ctor_wild_subpatterns);
11611214
let usefulness =
11621215
is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false);
1216+
1217+
// When all the conditions are met we have a match with a `non_exhaustive` enum
1218+
// that has the potential to trigger the `non_exhaustive_omitted_patterns` lint.
1219+
// To understand the workings checkout `Constructor::split` and `SplitWildcard::new/into_ctors`
1220+
if is_non_exhaustive_and_wild
1221+
// We check that the match has a wildcard pattern and that that wildcard is useful,
1222+
// meaning there are variants that are covered by the wildcard. Without the check
1223+
// for `witness_preference` the lint would trigger on `if let NonExhaustiveEnum::A = foo {}`
1224+
&& usefulness.is_useful() && matches!(witness_preference, RealArm)
1225+
&& matches!(
1226+
&ctor,
1227+
Constructor::Missing { nonexhaustive_enum_missing_real_variants: true }
1228+
)
1229+
{
1230+
let patterns = {
1231+
let mut split_wildcard = SplitWildcard::new(pcx);
1232+
split_wildcard.split(pcx, matrix.head_ctors(pcx.cx));
1233+
// Construct for each missing constructor a "wild" version of this
1234+
// constructor, that matches everything that can be built with
1235+
// it. For example, if `ctor` is a `Constructor::Variant` for
1236+
// `Option::Some`, we get the pattern `Some(_)`.
1237+
split_wildcard
1238+
.iter_missing(pcx)
1239+
// Filter out the `Constructor::NonExhaustive` variant it's meaningless
1240+
// to our lint
1241+
.filter(|c| !c.is_non_exhaustive())
1242+
.map(|missing_ctor| {
1243+
Fields::wildcards(pcx, missing_ctor).apply(pcx, missing_ctor)
1244+
})
1245+
.collect::<Vec<_>>()
1246+
};
1247+
1248+
lint_non_exhaustive_omitted_patterns(pcx.cx, pcx.ty, pcx.span, hir_id, patterns);
1249+
}
1250+
11631251
usefulness.apply_constructor(pcx, start_matrix, &ctor, &ctor_wild_subpatterns)
11641252
});
11651253
Usefulness::merge(witness_preference, usefulnesses)
11661254
};
1255+
11671256
debug!(?ret);
11681257
ret
11691258
}
@@ -1214,8 +1303,7 @@ crate fn compute_match_usefulness<'p, 'tcx>(
12141303
.copied()
12151304
.map(|arm| {
12161305
let v = PatStack::from_pattern(arm.pat);
1217-
let usefulness =
1218-
is_useful(cx, &matrix, &v, LeaveOutWitness, arm.hir_id, arm.has_guard, true);
1306+
let usefulness = is_useful(cx, &matrix, &v, RealArm, arm.hir_id, arm.has_guard, true);
12191307
if !arm.has_guard {
12201308
matrix.push(v);
12211309
}
@@ -1232,7 +1320,7 @@ crate fn compute_match_usefulness<'p, 'tcx>(
12321320

12331321
let wild_pattern = cx.pattern_arena.alloc(Pat::wildcard_from_ty(scrut_ty));
12341322
let v = PatStack::from_pattern(wild_pattern);
1235-
let usefulness = is_useful(cx, &matrix, &v, ConstructWitness, scrut_hir_id, false, true);
1323+
let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, scrut_hir_id, false, true);
12361324
let non_exhaustiveness_witnesses = match usefulness {
12371325
WithWitnesses(pats) => pats.into_iter().map(|w| w.single_pattern()).collect(),
12381326
NoWitnesses(_) => bug!(),

‎compiler/rustc_typeck/src/check/pat.rs

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use rustc_infer::infer;
1111
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
1212
use rustc_middle::ty::subst::GenericArg;
1313
use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeFoldable};
14+
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
1415
use rustc_span::hygiene::DesugaringKind;
1516
use rustc_span::lev_distance::find_best_match_for_name;
1617
use rustc_span::source_map::{Span, Spanned};
@@ -1261,7 +1262,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12611262
};
12621263

12631264
// Require `..` if struct has non_exhaustive attribute.
1264-
if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc {
1265+
let non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did.is_local();
1266+
if non_exhaustive && !etc {
12651267
self.error_foreign_non_exhaustive_spat(pat, adt.variant_descr(), fields.is_empty());
12661268
}
12671269

@@ -1276,24 +1278,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12761278
if etc {
12771279
tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit();
12781280
}
1279-
} else if !etc && !unmentioned_fields.is_empty() {
1281+
} else if !unmentioned_fields.is_empty() {
12801282
let accessible_unmentioned_fields: Vec<_> = unmentioned_fields
12811283
.iter()
12821284
.copied()
12831285
.filter(|(field, _)| {
12841286
field.vis.is_accessible_from(tcx.parent_module(pat.hir_id).to_def_id(), tcx)
12851287
})
12861288
.collect();
1287-
1288-
if accessible_unmentioned_fields.is_empty() {
1289-
unmentioned_err = Some(self.error_no_accessible_fields(pat, &fields));
1290-
} else {
1291-
unmentioned_err = Some(self.error_unmentioned_fields(
1292-
pat,
1293-
&accessible_unmentioned_fields,
1294-
accessible_unmentioned_fields.len() != unmentioned_fields.len(),
1295-
&fields,
1296-
));
1289+
if non_exhaustive {
1290+
self.non_exhaustive_reachable_pattern(pat, &accessible_unmentioned_fields, adt_ty)
1291+
} else if !etc {
1292+
if accessible_unmentioned_fields.is_empty() {
1293+
unmentioned_err = Some(self.error_no_accessible_fields(pat, &fields));
1294+
} else {
1295+
unmentioned_err = Some(self.error_unmentioned_fields(
1296+
pat,
1297+
&accessible_unmentioned_fields,
1298+
accessible_unmentioned_fields.len() != unmentioned_fields.len(),
1299+
&fields,
1300+
));
1301+
}
12971302
}
12981303
}
12991304
match (inexistent_fields_err, unmentioned_err) {
@@ -1604,6 +1609,51 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16041609
err
16051610
}
16061611

1612+
/// Report that a pattern for a `#[non_exhaustive]` struct marked with `non_exhaustive_omitted_patterns`
1613+
/// is not exhaustive enough.
1614+
///
1615+
/// Nb: the partner lint for enums lives in `compiler/rustc_mir_build/src/thir/pattern/usefulness.rs`.
1616+
fn non_exhaustive_reachable_pattern(
1617+
&self,
1618+
pat: &Pat<'_>,
1619+
unmentioned_fields: &[(&ty::FieldDef, Ident)],
1620+
ty: Ty<'tcx>,
1621+
) {
1622+
fn joined_uncovered_patterns(witnesses: &[&Ident]) -> String {
1623+
const LIMIT: usize = 3;
1624+
match witnesses {
1625+
[] => bug!(),
1626+
[witness] => format!("`{}`", witness),
1627+
[head @ .., tail] if head.len() < LIMIT => {
1628+
let head: Vec<_> = head.iter().map(<_>::to_string).collect();
1629+
format!("`{}` and `{}`", head.join("`, `"), tail)
1630+
}
1631+
_ => {
1632+
let (head, tail) = witnesses.split_at(LIMIT);
1633+
let head: Vec<_> = head.iter().map(<_>::to_string).collect();
1634+
format!("`{}` and {} more", head.join("`, `"), tail.len())
1635+
}
1636+
}
1637+
}
1638+
let joined_patterns = joined_uncovered_patterns(
1639+
&unmentioned_fields.iter().map(|(_, i)| i).collect::<Vec<_>>(),
1640+
);
1641+
1642+
self.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, |build| {
1643+
let mut lint = build.build("some fields are not explicitly listed");
1644+
lint.span_label(pat.span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns));
1645+
1646+
lint.help(
1647+
"ensure that all fields are mentioned explicitly by adding the suggested fields",
1648+
);
1649+
lint.note(&format!(
1650+
"the pattern is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found",
1651+
ty,
1652+
));
1653+
lint.emit();
1654+
});
1655+
}
1656+
16071657
/// Returns a diagnostic reporting a struct pattern which does not mention some fields.
16081658
///
16091659
/// ```text

‎src/test/ui/rfc-2008-non-exhaustive/auxiliary/enums.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,29 @@
44
pub enum NonExhaustiveEnum {
55
Unit,
66
Tuple(u32),
7-
Struct { field: u32 }
7+
Struct { field: u32 },
8+
}
9+
10+
#[non_exhaustive]
11+
pub enum NestedNonExhaustive {
12+
A(NonExhaustiveEnum),
13+
B,
14+
C,
815
}
916

1017
#[non_exhaustive]
1118
pub enum EmptyNonExhaustiveEnum {}
19+
20+
pub enum VariantNonExhaustive {
21+
#[non_exhaustive]
22+
Bar {
23+
x: u32,
24+
y: u64,
25+
},
26+
Baz(u32, u16),
27+
}
28+
29+
#[non_exhaustive]
30+
pub enum NonExhaustiveSingleVariant {
31+
A(bool),
32+
}

‎src/test/ui/rfc-2008-non-exhaustive/auxiliary/structs.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#[derive(Default)]
12
#[non_exhaustive]
23
pub struct NormalStruct {
34
pub first_field: u16,
@@ -15,11 +16,18 @@ pub struct TupleStruct(pub u16, pub u16);
1516
pub struct FunctionalRecord {
1617
pub first_field: u16,
1718
pub second_field: u16,
18-
pub third_field: bool
19+
pub third_field: bool,
1920
}
2021

2122
impl Default for FunctionalRecord {
2223
fn default() -> FunctionalRecord {
2324
FunctionalRecord { first_field: 640, second_field: 480, third_field: false }
2425
}
2526
}
27+
28+
#[derive(Default)]
29+
#[non_exhaustive]
30+
pub struct NestedStruct {
31+
pub foo: u16,
32+
pub bar: NormalStruct,
33+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Test that the `non_exhaustive_omitted_patterns` lint is triggered correctly.
2+
3+
// aux-build:enums.rs
4+
extern crate enums;
5+
6+
// aux-build:structs.rs
7+
extern crate structs;
8+
9+
use enums::{
10+
EmptyNonExhaustiveEnum, NestedNonExhaustive, NonExhaustiveEnum, NonExhaustiveSingleVariant,
11+
VariantNonExhaustive,
12+
};
13+
use structs::{FunctionalRecord, NestedStruct, NormalStruct};
14+
15+
#[non_exhaustive]
16+
#[derive(Default)]
17+
pub struct Foo {
18+
a: u8,
19+
b: usize,
20+
c: String,
21+
}
22+
23+
#[non_exhaustive]
24+
pub enum Bar {
25+
A,
26+
B,
27+
C,
28+
}
29+
30+
fn main() {
31+
let enumeration = Bar::A;
32+
33+
// Ok: this is a crate local non_exhaustive enum
34+
match enumeration {
35+
Bar::A => {}
36+
Bar::B => {}
37+
#[deny(non_exhaustive_omitted_patterns)]
38+
_ => {}
39+
}
40+
41+
let non_enum = NonExhaustiveEnum::Unit;
42+
43+
// Ok: without the attribute
44+
match non_enum {
45+
NonExhaustiveEnum::Unit => {}
46+
NonExhaustiveEnum::Tuple(_) => {}
47+
_ => {}
48+
}
49+
50+
match non_enum {
51+
NonExhaustiveEnum::Unit => {}
52+
NonExhaustiveEnum::Tuple(_) => {}
53+
#[deny(non_exhaustive_omitted_patterns)]
54+
_ => {}
55+
}
56+
//~^^ some variants are not matched explicitly
57+
58+
match non_enum {
59+
NonExhaustiveEnum::Unit | NonExhaustiveEnum::Struct { .. } => {}
60+
#[deny(non_exhaustive_omitted_patterns)]
61+
_ => {}
62+
}
63+
//~^^ some variants are not matched explicitly
64+
65+
let x = 5;
66+
match non_enum {
67+
NonExhaustiveEnum::Unit if x > 10 => {}
68+
NonExhaustiveEnum::Tuple(_) => {}
69+
NonExhaustiveEnum::Struct { .. } => {}
70+
#[deny(non_exhaustive_omitted_patterns)]
71+
_ => {}
72+
}
73+
//~^^ some variants are not matched explicitly
74+
75+
// Ok: all covered and not `unreachable-patterns`
76+
#[deny(unreachable_patterns)]
77+
match non_enum {
78+
NonExhaustiveEnum::Unit => {}
79+
NonExhaustiveEnum::Tuple(_) => {}
80+
NonExhaustiveEnum::Struct { .. } => {}
81+
#[deny(non_exhaustive_omitted_patterns)]
82+
_ => {}
83+
}
84+
85+
#[deny(non_exhaustive_omitted_patterns)]
86+
match NestedNonExhaustive::B {
87+
NestedNonExhaustive::A(NonExhaustiveEnum::Unit) => {}
88+
NestedNonExhaustive::A(_) => {}
89+
NestedNonExhaustive::B => {}
90+
_ => {}
91+
}
92+
//~^^ some variants are not matched explicitly
93+
//~^^^^^ some variants are not matched explicitly
94+
95+
// The io::ErrorKind has many `unstable` fields how do they interact with this
96+
// lint
97+
#[deny(non_exhaustive_omitted_patterns)]
98+
match std::io::ErrorKind::Other {
99+
std::io::ErrorKind::NotFound => {}
100+
std::io::ErrorKind::PermissionDenied => {}
101+
std::io::ErrorKind::ConnectionRefused => {}
102+
std::io::ErrorKind::ConnectionReset => {}
103+
std::io::ErrorKind::ConnectionAborted => {}
104+
std::io::ErrorKind::NotConnected => {}
105+
std::io::ErrorKind::AddrInUse => {}
106+
std::io::ErrorKind::AddrNotAvailable => {}
107+
std::io::ErrorKind::BrokenPipe => {}
108+
std::io::ErrorKind::AlreadyExists => {}
109+
std::io::ErrorKind::WouldBlock => {}
110+
std::io::ErrorKind::InvalidInput => {}
111+
std::io::ErrorKind::InvalidData => {}
112+
std::io::ErrorKind::TimedOut => {}
113+
std::io::ErrorKind::WriteZero => {}
114+
std::io::ErrorKind::Interrupted => {}
115+
std::io::ErrorKind::Other => {}
116+
std::io::ErrorKind::UnexpectedEof => {}
117+
std::io::ErrorKind::Unsupported => {}
118+
std::io::ErrorKind::OutOfMemory => {}
119+
// All stable variants are above and unstable in `_`
120+
_ => {}
121+
}
122+
//~^^ some variants are not matched explicitly
123+
124+
#[warn(non_exhaustive_omitted_patterns)]
125+
match VariantNonExhaustive::Baz(1, 2) {
126+
VariantNonExhaustive::Baz(_, _) => {}
127+
VariantNonExhaustive::Bar { x, .. } => {}
128+
}
129+
//~^^ some fields are not explicitly listed
130+
131+
#[warn(non_exhaustive_omitted_patterns)]
132+
let FunctionalRecord { first_field, second_field, .. } = FunctionalRecord::default();
133+
//~^ some fields are not explicitly listed
134+
135+
// Ok: this is local
136+
#[warn(non_exhaustive_omitted_patterns)]
137+
let Foo { a, b, .. } = Foo::default();
138+
139+
#[warn(non_exhaustive_omitted_patterns)]
140+
let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default();
141+
//~^ some fields are not explicitly listed
142+
//~^^ some fields are not explicitly listed
143+
144+
// Ok: because this only has 1 variant
145+
#[deny(non_exhaustive_omitted_patterns)]
146+
match NonExhaustiveSingleVariant::A(true) {
147+
NonExhaustiveSingleVariant::A(true) => {}
148+
_ => {}
149+
}
150+
151+
#[deny(non_exhaustive_omitted_patterns)]
152+
match NonExhaustiveSingleVariant::A(true) {
153+
_ => {}
154+
}
155+
//~^^ some variants are not matched explicitly
156+
157+
// Ok: we don't lint on `if let` expressions
158+
#[deny(non_exhaustive_omitted_patterns)]
159+
if let NonExhaustiveEnum::Tuple(_) = non_enum {}
160+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
warning: some fields are not explicitly listed
2+
--> $DIR/reachable-patterns.rs:127:9
3+
|
4+
LL | VariantNonExhaustive::Bar { x, .. } => {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `y` not listed
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/reachable-patterns.rs:124:12
9+
|
10+
LL | #[warn(non_exhaustive_omitted_patterns)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
= help: ensure that all fields are mentioned explicitly by adding the suggested fields
13+
= note: the pattern is of type `VariantNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found
14+
15+
warning: some fields are not explicitly listed
16+
--> $DIR/reachable-patterns.rs:132:9
17+
|
18+
LL | let FunctionalRecord { first_field, second_field, .. } = FunctionalRecord::default();
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `third_field` not listed
20+
|
21+
note: the lint level is defined here
22+
--> $DIR/reachable-patterns.rs:131:12
23+
|
24+
LL | #[warn(non_exhaustive_omitted_patterns)]
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
= help: ensure that all fields are mentioned explicitly by adding the suggested fields
27+
= note: the pattern is of type `FunctionalRecord` and the `non_exhaustive_omitted_patterns` attribute was found
28+
29+
warning: some fields are not explicitly listed
30+
--> $DIR/reachable-patterns.rs:140:29
31+
|
32+
LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default();
33+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `second_field` not listed
34+
|
35+
note: the lint level is defined here
36+
--> $DIR/reachable-patterns.rs:139:12
37+
|
38+
LL | #[warn(non_exhaustive_omitted_patterns)]
39+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40+
= help: ensure that all fields are mentioned explicitly by adding the suggested fields
41+
= note: the pattern is of type `NormalStruct` and the `non_exhaustive_omitted_patterns` attribute was found
42+
43+
warning: some fields are not explicitly listed
44+
--> $DIR/reachable-patterns.rs:140:9
45+
|
46+
LL | let NestedStruct { bar: NormalStruct { first_field, .. }, .. } = NestedStruct::default();
47+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `foo` not listed
48+
|
49+
= help: ensure that all fields are mentioned explicitly by adding the suggested fields
50+
= note: the pattern is of type `NestedStruct` and the `non_exhaustive_omitted_patterns` attribute was found
51+
52+
error: some variants are not matched explicitly
53+
--> $DIR/reachable-patterns.rs:54:9
54+
|
55+
LL | _ => {}
56+
| ^ pattern `Struct { .. }` not covered
57+
|
58+
note: the lint level is defined here
59+
--> $DIR/reachable-patterns.rs:53:16
60+
|
61+
LL | #[deny(non_exhaustive_omitted_patterns)]
62+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
63+
= help: ensure that all variants are matched explicitly by adding the suggested match arms
64+
= note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
65+
66+
error: some variants are not matched explicitly
67+
--> $DIR/reachable-patterns.rs:61:9
68+
|
69+
LL | _ => {}
70+
| ^ pattern `Tuple(_)` not covered
71+
|
72+
note: the lint level is defined here
73+
--> $DIR/reachable-patterns.rs:60:16
74+
|
75+
LL | #[deny(non_exhaustive_omitted_patterns)]
76+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
77+
= help: ensure that all variants are matched explicitly by adding the suggested match arms
78+
= note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
79+
80+
error: some variants are not matched explicitly
81+
--> $DIR/reachable-patterns.rs:71:9
82+
|
83+
LL | _ => {}
84+
| ^ pattern `Unit` not covered
85+
|
86+
note: the lint level is defined here
87+
--> $DIR/reachable-patterns.rs:70:16
88+
|
89+
LL | #[deny(non_exhaustive_omitted_patterns)]
90+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
91+
= help: ensure that all variants are matched explicitly by adding the suggested match arms
92+
= note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
93+
94+
error: some variants are not matched explicitly
95+
--> $DIR/reachable-patterns.rs:88:32
96+
|
97+
LL | NestedNonExhaustive::A(_) => {}
98+
| ^ patterns `Tuple(_)` and `Struct { .. }` not covered
99+
|
100+
note: the lint level is defined here
101+
--> $DIR/reachable-patterns.rs:85:12
102+
|
103+
LL | #[deny(non_exhaustive_omitted_patterns)]
104+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
105+
= help: ensure that all variants are matched explicitly by adding the suggested match arms
106+
= note: the matched value is of type `NonExhaustiveEnum` and the `non_exhaustive_omitted_patterns` attribute was found
107+
108+
error: some variants are not matched explicitly
109+
--> $DIR/reachable-patterns.rs:90:9
110+
|
111+
LL | _ => {}
112+
| ^ pattern `C` not covered
113+
|
114+
= help: ensure that all variants are matched explicitly by adding the suggested match arms
115+
= note: the matched value is of type `NestedNonExhaustive` and the `non_exhaustive_omitted_patterns` attribute was found
116+
117+
error: some variants are not matched explicitly
118+
--> $DIR/reachable-patterns.rs:120:9
119+
|
120+
LL | _ => {}
121+
| ^ patterns `HostUnreachable`, `NetworkUnreachable`, `NetworkDown` and 18 more not covered
122+
|
123+
note: the lint level is defined here
124+
--> $DIR/reachable-patterns.rs:97:12
125+
|
126+
LL | #[deny(non_exhaustive_omitted_patterns)]
127+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
128+
= help: ensure that all variants are matched explicitly by adding the suggested match arms
129+
= note: the matched value is of type `ErrorKind` and the `non_exhaustive_omitted_patterns` attribute was found
130+
131+
error: some variants are not matched explicitly
132+
--> $DIR/reachable-patterns.rs:153:9
133+
|
134+
LL | _ => {}
135+
| ^ pattern `A(_)` not covered
136+
|
137+
note: the lint level is defined here
138+
--> $DIR/reachable-patterns.rs:151:12
139+
|
140+
LL | #[deny(non_exhaustive_omitted_patterns)]
141+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
142+
= help: ensure that all variants are matched explicitly by adding the suggested match arms
143+
= note: the matched value is of type `NonExhaustiveSingleVariant` and the `non_exhaustive_omitted_patterns` attribute was found
144+
145+
error: aborting due to 7 previous errors; 4 warnings emitted
146+

‎src/test/ui/rfc-2008-non-exhaustive/struct.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ error[E0603]: tuple struct constructor `TupleStruct` is private
1616
LL | let ts_explicit = structs::TupleStruct(640, 480);
1717
| ^^^^^^^^^^^ private tuple struct constructor
1818
|
19-
::: $DIR/auxiliary/structs.rs:11:24
19+
::: $DIR/auxiliary/structs.rs:12:24
2020
|
2121
LL | pub struct TupleStruct(pub u16, pub u16);
2222
| ---------------- a constructor is private if any of the fields is private
2323
|
2424
note: the tuple struct constructor `TupleStruct` is defined here
25-
--> $DIR/auxiliary/structs.rs:11:1
25+
--> $DIR/auxiliary/structs.rs:12:1
2626
|
2727
LL | pub struct TupleStruct(pub u16, pub u16);
2828
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -34,7 +34,7 @@ LL | let us_explicit = structs::UnitStruct;
3434
| ^^^^^^^^^^ private unit struct
3535
|
3636
note: the unit struct `UnitStruct` is defined here
37-
--> $DIR/auxiliary/structs.rs:8:1
37+
--> $DIR/auxiliary/structs.rs:9:1
3838
|
3939
LL | pub struct UnitStruct;
4040
| ^^^^^^^^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)
Please sign in to comment.