Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for non_exhaustive rustified enums. #1575

Merged
merged 6 commits into from Jun 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 {
agodnic marked this conversation as resolved.
Show resolved Hide resolved
earth,
mars
};