diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index ca4cbf23ac..9a55ea3799 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -2577,6 +2577,8 @@ pub enum EnumVariation { NewType { /// Indicates whether the newtype will have bitwise operators is_bitfield: bool, + /// Indicates whether the variants will be represented as global constants + is_global: bool, }, /// The code for this enum will use consts Consts, @@ -2614,16 +2616,26 @@ impl std::str::FromStr for EnumVariation { "rust_non_exhaustive" => Ok(EnumVariation::Rust { non_exhaustive: true, }), - "bitfield" => Ok(EnumVariation::NewType { is_bitfield: true }), + "bitfield" => Ok(EnumVariation::NewType { + is_bitfield: true, + is_global: false, + }), "consts" => Ok(EnumVariation::Consts), "moduleconsts" => Ok(EnumVariation::ModuleConsts), - "newtype" => Ok(EnumVariation::NewType { is_bitfield: false }), + "newtype" => Ok(EnumVariation::NewType { + is_bitfield: false, + is_global: false, + }), + "newtype_global" => Ok(EnumVariation::NewType { + is_bitfield: false, + is_global: true, + }), _ => Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, concat!( "Got an invalid EnumVariation. Accepted values ", "are 'rust', 'rust_non_exhaustive', 'bitfield', 'consts',", - "'moduleconsts', and 'newtype'." + "'moduleconsts', 'newtype' and 'newtype_global'." ), )), } @@ -2644,6 +2656,7 @@ enum EnumBuilder<'a> { canonical_name: &'a str, tokens: proc_macro2::TokenStream, is_bitfield: bool, + is_global: bool, }, Consts { variants: Vec, @@ -2684,7 +2697,10 @@ impl<'a> EnumBuilder<'a> { let ident = Ident::new(name, Span::call_site()); match enum_variation { - EnumVariation::NewType { is_bitfield } => EnumBuilder::NewType { + EnumVariation::NewType { + is_bitfield, + is_global, + } => EnumBuilder::NewType { codegen_depth: enum_codegen_depth, canonical_name: name, tokens: quote! { @@ -2692,6 +2708,7 @@ impl<'a> EnumBuilder<'a> { pub struct #ident (pub #repr); }, is_bitfield, + is_global, }, EnumVariation::Rust { .. } => { @@ -2792,17 +2809,29 @@ impl<'a> EnumBuilder<'a> { } } - EnumBuilder::NewType { canonical_name, .. } => { + EnumBuilder::NewType { + canonical_name, + is_global, + .. + } => { if ctx.options().rust_features().associated_const && is_ty_named { let enum_ident = ctx.rust_ident(canonical_name); let variant_ident = ctx.rust_ident(variant_name); - result.push(quote! { - impl #enum_ident { - #doc - pub const #variant_ident : #rust_ty = #rust_ty ( #expr ); - } - }); + let tokens = quote! { + #doc + pub const #variant_ident : #rust_ty = #rust_ty ( #expr ); + }; + + if is_global { + result.push(tokens); + } else { + result.push(quote! { + impl #enum_ident { + #tokens + } + }); + } } else { let ident = ctx.rust_ident(match mangling_prefix { Some(prefix) => { diff --git a/src/ir/enum_ty.rs b/src/ir/enum_ty.rs index 97455c9123..2b039a4fac 100644 --- a/src/ir/enum_ty.rs +++ b/src/ir/enum_ty.rs @@ -187,10 +187,25 @@ impl Enum { &ctx.options().bitfield_enums, item, ) { - EnumVariation::NewType { is_bitfield: true } + EnumVariation::NewType { + is_bitfield: true, + is_global: false, + } } else if self.is_matching_enum(ctx, &ctx.options().newtype_enums, item) { - EnumVariation::NewType { is_bitfield: false } + EnumVariation::NewType { + is_bitfield: false, + is_global: false, + } + } else if self.is_matching_enum( + ctx, + &ctx.options().newtype_global_enums, + item, + ) { + EnumVariation::NewType { + is_bitfield: false, + is_global: true, + } } else if self.is_matching_enum( ctx, &ctx.options().rustified_enums, diff --git a/src/lib.rs b/src/lib.rs index b90faba613..3e5235641b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -279,11 +279,19 @@ impl Builder { codegen::EnumVariation::Rust { non_exhaustive: true, } => "rust_non_exhaustive", - codegen::EnumVariation::NewType { is_bitfield: true } => { - "bitfield" - } - codegen::EnumVariation::NewType { is_bitfield: false } => { - "newtype" + codegen::EnumVariation::NewType { + is_bitfield: true, + .. + } => "bitfield", + codegen::EnumVariation::NewType { + is_bitfield: false, + is_global, + } => { + if is_global { + "newtype_global" + } else { + "newtype" + } } codegen::EnumVariation::Consts => "consts", codegen::EnumVariation::ModuleConsts => "moduleconsts", @@ -984,6 +992,18 @@ impl Builder { self } + /// Mark the given enum (or set of enums, if using a pattern) as a newtype + /// whose variants are exposed as global constants. + /// + /// Regular expressions are supported. + /// + /// This makes bindgen generate a type that isn't a Rust `enum`. Regular + /// expressions are supported. + pub fn newtype_global_enum>(mut self, arg: T) -> Builder { + self.options.newtype_global_enums.insert(arg); + self + } + /// Mark the given enum (or set of enums, if using a pattern) as a Rust /// enum. /// @@ -1759,6 +1779,9 @@ struct BindgenOptions { /// The enum patterns to mark an enum as a newtype. newtype_enums: RegexSet, + /// The enum patterns to mark an enum as a global newtype. + newtype_global_enums: RegexSet, + /// The enum patterns to mark an enum as a Rust enum. rustified_enums: RegexSet, @@ -2097,6 +2120,7 @@ impl Default for BindgenOptions { default_enum_style: Default::default(), bitfield_enums: Default::default(), newtype_enums: Default::default(), + newtype_global_enums: Default::default(), rustified_enums: Default::default(), rustified_non_exhaustive_enums: Default::default(), constified_enums: Default::default(), diff --git a/src/options.rs b/src/options.rs index 83da21f42f..4db3fd5766 100644 --- a/src/options.rs +++ b/src/options.rs @@ -63,6 +63,12 @@ where .value_name("regex") .multiple_occurrences(true) .number_of_values(1), + Arg::new("newtype-global-enum") + .long("newtype-global-enum") + .help("Mark any enum whose name matches as a global newtype.") + .value_name("regex") + .multiple_occurrences(true) + .number_of_values(1), Arg::new("rustified-enum") .long("rustified-enum") .help("Mark any enum whose name matches as a Rust enum.") @@ -573,6 +579,12 @@ where } } + if let Some(newtypes) = matches.values_of("newtype-global-enum") { + for regex in newtypes { + builder = builder.newtype_global_enum(regex); + } + } + if let Some(rustifieds) = matches.values_of("rustified-enum") { for regex in rustifieds { builder = builder.rustified_enum(regex); diff --git a/tests/expectations/tests/newtype-global-enum.rs b/tests/expectations/tests/newtype-global-enum.rs new file mode 100644 index 0000000000..ff98063724 --- /dev/null +++ b/tests/expectations/tests/newtype-global-enum.rs @@ -0,0 +1,12 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +pub const Foo_Bar: Foo = 2; +pub const Foo_Baz: Foo = 4; +pub const Foo_Duplicated: Foo = 4; +pub const Foo_Negative: Foo = -3; +pub type Foo = ::std::os::raw::c_int; diff --git a/tests/headers/newtype-global-enum.hpp b/tests/headers/newtype-global-enum.hpp new file mode 100644 index 0000000000..8021a3cc72 --- /dev/null +++ b/tests/headers/newtype-global-enum.hpp @@ -0,0 +1,8 @@ +// bindgen-flags: --newtype-global-enum "Foo" --rust-target 1.28 -- -std=c++11 + +enum Foo { + Bar = 1 << 1, + Baz = 1 << 2, + Duplicated = 1 << 2, + Negative = -3, +};