Skip to content

Commit

Permalink
Add --no-default <regex> flag
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
varphone authored and emilio committed Nov 26, 2020
1 parent 30fb4ec commit d808261
Show file tree
Hide file tree
Showing 16 changed files with 234 additions and 4 deletions.
1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Expand Up @@ -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)
Expand Down
48 changes: 48 additions & 0 deletions 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 <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],
}
}
}
```
8 changes: 5 additions & 3 deletions src/codegen/mod.rs
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/ir/analysis/derive.rs
Expand Up @@ -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,
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/ir/annotations.rs
Expand Up @@ -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>,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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(),
Expand Down
6 changes: 6 additions & 0 deletions src/ir/context.rs
Expand Up @@ -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("::");
Expand Down
13 changes: 13 additions & 0 deletions src/lib.rs
Expand Up @@ -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"),
];

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
13 changes: 13 additions & 0 deletions src/options.rs
Expand Up @@ -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>.")
Expand Down Expand Up @@ -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);
Expand Down
23 changes: 23 additions & 0 deletions tests/expectations/tests/no_default.rs
@@ -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() }
}
}
22 changes: 22 additions & 0 deletions 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<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>>,
}
26 changes: 26 additions & 0 deletions 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::<NoDefault>(),
4usize,
concat!("Size of: ", stringify!(NoDefault))
);
assert_eq!(
::std::mem::align_of::<NoDefault>(),
4usize,
concat!("Alignment of ", stringify!(NoDefault))
);
}
35 changes: 35 additions & 0 deletions 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::<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)
)
);
}
11 changes: 11 additions & 0 deletions tests/headers/no_default.hpp
@@ -0,0 +1,11 @@

/** <div rustbindgen nodefault></div> */
template<typename T>
class DefaultButWait {
int whatever;
};

template<typename T>
class DefaultButWaitDerived {
DefaultButWait<T> whatever;
};
11 changes: 11 additions & 0 deletions tests/headers/no_default_bypass_derive_default.hpp
@@ -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];
};
5 changes: 5 additions & 0 deletions tests/headers/no_default_opaque.hpp
@@ -0,0 +1,5 @@
// bindgen-flags: --opaque-type "NoDefault" --no-default "NoDefault"

class NoDefault {
int i;
};
5 changes: 5 additions & 0 deletions tests/headers/no_default_whitelisted.hpp
@@ -0,0 +1,5 @@
// bindgen-flags: --whitelist-type "NoDefault" --no-default "NoDefault"

class NoDefault {
int i;
};

0 comments on commit d808261

Please sign in to comment.