Skip to content

Commit ccc43cd

Browse files
committed
Add support for non_exhaustive rustified enums.
Implements the feature discussed in rust-lang#1554.
1 parent 6aa5b2b commit ccc43cd

File tree

7 files changed

+85
-28
lines changed

7 files changed

+85
-28
lines changed

src/codegen/helpers.rs

Lines changed: 6 additions & 0 deletions
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

Lines changed: 25 additions & 21 deletions
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+
///
2174+
/// When the boolean parameter on this variant is set to true,
2175+
/// the generated enum should be non_exhaustive.
2176+
Rust(bool),
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(false)),
2216+
"rust_non_exhaustive" => Ok(EnumVariation::Rust(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,22 @@ 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 {
2584+
attrs.push(attributes::non_exhaustive());
2585+
}
2586+
},
2587+
EnumVariation::Bitfield => {
2588+
if ctx.options().rust_features.repr_transparent {
2589+
attrs.push(attributes::repr("transparent"));
2590+
} else {
2591+
attrs.push(attributes::repr("C"));
2592+
}
2593+
},
2594+
_ => {},
2595+
};
25922596

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

src/ir/enum_ty.rs

Lines changed: 3 additions & 1 deletion
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(false)
168+
} else if self.is_matching_enum(ctx, &ctx.options().rustified_enums_non_exhaustive, item) {
169+
EnumVariation::Rust(true)
168170
} else if self.is_matching_enum(ctx, &ctx.options().constified_enums, item) {
169171
EnumVariation::Consts
170172
} else {

src/lib.rs

Lines changed: 28 additions & 5 deletions
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(false) => "rust",
230+
codegen::EnumVariation::Rust(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_enums_non_exhaustive
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_enum_non_exhaustive<T: AsRef<str>>(mut self, arg: T) -> Builder {
838+
self.options.rustified_enums_non_exhaustive.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_enums_non_exhaustive: 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_enums_non_exhaustive: Default::default(),
16231646
constified_enums: Default::default(),
16241647
constified_enum_modules: Default::default(),
16251648
builtins: false,

src/options.rs

Lines changed: 1 addition & 1 deletion
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")
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
10+
#[repr(u32)]
11+
#[non_exhaustive]
12+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
13+
pub enum Planet {
14+
earth = 0,
15+
mars = 1,
16+
}

tests/headers/issue-1554.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// bindgen-flags: --default-enum-style rust_non_exhaustive
2+
3+
enum Planet {
4+
earth,
5+
mars
6+
};

0 commit comments

Comments
 (0)