From 0442b3096484565ee8b106b2c2a0d7b6be8a2230 Mon Sep 17 00:00:00 2001 From: Varphone Wong Date: Wed, 25 Nov 2020 17:24:46 +0800 Subject: [PATCH] Add --no-default flag Sometimes, we need customize the implement of `Default` for certain types, In these cases, the `nodefault` annotation can be used to prevent bindgen to autoderive the `Default` traits for a type. --- book/src/SUMMARY.md | 1 + book/src/nodefault.md | 48 +++++++++++++++++++ src/codegen/mod.rs | 8 ++-- src/ir/analysis/derive.rs | 2 +- src/ir/annotations.rs | 9 ++++ src/ir/context.rs | 6 +++ src/lib.rs | 13 +++++ src/options.rs | 13 +++++ tests/expectations/tests/no_default.rs | 23 +++++++++ .../tests/no_default_bypass_derive_default.rs | 22 +++++++++ tests/expectations/tests/no_default_opaque.rs | 26 ++++++++++ .../tests/no_default_whitelisted.rs | 35 ++++++++++++++ tests/headers/no_default.hpp | 11 +++++ .../no_default_bypass_derive_default.hpp | 11 +++++ tests/headers/no_default_opaque.hpp | 5 ++ tests/headers/no_default_whitelisted.hpp | 5 ++ 16 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 book/src/nodefault.md create mode 100644 tests/expectations/tests/no_default.rs create mode 100644 tests/expectations/tests/no_default_bypass_derive_default.rs create mode 100644 tests/expectations/tests/no_default_opaque.rs create mode 100644 tests/expectations/tests/no_default_whitelisted.rs create mode 100644 tests/headers/no_default.hpp create mode 100644 tests/headers/no_default_bypass_derive_default.hpp create mode 100644 tests/headers/no_default_opaque.hpp create mode 100644 tests/headers/no_default_whitelisted.hpp diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index e2347fb43c..16291c3631 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -18,6 +18,7 @@ - [Replacing One Type with Another](./replacing-types.md) - [Preventing the Derivation of `Copy` and `Clone`](./nocopy.md) - [Preventing the Derivation of `Debug`](./nodebug.md) + - [Preventing the Derivation of `Default`](./nodefault.md) - [Generating Bindings to C++](./cpp.md) - [Generating Bindings to Objective-c](./objc.md) - [Using Unions](./using-unions.md) diff --git a/book/src/nodefault.md b/book/src/nodefault.md new file mode 100644 index 0000000000..290a4a959b --- /dev/null +++ b/book/src/nodefault.md @@ -0,0 +1,48 @@ +# Preventing the Derivation of `Default` + +`bindgen` will attempt to derive/impl the `Default` traits on a best-effort basis. +Sometimes, we need customize the implement of `Default` for certain types, +In these cases, the `nodefault` annotation can be used to prevent bindgen +to autoderive the `Default` traits for a type. + +### Library + +* [`bindgen::Builder::no_default`](https://docs.rs/bindgen/latest/bindgen/struct.Builder.html#method.no_default) + +### Command Line + +* `--no-default ` + +### Annotations + +```c +/** + * We need to specify some preset values as the Default of Header. + * + * for example: + * + *
+ */ +struct Header { + unsigned int magic; + unsigned char data[252]; +}; + +... +``` + +### Customize Implements + +```rust +// Inlcude the generated bindings. +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +impl Default for Header { + fn default() -> Self { + Self { + magic: 0x10203040u32, + data: [0; 252usize], + } + } +} +``` diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 91acf5b03a..0034579f07 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -116,7 +116,7 @@ fn derives_of_item(item: &Item, ctx: &BindgenContext) -> DerivableTraits { derivable_traits |= DerivableTraits::DEBUG; } - if item.can_derive_default(ctx) { + if item.can_derive_default(ctx) && !item.annotations().disallow_default() { derivable_traits |= DerivableTraits::DEFAULT; } @@ -1892,8 +1892,10 @@ impl CodeGenerator for CompInfo { } if !derivable_traits.contains(DerivableTraits::DEFAULT) { - needs_default_impl = - ctx.options().derive_default && !self.is_forward_declaration(); + needs_default_impl = ctx.options().derive_default && + !self.is_forward_declaration() && + !ctx.no_default_by_name(item) && + !item.annotations().disallow_default(); } let all_template_params = item.all_template_params(ctx); diff --git a/src/ir/analysis/derive.rs b/src/ir/analysis/derive.rs index 48c544f6aa..6c50fa6cbf 100644 --- a/src/ir/analysis/derive.rs +++ b/src/ir/analysis/derive.rs @@ -446,11 +446,11 @@ impl DeriveTrait { match self { DeriveTrait::Copy => ctx.no_copy_by_name(item), DeriveTrait::Debug => ctx.no_debug_by_name(item), + DeriveTrait::Default => ctx.no_default_by_name(item), DeriveTrait::Hash => ctx.no_hash_by_name(item), DeriveTrait::PartialEqOrPartialOrd => { ctx.no_partialeq_by_name(item) } - _ => false, } } diff --git a/src/ir/annotations.rs b/src/ir/annotations.rs index da4ef4f75e..12664f6022 100644 --- a/src/ir/annotations.rs +++ b/src/ir/annotations.rs @@ -40,6 +40,8 @@ pub struct Annotations { disallow_copy: bool, /// Manually disable deriving debug on this type. disallow_debug: bool, + /// Manually disable deriving/implement default on this type. + disallow_default: bool, /// Whether fields should be marked as private or not. You can set this on /// structs (it will apply to all the fields), or individual fields. private_fields: Option, @@ -81,6 +83,7 @@ impl Default for Annotations { use_instead_of: None, disallow_copy: false, disallow_debug: false, + disallow_default: false, private_fields: None, accessor_kind: None, constify_enum_variant: false, @@ -155,6 +158,11 @@ impl Annotations { self.disallow_debug } + /// Should we avoid implementing the `Default` trait? + pub fn disallow_default(&self) -> bool { + self.disallow_default + } + /// Should the fields be private? pub fn private_fields(&self) -> Option { self.private_fields @@ -181,6 +189,7 @@ impl Annotations { "hide" => self.hide = true, "nocopy" => self.disallow_copy = true, "nodebug" => self.disallow_debug = true, + "nodefault" => self.disallow_default = true, "replaces" => { self.use_instead_of = Some( attr.value.split("::").map(Into::into).collect(), diff --git a/src/ir/context.rs b/src/ir/context.rs index bb0c3f9dc9..1a54375779 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -2591,6 +2591,12 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.options().no_debug_types.matches(&name) } + /// Check if `--no-default` flag is enabled for this item. + pub fn no_default_by_name(&self, item: &Item) -> bool { + let name = item.path_for_whitelisting(self)[1..].join("::"); + self.options().no_default_types.matches(&name) + } + /// Check if `--no-hash` flag is enabled for this item. pub fn no_hash_by_name(&self, item: &Item) -> bool { let name = item.path_for_whitelisting(self)[1..].join("::"); diff --git a/src/lib.rs b/src/lib.rs index 08b9381785..ead8f05ab1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -305,6 +305,7 @@ impl Builder { (&self.options.no_partialeq_types, "--no-partialeq"), (&self.options.no_copy_types, "--no-copy"), (&self.options.no_debug_types, "--no-debug"), + (&self.options.no_default_types, "--no-default"), (&self.options.no_hash_types, "--no-hash"), ]; @@ -1447,6 +1448,13 @@ impl Builder { self } + /// Don't derive/impl `Default` for a given type. Regular + /// expressions are supported. + pub fn no_default>(mut self, arg: T) -> Self { + self.options.no_default_types.insert(arg.into()); + self + } + /// Don't derive `Hash` for a given type. Regular /// expressions are supported. pub fn no_hash>(mut self, arg: T) -> Builder { @@ -1737,6 +1745,9 @@ struct BindgenOptions { /// The set of types that we should not derive `Debug` for. no_debug_types: RegexSet, + /// The set of types that we should not derive/impl `Default` for. + no_default_types: RegexSet, + /// The set of types that we should not derive `Hash` for. no_hash_types: RegexSet, @@ -1774,6 +1785,7 @@ impl BindgenOptions { &mut self.no_partialeq_types, &mut self.no_copy_types, &mut self.no_debug_types, + &mut self.no_default_types, &mut self.no_hash_types, ]; let record_matches = self.record_matches; @@ -1874,6 +1886,7 @@ impl Default for BindgenOptions { no_partialeq_types: Default::default(), no_copy_types: Default::default(), no_debug_types: Default::default(), + no_default_types: Default::default(), no_hash_types: Default::default(), array_pointers_in_arguments: false, wasm_import_module_name: None, diff --git a/src/options.rs b/src/options.rs index a850dbbbfe..d56b7a97cd 100644 --- a/src/options.rs +++ b/src/options.rs @@ -451,6 +451,13 @@ where .takes_value(true) .multiple(true) .number_of_values(1), + Arg::with_name("no-default") + .long("no-default") + .help("Avoid deriving/implement Default for types matching .") + .value_name("regex") + .takes_value(true) + .multiple(true) + .number_of_values(1), Arg::with_name("no-hash") .long("no-hash") .help("Avoid deriving Hash for types matching .") @@ -867,6 +874,12 @@ where } } + if let Some(no_default) = matches.values_of("no-default") { + for regex in no_default { + builder = builder.no_default(regex); + } + } + if let Some(no_hash) = matches.values_of("no-hash") { for regex in no_hash { builder = builder.no_hash(regex); diff --git a/tests/expectations/tests/no_default.rs b/tests/expectations/tests/no_default.rs new file mode 100644 index 0000000000..27ea3035c4 --- /dev/null +++ b/tests/expectations/tests/no_default.rs @@ -0,0 +1,23 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +///
+#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DefaultButWait { + pub whatever: ::std::os::raw::c_int, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DefaultButWaitDerived { + pub whatever: DefaultButWait, +} +impl Default for DefaultButWaitDerived { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} diff --git a/tests/expectations/tests/no_default_bypass_derive_default.rs b/tests/expectations/tests/no_default_bypass_derive_default.rs new file mode 100644 index 0000000000..4b97e3f489 --- /dev/null +++ b/tests/expectations/tests/no_default_bypass_derive_default.rs @@ -0,0 +1,22 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C)] +pub struct Generic { + pub t: [T; 40usize], + pub _phantom_0: ::std::marker::PhantomData<::std::cell::UnsafeCell>, +} +impl Default for Generic { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +pub struct NoDefault { + pub t: [T; 40usize], + pub _phantom_0: ::std::marker::PhantomData<::std::cell::UnsafeCell>, +} diff --git a/tests/expectations/tests/no_default_opaque.rs b/tests/expectations/tests/no_default_opaque.rs new file mode 100644 index 0000000000..3c928551e6 --- /dev/null +++ b/tests/expectations/tests/no_default_opaque.rs @@ -0,0 +1,26 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C)] +#[repr(align(4))] +#[derive(Debug, Copy, Clone)] +pub struct NoDefault { + pub _bindgen_opaque_blob: u32, +} +#[test] +fn bindgen_test_layout_NoDefault() { + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(NoDefault)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(NoDefault)) + ); +} diff --git a/tests/expectations/tests/no_default_whitelisted.rs b/tests/expectations/tests/no_default_whitelisted.rs new file mode 100644 index 0000000000..980f1575f0 --- /dev/null +++ b/tests/expectations/tests/no_default_whitelisted.rs @@ -0,0 +1,35 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct NoDefault { + pub i: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_NoDefault() { + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(NoDefault)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(NoDefault)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).i as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(NoDefault), + "::", + stringify!(i) + ) + ); +} diff --git a/tests/headers/no_default.hpp b/tests/headers/no_default.hpp new file mode 100644 index 0000000000..79d25fbdc2 --- /dev/null +++ b/tests/headers/no_default.hpp @@ -0,0 +1,11 @@ + +/**
*/ +template +class DefaultButWait { + int whatever; +}; + +template +class DefaultButWaitDerived { + DefaultButWait whatever; +}; diff --git a/tests/headers/no_default_bypass_derive_default.hpp b/tests/headers/no_default_bypass_derive_default.hpp new file mode 100644 index 0000000000..0f8339062f --- /dev/null +++ b/tests/headers/no_default_bypass_derive_default.hpp @@ -0,0 +1,11 @@ +// bindgen-flags: --no-default "NoDefault" + +template +class Generic { + T t[40]; +}; + +template +class NoDefault { + T t[40]; +}; diff --git a/tests/headers/no_default_opaque.hpp b/tests/headers/no_default_opaque.hpp new file mode 100644 index 0000000000..3245d8f93c --- /dev/null +++ b/tests/headers/no_default_opaque.hpp @@ -0,0 +1,5 @@ +// bindgen-flags: --opaque-type "NoDefault" --no-default "NoDefault" + +class NoDefault { + int i; +}; diff --git a/tests/headers/no_default_whitelisted.hpp b/tests/headers/no_default_whitelisted.hpp new file mode 100644 index 0000000000..12676be6d2 --- /dev/null +++ b/tests/headers/no_default_whitelisted.hpp @@ -0,0 +1,5 @@ +// bindgen-flags: --whitelist-type "NoDefault" --no-default "NoDefault" + +class NoDefault { + int i; +};