Skip to content

Commit

Permalink
Add support for non_exhaustive rustified enums. (#1575)
Browse files Browse the repository at this point in the history
Implements the feature discussed in
#1554.
  • Loading branch information
emilio committed Jun 12, 2019
2 parents 6aa5b2b + 2a52230 commit 1661421
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 28 deletions.
6 changes: 6 additions & 0 deletions src/codegen/helpers.rs
Expand Up @@ -42,6 +42,12 @@ pub mod attributes {
}
}

pub fn non_exhaustive() -> TokenStream {
quote! {
#[non_exhaustive]
}
}

pub fn doc(comment: String) -> TokenStream {
// NOTE(emilio): By this point comments are already preprocessed and in
// `///` form. Quote turns them into `#[doc]` comments, but oh well.
Expand Down
48 changes: 27 additions & 21 deletions src/codegen/mod.rs
Expand Up @@ -2170,7 +2170,10 @@ impl MethodCodegen for Method {
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum EnumVariation {
/// The code for this enum will use a Rust enum
Rust,
Rust {
/// Indicates whether the generated struct should be #[non_exhaustive]
non_exhaustive: bool
},
/// The code for this enum will use a bitfield
Bitfield,
/// The code for this enum will use consts
Expand All @@ -2182,14 +2185,7 @@ pub enum EnumVariation {
impl EnumVariation {
fn is_rust(&self) -> bool {
match *self {
EnumVariation::Rust => true,
_ => false
}
}

fn is_bitfield(&self) -> bool {
match *self {
EnumVariation::Bitfield {..} => true,
EnumVariation::Rust{ .. } => true,
_ => false
}
}
Expand All @@ -2216,13 +2212,14 @@ impl std::str::FromStr for EnumVariation {
/// Create a `EnumVariation` from a string.
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"rust" => Ok(EnumVariation::Rust),
"rust" => Ok(EnumVariation::Rust{ non_exhaustive: false }),
"rust_non_exhaustive" => Ok(EnumVariation::Rust{ non_exhaustive: true }),
"bitfield" => Ok(EnumVariation::Bitfield),
"consts" => Ok(EnumVariation::Consts),
"moduleconsts" => Ok(EnumVariation::ModuleConsts),
_ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput,
concat!("Got an invalid EnumVariation. Accepted values ",
"are 'rust', 'bitfield', 'consts', and ",
"are 'rust', 'rust_non_exhaustive', 'bitfield', 'consts', and ",
"'moduleconsts'."))),
}
}
Expand Down Expand Up @@ -2288,7 +2285,7 @@ impl<'a> EnumBuilder<'a> {
}
}

EnumVariation::Rust => {
EnumVariation::Rust { .. } => {
let tokens = quote!();
EnumBuilder::Rust {
codegen_depth: enum_codegen_depth + 1,
Expand Down Expand Up @@ -2580,15 +2577,24 @@ impl CodeGenerator for Enum {
let variation = self.computed_enum_variation(ctx, item);

// TODO(emilio): Delegate this to the builders?
if variation.is_rust() {
attrs.push(attributes::repr(repr_name));
} else if variation.is_bitfield() {
if ctx.options().rust_features.repr_transparent {
attrs.push(attributes::repr("transparent"));
} else {
attrs.push(attributes::repr("C"));
}
}
match variation {
EnumVariation::Rust { non_exhaustive } => {
attrs.push(attributes::repr(repr_name));
if non_exhaustive && ctx.options().rust_features().non_exhaustive {
attrs.push(attributes::non_exhaustive());
} else if non_exhaustive && !ctx.options().rust_features().non_exhaustive {
panic!("The rust target you're using doesn't seem to support non_exhaustive enums");
}
},
EnumVariation::Bitfield => {
if ctx.options().rust_features.repr_transparent {
attrs.push(attributes::repr("transparent"));
} else {
attrs.push(attributes::repr("C"));
}
},
_ => {},
};

if let Some(comment) = item.comment(ctx) {
attrs.push(attributes::doc(comment));
Expand Down
2 changes: 2 additions & 0 deletions src/features.rs
Expand Up @@ -206,6 +206,8 @@ rust_feature_def!(
Nightly {
/// `thiscall` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/42202))
=> thiscall_abi;
/// `non_exhaustive` enums/structs ([Tracking issue](https://github.com/rust-lang/rust/issues/44109))
=> non_exhaustive;
}
);

Expand Down
4 changes: 3 additions & 1 deletion src/ir/enum_ty.rs
Expand Up @@ -164,7 +164,9 @@ impl Enum {
} else if self.is_matching_enum(ctx, &ctx.options().bitfield_enums, item) {
EnumVariation::Bitfield
} else if self.is_matching_enum(ctx, &ctx.options().rustified_enums, item) {
EnumVariation::Rust
EnumVariation::Rust { non_exhaustive: false }
} else if self.is_matching_enum(ctx, &ctx.options().rustified_non_exhaustive_enums, item) {
EnumVariation::Rust { non_exhaustive: true }
} else if self.is_matching_enum(ctx, &ctx.options().constified_enums, item) {
EnumVariation::Consts
} else {
Expand Down
33 changes: 28 additions & 5 deletions src/lib.rs
Expand Up @@ -226,7 +226,8 @@ impl Builder {
if self.options.default_enum_style != Default::default() {
output_vector.push("--default-enum-style=".into());
output_vector.push(match self.options.default_enum_style {
codegen::EnumVariation::Rust => "rust",
codegen::EnumVariation::Rust { non_exhaustive: false } => "rust",
codegen::EnumVariation::Rust { non_exhaustive: true } => "rust_non_exhaustive",
codegen::EnumVariation::Bitfield => "bitfield",
codegen::EnumVariation::Consts => "consts",
codegen::EnumVariation::ModuleConsts => "moduleconsts",
Expand All @@ -253,6 +254,16 @@ impl Builder {
})
.count();

self.options
.rustified_non_exhaustive_enums
.get_items()
.iter()
.map(|item| {
output_vector.push("--rustified-enum-non-exhaustive".into());
output_vector.push(item.to_owned());
})
.count();

self.options
.constified_enum_modules
.get_items()
Expand Down Expand Up @@ -810,15 +821,24 @@ impl Builder {
/// This makes bindgen generate enums instead of constants. Regular
/// expressions are supported.
///
/// **Use this with caution.** You should not be using Rust enums unless
/// you have complete control of the C/C++ code that you're binding to.
/// Take a look at https://github.com/rust-lang/rust/issues/36927 for
/// more information.
/// **Use this with caution,** you probably want to use the non_exhaustive
/// flavor of rust enums instead of this one. Take a look at
/// https://github.com/rust-lang/rust/issues/36927 for more information.
pub fn rustified_enum<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.rustified_enums.insert(arg);
self
}

/// Mark the given enum (or set of enums, if using a pattern) as a Rust
/// enum with the #[non_exhaustive] attribute.
///
/// This makes bindgen generate enums instead of constants. Regular
/// expressions are supported.
pub fn rustified_non_exhaustive_enum<T: AsRef<str>>(mut self, arg: T) -> Builder {
self.options.rustified_non_exhaustive_enums.insert(arg);
self
}

/// Mark the given enum (or set of enums, if using a pattern) as a set of
/// constants that are not to be put into a module.
pub fn constified_enum<T: AsRef<str>>(mut self, arg: T) -> Builder {
Expand Down Expand Up @@ -1367,6 +1387,8 @@ struct BindgenOptions {
/// The enum patterns to mark an enum as a Rust enum.
rustified_enums: RegexSet,

rustified_non_exhaustive_enums: RegexSet,

/// The enum patterns to mark an enum as a module of constants.
constified_enum_modules: RegexSet,

Expand Down Expand Up @@ -1620,6 +1642,7 @@ impl Default for BindgenOptions {
default_enum_style: Default::default(),
bitfield_enums: Default::default(),
rustified_enums: Default::default(),
rustified_non_exhaustive_enums: Default::default(),
constified_enums: Default::default(),
constified_enum_modules: Default::default(),
builtins: false,
Expand Down
2 changes: 1 addition & 1 deletion src/options.rs
Expand Up @@ -31,7 +31,7 @@ where
.help("The default style of code used to generate enums.")
.value_name("variant")
.default_value("consts")
.possible_values(&["consts", "moduleconsts", "bitfield", "rust"])
.possible_values(&["consts", "moduleconsts", "bitfield", "rust", "rust_non_exhaustive"])
.multiple(false),
Arg::with_name("bitfield-enum")
.long("bitfield-enum")
Expand Down
19 changes: 19 additions & 0 deletions tests/expectations/tests/issue-1554.rs
@@ -0,0 +1,19 @@
/* automatically generated by rust-bindgen */

#![allow(
dead_code,
non_snake_case,
non_camel_case_types,
non_upper_case_globals
)]
#![cfg(feature = "nightly")]
#![feature(non_exhaustive)]


#[repr(u32)]
#[non_exhaustive]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Planet {
earth = 0,
mars = 1,
}
6 changes: 6 additions & 0 deletions tests/headers/issue-1554.h
@@ -0,0 +1,6 @@
// bindgen-flags: --default-enum-style rust_non_exhaustive --rust-target nightly --raw-line '#![cfg(feature = "nightly")]' --raw-line '#![feature(non_exhaustive)]'

enum Planet {
earth,
mars
};

0 comments on commit 1661421

Please sign in to comment.