rustc_lint/types/
improper_ctypes.rs

1use std::ops::ControlFlow;
2
3use rustc_errors::DiagMessage;
4use rustc_hir::def::CtorKind;
5use rustc_middle::ty;
6
7use 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) }.
17pub(crate) fn check_non_exhaustive_variant(
18    non_exhaustive_variant_list: 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_exhaustive_variant_list {
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    if variant.field_list_has_applicable_non_exhaustive() {
34        return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive_variant);
35    }
36
37    ControlFlow::Continue(())
38}
39
40fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool {
41    // CtorKind::Const means a "unit" ctor
42    !matches!(variant.ctor_kind(), Some(CtorKind::Const))
43}