Add --no-default <regex> 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.
This commit is contained in:
Varphone Wong 2020-11-25 17:24:46 +08:00 committed by Emilio Cobos Álvarez
parent 30fb4eccb3
commit d8082613e2
16 changed files with 234 additions and 4 deletions

View File

@ -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)

48
book/src/nodefault.md Normal file
View File

@ -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 <regex>`
### Annotations
```c
/**
* We need to specify some preset values as the Default of Header.
*
* for example:
*
* <div rustbindgen nodefault></div>
*/
struct Header {
unsigned int magic;
unsigned char data[252];
};
...
```
### Customize Implements
```rust,ignore
// Include the generated bindings.
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
impl Default for Header {
fn default() -> Self {
Self {
magic: 0x10203040u32,
data: [0; 252usize],
}
}
}
```

View File

@ -118,7 +118,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;
}
@ -1900,8 +1900,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);

View File

@ -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,
}
}

View File

@ -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<bool>,
@ -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<bool> {
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(),

View File

@ -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("::");

View File

@ -298,6 +298,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"),
];
@ -1446,6 +1447,13 @@ impl Builder {
self
}
/// Don't derive/impl `Default` for a given type. Regular
/// expressions are supported.
pub fn no_default<T: Into<String>>(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<T: Into<String>>(mut self, arg: T) -> Builder {
@ -1745,6 +1753,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,
@ -1786,6 +1797,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;
@ -1886,6 +1898,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,

View File

@ -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 <regex>.")
.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 <regex>.")
@ -871,6 +878,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);

View File

@ -0,0 +1,23 @@
#![allow(
dead_code,
non_snake_case,
non_camel_case_types,
non_upper_case_globals
)]
/// <div rustbindgen nodefault></div>
#[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() }
}
}

View File

@ -0,0 +1,22 @@
#![allow(
dead_code,
non_snake_case,
non_camel_case_types,
non_upper_case_globals
)]
#[repr(C)]
pub struct Generic<T> {
pub t: [T; 40usize],
pub _phantom_0: ::std::marker::PhantomData<::std::cell::UnsafeCell<T>>,
}
impl<T> Default for Generic<T> {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
#[repr(C)]
pub struct NoDefault<T> {
pub t: [T; 40usize],
pub _phantom_0: ::std::marker::PhantomData<::std::cell::UnsafeCell<T>>,
}

View File

@ -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::<NoDefault>(),
4usize,
concat!("Size of: ", stringify!(NoDefault))
);
assert_eq!(
::std::mem::align_of::<NoDefault>(),
4usize,
concat!("Alignment of ", stringify!(NoDefault))
);
}

View File

@ -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::<NoDefault>(),
4usize,
concat!("Size of: ", stringify!(NoDefault))
);
assert_eq!(
::std::mem::align_of::<NoDefault>(),
4usize,
concat!("Alignment of ", stringify!(NoDefault))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<NoDefault>())).i as *const _ as usize },
0usize,
concat!(
"Offset of field: ",
stringify!(NoDefault),
"::",
stringify!(i)
)
);
}

View File

@ -0,0 +1,11 @@
/** <div rustbindgen nodefault></div> */
template<typename T>
class DefaultButWait {
int whatever;
};
template<typename T>
class DefaultButWaitDerived {
DefaultButWait<T> whatever;
};

View File

@ -0,0 +1,11 @@
// bindgen-flags: --no-default "NoDefault"
template<typename T>
class Generic {
T t[40];
};
template<typename T>
class NoDefault {
T t[40];
};

View File

@ -0,0 +1,5 @@
// bindgen-flags: --opaque-type "NoDefault" --no-default "NoDefault"
class NoDefault {
int i;
};

View File

@ -0,0 +1,5 @@
// bindgen-flags: --whitelist-type "NoDefault" --no-default "NoDefault"
class NoDefault {
int i;
};