Skip to content

Commit 1661421

Browse files
authored
Add support for non_exhaustive rustified enums. (#1575)
Implements the feature discussed in #1554.
2 parents 6aa5b2b + 2a52230 commit 1661421

File tree

8 files changed

+92
-28
lines changed

8 files changed

+92
-28
lines changed

src/codegen/helpers.rs

+6
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ pub mod attributes {
4242
}
4343
}
4444

45+
pub fn non_exhaustive() -> TokenStream {
46+
quote! {
47+
#[non_exhaustive]
48+
}
49+
}
50+
4551
pub fn doc(comment: String) -> TokenStream {
4652
// NOTE(emilio): By this point comments are already preprocessed and in
4753
// `///` form. Quote turns them into `#[doc]` comments, but oh well.

src/codegen/mod.rs

+27-21
Original file line numberDiff line numberDiff line change
@@ -2170,7 +2170,10 @@ impl MethodCodegen for Method {
21702170
#[derive(Copy, Clone, PartialEq, Debug)]
21712171
pub enum EnumVariation {
21722172
/// The code for this enum will use a Rust enum
2173-
Rust,
2173+
Rust {
2174+
/// Indicates whether the generated struct should be #[non_exhaustive]
2175+
non_exhaustive: bool
2176+
},
21742177
/// The code for this enum will use a bitfield
21752178
Bitfield,
21762179
/// The code for this enum will use consts
@@ -2182,14 +2185,7 @@ pub enum EnumVariation {
21822185
impl EnumVariation {
21832186
fn is_rust(&self) -> bool {
21842187
match *self {
2185-
EnumVariation::Rust => true,
2186-
_ => false
2187-
}
2188-
}
2189-
2190-
fn is_bitfield(&self) -> bool {
2191-
match *self {
2192-
EnumVariation::Bitfield {..} => true,
2188+
EnumVariation::Rust{ .. } => true,
21932189
_ => false
21942190
}
21952191
}
@@ -2216,13 +2212,14 @@ impl std::str::FromStr for EnumVariation {
22162212
/// Create a `EnumVariation` from a string.
22172213
fn from_str(s: &str) -> Result<Self, Self::Err> {
22182214
match s {
2219-
"rust" => Ok(EnumVariation::Rust),
2215+
"rust" => Ok(EnumVariation::Rust{ non_exhaustive: false }),
2216+
"rust_non_exhaustive" => Ok(EnumVariation::Rust{ non_exhaustive: true }),
22202217
"bitfield" => Ok(EnumVariation::Bitfield),
22212218
"consts" => Ok(EnumVariation::Consts),
22222219
"moduleconsts" => Ok(EnumVariation::ModuleConsts),
22232220
_ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput,
22242221
concat!("Got an invalid EnumVariation. Accepted values ",
2225-
"are 'rust', 'bitfield', 'consts', and ",
2222+
"are 'rust', 'rust_non_exhaustive', 'bitfield', 'consts', and ",
22262223
"'moduleconsts'."))),
22272224
}
22282225
}
@@ -2288,7 +2285,7 @@ impl<'a> EnumBuilder<'a> {
22882285
}
22892286
}
22902287

2291-
EnumVariation::Rust => {
2288+
EnumVariation::Rust { .. } => {
22922289
let tokens = quote!();
22932290
EnumBuilder::Rust {
22942291
codegen_depth: enum_codegen_depth + 1,
@@ -2580,15 +2577,24 @@ impl CodeGenerator for Enum {
25802577
let variation = self.computed_enum_variation(ctx, item);
25812578

25822579
// TODO(emilio): Delegate this to the builders?
2583-
if variation.is_rust() {
2584-
attrs.push(attributes::repr(repr_name));
2585-
} else if variation.is_bitfield() {
2586-
if ctx.options().rust_features.repr_transparent {
2587-
attrs.push(attributes::repr("transparent"));
2588-
} else {
2589-
attrs.push(attributes::repr("C"));
2590-
}
2591-
}
2580+
match variation {
2581+
EnumVariation::Rust { non_exhaustive } => {
2582+
attrs.push(attributes::repr(repr_name));
2583+
if non_exhaustive && ctx.options().rust_features().non_exhaustive {
2584+
attrs.push(attributes::non_exhaustive());
2585+
} else if non_exhaustive && !ctx.options().rust_features().non_exhaustive {
2586+
panic!("The rust target you're using doesn't seem to support non_exhaustive enums");
2587+
}
2588+
},
2589+
EnumVariation::Bitfield => {
2590+
if ctx.options().rust_features.repr_transparent {
2591+
attrs.push(attributes::repr("transparent"));
2592+
} else {
2593+
attrs.push(attributes::repr("C"));
2594+
}
2595+
},
2596+
_ => {},
2597+
};
25922598

25932599
if let Some(comment) = item.comment(ctx) {
25942600
attrs.push(attributes::doc(comment));

src/features.rs

+2
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ rust_feature_def!(
206206
Nightly {
207207
/// `thiscall` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/42202))
208208
=> thiscall_abi;
209+
/// `non_exhaustive` enums/structs ([Tracking issue](https://github.com/rust-lang/rust/issues/44109))
210+
=> non_exhaustive;
209211
}
210212
);
211213

src/ir/enum_ty.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,9 @@ impl Enum {
164164
} else if self.is_matching_enum(ctx, &ctx.options().bitfield_enums, item) {
165165
EnumVariation::Bitfield
166166
} else if self.is_matching_enum(ctx, &ctx.options().rustified_enums, item) {
167-
EnumVariation::Rust
167+
EnumVariation::Rust { non_exhaustive: false }
168+
} else if self.is_matching_enum(ctx, &ctx.options().rustified_non_exhaustive_enums, item) {
169+
EnumVariation::Rust { non_exhaustive: true }
168170
} else if self.is_matching_enum(ctx, &ctx.options().constified_enums, item) {
169171
EnumVariation::Consts
170172
} else {

src/lib.rs

+28-5
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,8 @@ impl Builder {
226226
if self.options.default_enum_style != Default::default() {
227227
output_vector.push("--default-enum-style=".into());
228228
output_vector.push(match self.options.default_enum_style {
229-
codegen::EnumVariation::Rust => "rust",
229+
codegen::EnumVariation::Rust { non_exhaustive: false } => "rust",
230+
codegen::EnumVariation::Rust { non_exhaustive: true } => "rust_non_exhaustive",
230231
codegen::EnumVariation::Bitfield => "bitfield",
231232
codegen::EnumVariation::Consts => "consts",
232233
codegen::EnumVariation::ModuleConsts => "moduleconsts",
@@ -253,6 +254,16 @@ impl Builder {
253254
})
254255
.count();
255256

257+
self.options
258+
.rustified_non_exhaustive_enums
259+
.get_items()
260+
.iter()
261+
.map(|item| {
262+
output_vector.push("--rustified-enum-non-exhaustive".into());
263+
output_vector.push(item.to_owned());
264+
})
265+
.count();
266+
256267
self.options
257268
.constified_enum_modules
258269
.get_items()
@@ -810,15 +821,24 @@ impl Builder {
810821
/// This makes bindgen generate enums instead of constants. Regular
811822
/// expressions are supported.
812823
///
813-
/// **Use this with caution.** You should not be using Rust enums unless
814-
/// you have complete control of the C/C++ code that you're binding to.
815-
/// Take a look at https://github.com/rust-lang/rust/issues/36927 for
816-
/// more information.
824+
/// **Use this with caution,** you probably want to use the non_exhaustive
825+
/// flavor of rust enums instead of this one. Take a look at
826+
/// https://github.com/rust-lang/rust/issues/36927 for more information.
817827
pub fn rustified_enum<T: AsRef<str>>(mut self, arg: T) -> Builder {
818828
self.options.rustified_enums.insert(arg);
819829
self
820830
}
821831

832+
/// Mark the given enum (or set of enums, if using a pattern) as a Rust
833+
/// enum with the #[non_exhaustive] attribute.
834+
///
835+
/// This makes bindgen generate enums instead of constants. Regular
836+
/// expressions are supported.
837+
pub fn rustified_non_exhaustive_enum<T: AsRef<str>>(mut self, arg: T) -> Builder {
838+
self.options.rustified_non_exhaustive_enums.insert(arg);
839+
self
840+
}
841+
822842
/// Mark the given enum (or set of enums, if using a pattern) as a set of
823843
/// constants that are not to be put into a module.
824844
pub fn constified_enum<T: AsRef<str>>(mut self, arg: T) -> Builder {
@@ -1367,6 +1387,8 @@ struct BindgenOptions {
13671387
/// The enum patterns to mark an enum as a Rust enum.
13681388
rustified_enums: RegexSet,
13691389

1390+
rustified_non_exhaustive_enums: RegexSet,
1391+
13701392
/// The enum patterns to mark an enum as a module of constants.
13711393
constified_enum_modules: RegexSet,
13721394

@@ -1620,6 +1642,7 @@ impl Default for BindgenOptions {
16201642
default_enum_style: Default::default(),
16211643
bitfield_enums: Default::default(),
16221644
rustified_enums: Default::default(),
1645+
rustified_non_exhaustive_enums: Default::default(),
16231646
constified_enums: Default::default(),
16241647
constified_enum_modules: Default::default(),
16251648
builtins: false,

src/options.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ where
3131
.help("The default style of code used to generate enums.")
3232
.value_name("variant")
3333
.default_value("consts")
34-
.possible_values(&["consts", "moduleconsts", "bitfield", "rust"])
34+
.possible_values(&["consts", "moduleconsts", "bitfield", "rust", "rust_non_exhaustive"])
3535
.multiple(false),
3636
Arg::with_name("bitfield-enum")
3737
.long("bitfield-enum")
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* automatically generated by rust-bindgen */
2+
3+
#![allow(
4+
dead_code,
5+
non_snake_case,
6+
non_camel_case_types,
7+
non_upper_case_globals
8+
)]
9+
#![cfg(feature = "nightly")]
10+
#![feature(non_exhaustive)]
11+
12+
13+
#[repr(u32)]
14+
#[non_exhaustive]
15+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
16+
pub enum Planet {
17+
earth = 0,
18+
mars = 1,
19+
}

tests/headers/issue-1554.h

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// bindgen-flags: --default-enum-style rust_non_exhaustive --rust-target nightly --raw-line '#![cfg(feature = "nightly")]' --raw-line '#![feature(non_exhaustive)]'
2+
3+
enum Planet {
4+
earth,
5+
mars
6+
};

0 commit comments

Comments
 (0)