Skip to content

Commit 33a06b7

Browse files
committedSep 14, 2021
Add reachable_patterns lint to rfc-2008-non_exhaustive
Add linting on non_exhaustive structs and enum variants Add ui tests for non_exhaustive reachable lint Rename to non_exhaustive_omitted_patterns and avoid triggering on if let
·
1.88.01.57.0
1 parent c3c0f80 commit 33a06b7

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.