summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmilio Cobos Álvarez <emilio@crisal.io>2019-06-13 01:14:21 +0200
committerGitHub <noreply@github.com>2019-06-13 01:14:21 +0200
commit16614210ba16dc885b2952529990c0a74663a719 (patch)
treeeeab981d028ba091deffeae70a3c5ef61437a5cc
parent6aa5b2b4222f5a95270f630defab633f8ac57bc0 (diff)
parent2a522304b7302191bff8fe0cb540f8bba0da395c (diff)
Add support for non_exhaustive rustified enums. (#1575)
Implements the feature discussed in https://github.com/rust-lang/rust-bindgen/issues/1554.
-rw-r--r--src/codegen/helpers.rs6
-rw-r--r--src/codegen/mod.rs48
-rw-r--r--src/features.rs2
-rw-r--r--src/ir/enum_ty.rs4
-rw-r--r--src/lib.rs33
-rw-r--r--src/options.rs2
-rw-r--r--tests/expectations/tests/issue-1554.rs19
-rw-r--r--tests/headers/issue-1554.h6
8 files changed, 92 insertions, 28 deletions
diff --git a/src/codegen/helpers.rs b/src/codegen/helpers.rs
index b630a70b..1e8534ee 100644
--- a/src/codegen/helpers.rs
+++ b/src/codegen/helpers.rs
@@ -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.
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index ceb2f5b8..15ace9e9 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -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
@@ -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
}
}
@@ -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'."))),
}
}
@@ -2288,7 +2285,7 @@ impl<'a> EnumBuilder<'a> {
}
}
- EnumVariation::Rust => {
+ EnumVariation::Rust { .. } => {
let tokens = quote!();
EnumBuilder::Rust {
codegen_depth: enum_codegen_depth + 1,
@@ -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));
diff --git a/src/features.rs b/src/features.rs
index 05f5dc42..4dc526ec 100644
--- a/src/features.rs
+++ b/src/features.rs
@@ -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;
}
);
diff --git a/src/ir/enum_ty.rs b/src/ir/enum_ty.rs
index f3da2199..be33ed4b 100644
--- a/src/ir/enum_ty.rs
+++ b/src/ir/enum_ty.rs
@@ -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 {
diff --git a/src/lib.rs b/src/lib.rs
index 4a695f60..b669bcb2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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",
@@ -254,6 +255,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()
.iter()
@@ -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 {
@@ -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,
@@ -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,
diff --git a/src/options.rs b/src/options.rs
index 300036e0..43ad2edc 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -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")
diff --git a/tests/expectations/tests/issue-1554.rs b/tests/expectations/tests/issue-1554.rs
new file mode 100644
index 00000000..c3fb0d48
--- /dev/null
+++ b/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,
+}
diff --git a/tests/headers/issue-1554.h b/tests/headers/issue-1554.h
new file mode 100644
index 00000000..13452923
--- /dev/null
+++ b/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
+};