Skip to content

Commit 61f7000

Browse files
committed
Add helper methods checking for "#[non_exhaustive] that's active"
A check for `#[non_exhaustive]` is often done in combination with checking whether the type is local to the crate, in a variety of ways. Create a helper method and standardize on it as the way to check for this.
1 parent 59a9b9e commit 61f7000

File tree

15 files changed

+46
-46
lines changed

15 files changed

+46
-46
lines changed

compiler/rustc_hir_typeck/src/cast.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ use rustc_middle::ty::error::TypeError;
4242
use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, VariantDef};
4343
use rustc_middle::{bug, span_bug};
4444
use rustc_session::lint;
45-
use rustc_span::def_id::LOCAL_CRATE;
4645
use rustc_span::{DUMMY_SP, Span, sym};
4746
use rustc_trait_selection::infer::InferCtxtExt;
4847
use rustc_type_ir::elaborate;
@@ -789,7 +788,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
789788
_ => return Err(CastError::NonScalar),
790789
};
791790
if let ty::Adt(adt_def, _) = *self.expr_ty.kind()
792-
&& adt_def.did().krate != LOCAL_CRATE
791+
&& !adt_def.did().is_local()
793792
&& adt_def.variants().iter().any(VariantDef::is_field_list_non_exhaustive)
794793
{
795794
return Err(CastError::ForeignNonExhaustiveAdt);

compiler/rustc_hir_typeck/src/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1966,7 +1966,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19661966

19671967
// Prohibit struct expressions when non-exhaustive flag is set.
19681968
let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type");
1969-
if !adt.did().is_local() && variant.is_field_list_non_exhaustive() {
1969+
if variant.field_list_has_applicable_non_exhaustive() {
19701970
self.dcx()
19711971
.emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() });
19721972
}

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

+2-9
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_middle::hir::place::ProjectionKind;
2020
pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection};
2121
use rustc_middle::mir::FakeReadCause;
2222
use rustc_middle::ty::{
23-
self, AdtKind, BorrowKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, adjustment,
23+
self, BorrowKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, adjustment,
2424
};
2525
use rustc_middle::{bug, span_bug};
2626
use rustc_span::{ErrorGuaranteed, Span};
@@ -1834,14 +1834,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
18341834
// to assume that more cases will be added to the variant in the future. This mean
18351835
// that we should handle non-exhaustive SingleVariant the same way we would handle
18361836
// a MultiVariant.
1837-
// If the variant is not local it must be defined in another crate.
1838-
let is_non_exhaustive = match def.adt_kind() {
1839-
AdtKind::Struct | AdtKind::Union => {
1840-
def.non_enum_variant().is_field_list_non_exhaustive()
1841-
}
1842-
AdtKind::Enum => def.is_variant_list_non_exhaustive(),
1843-
};
1844-
def.variants().len() > 1 || (!def.did().is_local() && is_non_exhaustive)
1837+
def.variants().len() > 1 || def.variant_list_has_applicable_non_exhaustive()
18451838
} else {
18461839
false
18471840
}

compiler/rustc_hir_typeck/src/pat.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1722,7 +1722,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17221722
};
17231723

17241724
// Require `..` if struct has non_exhaustive attribute.
1725-
let non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did().is_local();
1725+
let non_exhaustive = variant.field_list_has_applicable_non_exhaustive();
17261726
if non_exhaustive && !has_rest_pat {
17271727
self.error_foreign_non_exhaustive_spat(pat, adt.variant_descr(), fields.is_empty());
17281728
}

compiler/rustc_lint/src/types.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -1193,9 +1193,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11931193
};
11941194
}
11951195

1196-
let is_non_exhaustive =
1197-
def.non_enum_variant().is_field_list_non_exhaustive();
1198-
if is_non_exhaustive && !def.did().is_local() {
1196+
if def.non_enum_variant().field_list_has_applicable_non_exhaustive() {
11991197
return FfiUnsafe {
12001198
ty,
12011199
reason: if def.is_struct() {
@@ -1248,14 +1246,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12481246
};
12491247
}
12501248

1251-
use improper_ctypes::{
1252-
check_non_exhaustive_variant, non_local_and_non_exhaustive,
1253-
};
1249+
use improper_ctypes::check_non_exhaustive_variant;
12541250

1255-
let non_local_def = non_local_and_non_exhaustive(def);
1251+
let non_exhaustive = def.variant_list_has_applicable_non_exhaustive();
12561252
// Check the contained variants.
12571253
let ret = def.variants().iter().try_for_each(|variant| {
1258-
check_non_exhaustive_variant(non_local_def, variant)
1254+
check_non_exhaustive_variant(non_exhaustive, variant)
12591255
.map_break(|reason| FfiUnsafe { ty, reason, help: None })?;
12601256

12611257
match self.check_variant_for_ffi(acc, ty, def, variant, args) {

compiler/rustc_lint/src/types/improper_ctypes.rs

+3-11
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ use crate::fluent_generated as fluent;
1515
/// so we don't need the lint to account for it.
1616
/// e.g. going from enum Foo { A, B, C } to enum Foo { A, B, C, D(u32) }.
1717
pub(crate) fn check_non_exhaustive_variant(
18-
non_local_def: bool,
18+
non_exhaustive_variant_list: bool,
1919
variant: &ty::VariantDef,
2020
) -> ControlFlow<DiagMessage, ()> {
2121
// non_exhaustive suggests it is possible that someone might break ABI
2222
// see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344
2323
// so warn on complex enums being used outside their crate
24-
if non_local_def {
24+
if non_exhaustive_variant_list {
2525
// which is why we only warn about really_tagged_union reprs from https://rust.tf/rfc2195
2626
// with an enum like `#[repr(u8)] enum Enum { A(DataA), B(DataB), }`
2727
// but exempt enums with unit ctors like C's (e.g. from rust-bindgen)
@@ -30,8 +30,7 @@ pub(crate) fn check_non_exhaustive_variant(
3030
}
3131
}
3232

33-
let non_exhaustive_variant_fields = variant.is_field_list_non_exhaustive();
34-
if non_exhaustive_variant_fields && !variant.def_id.is_local() {
33+
if variant.field_list_has_applicable_non_exhaustive() {
3534
return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive_variant);
3635
}
3736

@@ -42,10 +41,3 @@ fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool {
4241
// CtorKind::Const means a "unit" ctor
4342
!matches!(variant.ctor_kind(), Some(CtorKind::Const))
4443
}
45-
46-
// non_exhaustive suggests it is possible that someone might break ABI
47-
// see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344
48-
// so warn on complex enums being used outside their crate
49-
pub(crate) fn non_local_and_non_exhaustive(def: ty::AdtDef<'_>) -> bool {
50-
def.is_variant_list_non_exhaustive() && !def.did().is_local()
51-
}

compiler/rustc_middle/src/ty/adt.rs

+11
Original file line numberDiff line numberDiff line change
@@ -329,11 +329,22 @@ impl<'tcx> AdtDef<'tcx> {
329329
}
330330

331331
/// Returns `true` if the variant list of this ADT is `#[non_exhaustive]`.
332+
///
333+
/// Note that this function will return `true` even if the ADT has been
334+
/// defined in the crate currently being compiled. If that's not what you
335+
/// want, see [`Self::variant_list_has_applicable_non_exhaustive`].
332336
#[inline]
333337
pub fn is_variant_list_non_exhaustive(self) -> bool {
334338
self.flags().contains(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE)
335339
}
336340

341+
/// Returns `true` if the variant list of this ADT is `#[non_exhaustive]`
342+
/// and has been defined in another crate.
343+
#[inline]
344+
pub fn variant_list_has_applicable_non_exhaustive(self) -> bool {
345+
self.is_variant_list_non_exhaustive() && !self.did().is_local()
346+
}
347+
337348
/// Returns the kind of the ADT.
338349
#[inline]
339350
pub fn adt_kind(self) -> AdtKind {

compiler/rustc_middle/src/ty/inhabitedness/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ impl<'tcx> Ty<'tcx> {
107107
// For now, unions are always considered inhabited
108108
Adt(adt, _) if adt.is_union() => InhabitedPredicate::True,
109109
// Non-exhaustive ADTs from other crates are always considered inhabited
110-
Adt(adt, _) if adt.is_variant_list_non_exhaustive() && !adt.did().is_local() => {
110+
Adt(adt, _) if adt.variant_list_has_applicable_non_exhaustive() => {
111111
InhabitedPredicate::True
112112
}
113113
Never => InhabitedPredicate::False,

compiler/rustc_middle/src/ty/mod.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -1206,12 +1206,23 @@ impl VariantDef {
12061206
}
12071207
}
12081208

1209-
/// Is this field list non-exhaustive?
1209+
/// Returns `true` if the field list of this variant is `#[non_exhaustive]`.
1210+
///
1211+
/// Note that this function will return `true` even if the type has been
1212+
/// defined in the crate currently being compiled. If that's not what you
1213+
/// want, see [`Self::field_list_has_applicable_non_exhaustive`].
12101214
#[inline]
12111215
pub fn is_field_list_non_exhaustive(&self) -> bool {
12121216
self.flags.intersects(VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE)
12131217
}
12141218

1219+
/// Returns `true` if the field list of this variant is `#[non_exhaustive]`
1220+
/// and the type has been defined in another crate.
1221+
#[inline]
1222+
pub fn field_list_has_applicable_non_exhaustive(&self) -> bool {
1223+
self.is_field_list_non_exhaustive() && !self.def_id.is_local()
1224+
}
1225+
12151226
/// Computes the `Ident` of this variant by looking up the `Span`
12161227
pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident {
12171228
Ident::new(self.name, tcx.def_ident_span(self.def_id).unwrap())

compiler/rustc_mir_build/src/builder/matches/match_pair.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,7 @@ impl<'tcx> MatchPairTree<'tcx> {
277277
.inhabited_predicate(cx.tcx, adt_def)
278278
.instantiate(cx.tcx, args)
279279
.apply_ignore_module(cx.tcx, cx.infcx.typing_env(cx.param_env))
280-
}) && (adt_def.did().is_local()
281-
|| !adt_def.is_variant_list_non_exhaustive());
280+
}) && !adt_def.variant_list_has_applicable_non_exhaustive();
282281
if irrefutable { None } else { Some(TestCase::Variant { adt_def, variant_index }) }
283282
}
284283

compiler/rustc_mir_build/src/errors.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -613,9 +613,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNo
613613
diag.span_note(span, fluent::mir_build_def_note);
614614
}
615615

616-
let is_variant_list_non_exhaustive = matches!(self.ty.kind(),
617-
ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local());
618-
if is_variant_list_non_exhaustive {
616+
let is_non_exhaustive = matches!(self.ty.kind(),
617+
ty::Adt(def, _) if def.variant_list_has_applicable_non_exhaustive());
618+
if is_non_exhaustive {
619619
diag.note(fluent::mir_build_non_exhaustive_type_note);
620620
} else {
621621
diag.note(fluent::mir_build_type_note);

compiler/rustc_pattern_analysis/src/rustc.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
150150
/// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
151151
pub fn is_foreign_non_exhaustive_enum(&self, ty: RevealedTy<'tcx>) -> bool {
152152
match ty.kind() {
153-
ty::Adt(def, ..) => {
154-
def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did().is_local()
155-
}
153+
ty::Adt(def, ..) => def.variant_list_has_applicable_non_exhaustive(),
156154
_ => false,
157155
}
158156
}

src/tools/clippy/clippy_lints/src/default.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
134134
&& let ty::Adt(adt, args) = *binding_type.kind()
135135
&& adt.is_struct()
136136
&& let variant = adt.non_enum_variant()
137-
&& (adt.did().is_local() || !variant.is_field_list_non_exhaustive())
137+
&& !variant.field_list_has_applicable_non_exhaustive()
138138
&& let module_did = cx.tcx.parent_module(stmt.hir_id)
139139
&& variant
140140
.fields

src/tools/clippy/clippy_lints/src/needless_update.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate {
5454
if let ExprKind::Struct(_, fields, StructTailExpr::Base(base)) = expr.kind {
5555
let ty = cx.typeck_results().expr_ty(expr);
5656
if let ty::Adt(def, _) = ty.kind() {
57-
if fields.len() == def.non_enum_variant().fields.len()
58-
&& !def.variant(0_usize.into()).is_field_list_non_exhaustive()
57+
let variant = def.non_enum_variant();
58+
if fields.len() == variant.fields.len()
59+
&& !variant.is_field_list_non_exhaustive()
5960
{
6061
span_lint(
6162
cx,

src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl LateLintPass<'_> for UnneededStructPattern {
5151
let variant = cx.tcx.adt_def(enum_did).variant_with_id(did);
5252

5353
let has_only_fields_brackets = variant.ctor.is_some() && variant.fields.is_empty();
54-
let non_exhaustive_activated = !variant.def_id.is_local() && variant.is_field_list_non_exhaustive();
54+
let non_exhaustive_activated = variant.field_list_has_applicable_non_exhaustive();
5555
if !has_only_fields_brackets || non_exhaustive_activated {
5656
return;
5757
}

0 commit comments

Comments
 (0)