diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index a37558677a..ea01d4b75e 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -749,6 +749,16 @@ impl CodeGenerator for Type { quote! {} }; + let alias_style = if ctx.options().type_alias.matches(&name) { + AliasVariation::TypeAlias + } else if ctx.options().new_type_alias.matches(&name) { + AliasVariation::NewType + } else if ctx.options().new_type_alias_deref.matches(&name) { + AliasVariation::NewTypeDeref + } else { + ctx.options().default_alias_style + }; + // We prefer using `pub use` over `pub type` because of: // https://github.com/rust-lang/rust/issues/26264 if inner_rust_type.to_string().chars().all(|c| match c { @@ -758,6 +768,7 @@ impl CodeGenerator for Type { _ => false, }) && outer_params.is_empty() && !is_opaque && + alias_style == AliasVariation::TypeAlias && inner_item.expect_type().canonical_type(ctx).is_enum() { tokens.append_all(quote! { @@ -772,8 +783,21 @@ impl CodeGenerator for Type { return; } - tokens.append_all(quote! { - pub type #rust_name + tokens.append_all(match alias_style { + AliasVariation::TypeAlias => quote! { + pub type #rust_name + }, + AliasVariation::NewType | AliasVariation::NewTypeDeref => { + assert!( + ctx.options().rust_features().repr_transparent, + "repr_transparent feature is required to use {:?}", + alias_style + ); + quote! { + #[repr(transparent)] + pub struct #rust_name + } + } }); let params: Vec<_> = outer_params @@ -806,10 +830,36 @@ impl CodeGenerator for Type { }); } - tokens.append_all(quote! { - = #inner_rust_type ; + tokens.append_all(match alias_style { + AliasVariation::TypeAlias => quote! { + = #inner_rust_type ; + }, + AliasVariation::NewType | AliasVariation::NewTypeDeref => { + quote! { + (pub #inner_rust_type) ; + } + } }); + if alias_style == AliasVariation::NewTypeDeref { + let prefix = ctx.trait_prefix(); + tokens.append_all(quote! { + impl ::#prefix::ops::Deref for #rust_name { + type Target = #inner_rust_type; + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl ::#prefix::ops::DerefMut for #rust_name { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + }); + } + result.push(tokens); } TypeKind::Enum(ref ei) => ei.codegen(ctx, result, item), @@ -2870,6 +2920,54 @@ impl CodeGenerator for Enum { } } +/// Enum for how aliases should be translated. +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum AliasVariation { + /// Convert to regular Rust alias + TypeAlias, + /// Create a new type by wrapping the old type in a struct and using #[repr(transparent)] + NewType, + /// Same as NewStruct but also impl Deref to be able to use the methods of the wrapped type + NewTypeDeref, +} + +impl AliasVariation { + /// Convert an `AliasVariation` to its str representation. + pub fn as_str(&self) -> &str { + match self { + AliasVariation::TypeAlias => "type_alias", + AliasVariation::NewType => "new_type", + AliasVariation::NewTypeDeref => "new_type_deref", + } + } +} + +impl Default for AliasVariation { + fn default() -> AliasVariation { + AliasVariation::TypeAlias + } +} + +impl std::str::FromStr for AliasVariation { + type Err = std::io::Error; + + /// Create an `AliasVariation` from a string. + fn from_str(s: &str) -> Result { + match s { + "type_alias" => Ok(AliasVariation::TypeAlias), + "new_type" => Ok(AliasVariation::NewType), + "new_type_deref" => Ok(AliasVariation::NewTypeDeref), + _ => Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + concat!( + "Got an invalid AliasVariation. Accepted values ", + "are 'type_alias', 'new_type', and 'new_type_deref'" + ), + )), + } + } +} + /// Fallible conversion to an opaque blob. /// /// Implementors of this trait should provide the `try_get_layout` method to diff --git a/src/lib.rs b/src/lib.rs index 4df8945406..8fa1b3df3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,7 +83,7 @@ doc_mod!(ir, ir_docs); doc_mod!(parse, parse_docs); doc_mod!(regex_set, regex_set_docs); -pub use codegen::EnumVariation; +pub use codegen::{AliasVariation, EnumVariation}; use features::RustFeatures; pub use features::{RustTarget, LATEST_STABLE_RUST, RUST_TARGET_STRINGS}; use ir::context::{BindgenContext, ItemId}; @@ -292,6 +292,42 @@ impl Builder { }) .count(); + if self.options.default_alias_style != Default::default() { + output_vector.push("--default-alias-style=".into()); + output_vector + .push(self.options.default_alias_style.as_str().into()); + } + + self.options + .type_alias + .get_items() + .iter() + .map(|item| { + output_vector.push("--type-alias".into()); + output_vector.push(item.to_owned()); + }) + .count(); + + self.options + .new_type_alias + .get_items() + .iter() + .map(|item| { + output_vector.push("--new-type-alias".into()); + output_vector.push(item.to_owned()); + }) + .count(); + + self.options + .new_type_alias_deref + .get_items() + .iter() + .map(|item| { + output_vector.push("--new-type-alias-deref".into()); + output_vector.push(item.to_owned()); + }) + .count(); + self.options .blacklisted_types .get_items() @@ -873,6 +909,45 @@ impl Builder { self } + /// Set the default style of code to generate for typedefs + pub fn default_alias_style( + mut self, + arg: codegen::AliasVariation, + ) -> Builder { + self.options.default_alias_style = arg; + self + } + + /// Mark the given typedef alias (or set of aliases, if using a pattern) to + /// use regular Rust type aliasing. + /// + /// This is the default behavior and should be used if `default_alias_style` + /// was set to NewType or NewTypeDeref and you want to override it for a + /// set of typedefs. + pub fn type_alias>(mut self, arg: T) -> Builder { + self.options.type_alias.insert(arg); + self + } + + /// Mark the given typedef alias (or set of aliases, if using a pattern) to + /// be generated as a new type by having the aliased type be wrapped in a + /// #[repr(transparent)] struct. + /// + /// Used to enforce stricter type checking. + pub fn new_type_alias>(mut self, arg: T) -> Builder { + self.options.new_type_alias.insert(arg); + self + } + + /// Mark the given typedef alias (or set of aliases, if using a pattern) to + /// be generated as a new type by having the aliased type be wrapped in a + /// #[repr(transparent)] struct and also have an automatically generated + /// impl's of `Deref` and `DerefMut` to their aliased type. + pub fn new_type_alias_deref>(mut self, arg: T) -> Builder { + self.options.new_type_alias_deref.insert(arg); + self + } + /// Add a string to prepend to the generated bindings. The string is passed /// through without any modification. pub fn raw_line>(mut self, arg: T) -> Self { @@ -1443,6 +1518,19 @@ struct BindgenOptions { /// The enum patterns to mark an enum as a set of constants. constified_enums: RegexSet, + /// The default style of code to generate for typedefs. + default_alias_style: codegen::AliasVariation, + + /// Typedef patterns that will use regular type aliasing. + type_alias: RegexSet, + + /// Typedef patterns that will be aliased by creating a new struct. + new_type_alias: RegexSet, + + /// Typedef patterns that will be wrapped in a new struct and have + /// Deref and Deref to their aliased type. + new_type_alias_deref: RegexSet, + /// Whether we should generate builtins or not. builtins: bool, @@ -1651,6 +1739,9 @@ impl BindgenOptions { &mut self.constified_enum_modules, &mut self.rustified_enums, &mut self.rustified_non_exhaustive_enums, + &mut self.type_alias, + &mut self.new_type_alias, + &mut self.new_type_alias_deref, &mut self.no_partialeq_types, &mut self.no_copy_types, &mut self.no_hash_types, @@ -1696,6 +1787,10 @@ impl Default for BindgenOptions { rustified_non_exhaustive_enums: Default::default(), constified_enums: Default::default(), constified_enum_modules: Default::default(), + default_alias_style: Default::default(), + type_alias: Default::default(), + new_type_alias: Default::default(), + new_type_alias_deref: Default::default(), builtins: false, emit_ast: false, emit_ir: false, diff --git a/src/options.rs b/src/options.rs index af50466c00..4f87240853 100644 --- a/src/options.rs +++ b/src/options.rs @@ -1,5 +1,5 @@ use bindgen::{ - builder, Builder, CodegenConfig, EnumVariation, RustTarget, + builder, AliasVariation, Builder, CodegenConfig, EnumVariation, RustTarget, RUST_TARGET_STRINGS, }; use clap::{App, Arg}; @@ -79,6 +79,47 @@ where .takes_value(true) .multiple(true) .number_of_values(1), + Arg::with_name("default-alias-style") + .long("default-alias-style") + .help("The default style of code used to generate typedefs.") + .value_name("variant") + .default_value("type_alias") + .possible_values(&[ + "type_alias", + "new_type", + "new_type_deref", + ]) + .multiple(false), + Arg::with_name("normal-alias") + .long("normal-alias") + .help( + "Mark any typedef alias whose name matches to use \ + normal type aliasing.", + ) + .value_name("regex") + .takes_value(true) + .multiple(true) + .number_of_values(1), + Arg::with_name("new-type-alias") + .long("new-type-alias") + .help( + "Mark any typedef alias whose name matches to have \ + a new type generated for it.", + ) + .value_name("regex") + .takes_value(true) + .multiple(true) + .number_of_values(1), + Arg::with_name("new-type-alias-deref") + .long("new-type-alias-deref") + .help( + "Mark any typedef alias whose name matches to have \ + a new type with Deref and DerefMut to the inner type.", + ) + .value_name("regex") + .takes_value(true) + .multiple(true) + .number_of_values(1), Arg::with_name("blacklist-type") .long("blacklist-type") .help("Mark as hidden.") @@ -436,6 +477,30 @@ where builder = builder.constified_enum_module(regex); } } + + if let Some(variant) = matches.value_of("default-alias-style") { + builder = + builder.default_alias_style(AliasVariation::from_str(variant)?); + } + + if let Some(type_alias) = matches.values_of("normal-alias") { + for regex in type_alias { + builder = builder.type_alias(regex); + } + } + + if let Some(new_type) = matches.values_of("new-type-alias") { + for regex in new_type { + builder = builder.new_type_alias(regex); + } + } + + if let Some(new_type_deref) = matches.values_of("new-type-alias-deref") { + for regex in new_type_deref { + builder = builder.new_type_alias_deref(regex); + } + } + if let Some(hidden_types) = matches.values_of("blacklist-type") { for ty in hidden_types { builder = builder.blacklist_type(ty); diff --git a/tests/expectations/tests/issue-1488-enum-new-type.rs b/tests/expectations/tests/issue-1488-enum-new-type.rs new file mode 100644 index 0000000000..2e0334a87b --- /dev/null +++ b/tests/expectations/tests/issue-1488-enum-new-type.rs @@ -0,0 +1,47 @@ +/* automatically generated by rust-bindgen */ + +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +pub const Foo_A: Foo = 0; +pub const Foo_B: Foo = 1; +pub type Foo = u32; +#[repr(transparent)] +pub struct FooAlias(pub Foo); +pub mod Bar { + pub type Type = u32; + pub const C: Type = 0; + pub const D: Type = 1; +} +#[repr(transparent)] +pub struct BarAlias(pub Bar::Type); +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum Qux { + E = 0, + F = 1, +} +#[repr(transparent)] +pub struct QuxAlias(pub Qux); +pub const Baz_G: Baz = 0; +pub const Baz_H: Baz = 1; +pub type Baz = u32; +#[repr(transparent)] +pub struct BazAlias(pub Baz); +impl ::std::ops::Deref for BazAlias { + type Target = Baz; + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl ::std::ops::DerefMut for BazAlias { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/tests/expectations/tests/issue-1488-options.rs b/tests/expectations/tests/issue-1488-options.rs new file mode 100644 index 0000000000..ce85c329c1 --- /dev/null +++ b/tests/expectations/tests/issue-1488-options.rs @@ -0,0 +1,27 @@ +/* automatically generated by rust-bindgen */ + +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +pub type OSStatus = ::std::os::raw::c_int; +#[repr(transparent)] +pub struct SomePtr(pub *mut ::std::os::raw::c_void); +#[repr(transparent)] +pub struct AnotherPtr(pub *mut ::std::os::raw::c_void); +impl ::std::ops::Deref for AnotherPtr { + type Target = *mut ::std::os::raw::c_void; + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl ::std::ops::DerefMut for AnotherPtr { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/tests/expectations/tests/issue-1488-template-alias-new-type.rs b/tests/expectations/tests/issue-1488-template-alias-new-type.rs new file mode 100644 index 0000000000..94cd5c34e2 --- /dev/null +++ b/tests/expectations/tests/issue-1488-template-alias-new-type.rs @@ -0,0 +1,11 @@ +/* automatically generated by rust-bindgen */ + +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(transparent)] +pub struct Wrapped(pub T); diff --git a/tests/headers/issue-1488-enum-new-type.h b/tests/headers/issue-1488-enum-new-type.h new file mode 100644 index 0000000000..6855283ffb --- /dev/null +++ b/tests/headers/issue-1488-enum-new-type.h @@ -0,0 +1,29 @@ +// bindgen-flags: --default-alias-style=new_type --constified-enum "Foo" --constified-enum-module "Bar" --rustified-enum "Qux" --new-type-alias-deref "BazAlias" + +enum Foo { + A, + B +}; + +typedef enum Foo FooAlias; + +enum Bar { + C, + D +}; + +typedef enum Bar BarAlias; + +enum Qux { + E, + F +}; + +typedef enum Qux QuxAlias; + +enum Baz { + G, + H +}; + +typedef enum Baz BazAlias; \ No newline at end of file diff --git a/tests/headers/issue-1488-options.h b/tests/headers/issue-1488-options.h new file mode 100644 index 0000000000..fba1c93bac --- /dev/null +++ b/tests/headers/issue-1488-options.h @@ -0,0 +1,5 @@ +// bindgen-flags: --new-type-alias "SomePtr" --new-type-alias-deref "AnotherPtr" + +typedef int OSStatus; +typedef void* SomePtr; +typedef void* AnotherPtr; \ No newline at end of file diff --git a/tests/headers/issue-1488-template-alias-new-type.hpp b/tests/headers/issue-1488-template-alias-new-type.hpp new file mode 100644 index 0000000000..4374da3db2 --- /dev/null +++ b/tests/headers/issue-1488-template-alias-new-type.hpp @@ -0,0 +1,4 @@ +// bindgen-flags: --with-derive-hash --with-derive-partialeq --with-derive-eq --default-alias-style=new_type -- -std=c++14 + +template +using Wrapped = T;