|
| 1 | +use std::ops::ControlFlow; |
| 2 | + |
| 3 | +use rustc_errors::DiagMessage; |
| 4 | +use rustc_hir::def::CtorKind; |
| 5 | +use rustc_middle::ty; |
| 6 | + |
| 7 | +use crate::fluent_generated as fluent; |
| 8 | + |
| 9 | +/// Check a variant of a non-exhaustive enum for improper ctypes |
| 10 | +/// |
| 11 | +/// We treat `#[non_exhaustive] enum` as "ensure that code will compile if new variants are added". |
| 12 | +/// This includes linting, on a best-effort basis. There are valid additions that are unlikely. |
| 13 | +/// |
| 14 | +/// Adding a data-carrying variant to an existing C-like enum that is passed to C is "unlikely", |
| 15 | +/// so we don't need the lint to account for it. |
| 16 | +/// e.g. going from enum Foo { A, B, C } to enum Foo { A, B, C, D(u32) }. |
| 17 | +pub(crate) fn check_non_exhaustive_variant( |
| 18 | + non_local_def: bool, |
| 19 | + variant: &ty::VariantDef, |
| 20 | +) -> ControlFlow<DiagMessage, ()> { |
| 21 | + // non_exhaustive suggests it is possible that someone might break ABI |
| 22 | + // see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344 |
| 23 | + // so warn on complex enums being used outside their crate |
| 24 | + if non_local_def { |
| 25 | + // which is why we only warn about really_tagged_union reprs from https://rust.tf/rfc2195 |
| 26 | + // with an enum like `#[repr(u8)] enum Enum { A(DataA), B(DataB), }` |
| 27 | + // but exempt enums with unit ctors like C's (e.g. from rust-bindgen) |
| 28 | + if variant_has_complex_ctor(variant) { |
| 29 | + return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive); |
| 30 | + } |
| 31 | + } |
| 32 | + |
| 33 | + let non_exhaustive_variant_fields = variant.is_field_list_non_exhaustive(); |
| 34 | + if non_exhaustive_variant_fields && !variant.def_id.is_local() { |
| 35 | + return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive_variant); |
| 36 | + } |
| 37 | + |
| 38 | + ControlFlow::Continue(()) |
| 39 | +} |
| 40 | + |
| 41 | +fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool { |
| 42 | + // CtorKind::Const means a "unit" ctor |
| 43 | + !matches!(variant.ctor_kind(), Some(CtorKind::Const)) |
| 44 | +} |
| 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 | +} |
0 commit comments