Skip to content

Commit 78b5172

Browse files
committed
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.
1 parent 7792d63 commit 78b5172

16 files changed

+234
-4
lines changed

book/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- [Replacing One Type with Another](./replacing-types.md)
1919
- [Preventing the Derivation of `Copy` and `Clone`](./nocopy.md)
2020
- [Preventing the Derivation of `Debug`](./nodebug.md)
21+
- [Preventing the Derivation of `Default`](./nodefault.md)
2122
- [Generating Bindings to C++](./cpp.md)
2223
- [Generating Bindings to Objective-c](./objc.md)
2324
- [Using Unions](./using-unions.md)

book/src/nodefault.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Preventing the Derivation of `Default`
2+
3+
`bindgen` will attempt to derive/impl the `Default` traits on a best-effort basis.
4+
Sometimes, we need customize the implement of `Default` for certain types,
5+
In these cases, the `nodefault` annotation can be used to prevent bindgen
6+
to autoderive the `Default` traits for a type.
7+
8+
### Library
9+
10+
* [`bindgen::Builder::no_default`](https://docs.rs/bindgen/latest/bindgen/struct.Builder.html#method.no_default)
11+
12+
### Command Line
13+
14+
* `--no-default <regex>`
15+
16+
### Annotations
17+
18+
```c
19+
/**
20+
* We need to specify some preset values as the Default of Header.
21+
*
22+
* for example:
23+
*
24+
* <div rustbindgen nodefault></div>
25+
*/
26+
struct Header {
27+
unsigned int magic;
28+
unsigned char data[252];
29+
};
30+
31+
...
32+
```
33+
34+
### Customize Implements
35+
36+
```rust
37+
// Inlcude the generated bindings.
38+
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
39+
40+
impl Default for Header {
41+
fn default() -> Self {
42+
Self {
43+
magic: 0x10203040u32,
44+
data: [0; 252usize],
45+
}
46+
}
47+
}
48+
```

src/codegen/mod.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ fn derives_of_item(item: &Item, ctx: &BindgenContext) -> DerivableTraits {
118118
derivable_traits |= DerivableTraits::DEBUG;
119119
}
120120

121-
if item.can_derive_default(ctx) {
121+
if item.can_derive_default(ctx) && !item.annotations().disallow_default() {
122122
derivable_traits |= DerivableTraits::DEFAULT;
123123
}
124124

@@ -1900,8 +1900,10 @@ impl CodeGenerator for CompInfo {
19001900
}
19011901

19021902
if !derivable_traits.contains(DerivableTraits::DEFAULT) {
1903-
needs_default_impl =
1904-
ctx.options().derive_default && !self.is_forward_declaration();
1903+
needs_default_impl = ctx.options().derive_default &&
1904+
!self.is_forward_declaration() &&
1905+
!ctx.no_default_by_name(item) &&
1906+
!item.annotations().disallow_default();
19051907
}
19061908

19071909
let all_template_params = item.all_template_params(ctx);

src/ir/analysis/derive.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,11 +446,11 @@ impl DeriveTrait {
446446
match self {
447447
DeriveTrait::Copy => ctx.no_copy_by_name(item),
448448
DeriveTrait::Debug => ctx.no_debug_by_name(item),
449+
DeriveTrait::Default => ctx.no_default_by_name(item),
449450
DeriveTrait::Hash => ctx.no_hash_by_name(item),
450451
DeriveTrait::PartialEqOrPartialOrd => {
451452
ctx.no_partialeq_by_name(item)
452453
}
453-
_ => false,
454454
}
455455
}
456456

src/ir/annotations.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ pub struct Annotations {
4040
disallow_copy: bool,
4141
/// Manually disable deriving debug on this type.
4242
disallow_debug: bool,
43+
/// Manually disable deriving/implement default on this type.
44+
disallow_default: bool,
4345
/// Whether fields should be marked as private or not. You can set this on
4446
/// structs (it will apply to all the fields), or individual fields.
4547
private_fields: Option<bool>,
@@ -81,6 +83,7 @@ impl Default for Annotations {
8183
use_instead_of: None,
8284
disallow_copy: false,
8385
disallow_debug: false,
86+
disallow_default: false,
8487
private_fields: None,
8588
accessor_kind: None,
8689
constify_enum_variant: false,
@@ -155,6 +158,11 @@ impl Annotations {
155158
self.disallow_debug
156159
}
157160

161+
/// Should we avoid implementing the `Default` trait?
162+
pub fn disallow_default(&self) -> bool {
163+
self.disallow_default
164+
}
165+
158166
/// Should the fields be private?
159167
pub fn private_fields(&self) -> Option<bool> {
160168
self.private_fields
@@ -181,6 +189,7 @@ impl Annotations {
181189
"hide" => self.hide = true,
182190
"nocopy" => self.disallow_copy = true,
183191
"nodebug" => self.disallow_debug = true,
192+
"nodefault" => self.disallow_default = true,
184193
"replaces" => {
185194
self.use_instead_of = Some(
186195
attr.value.split("::").map(Into::into).collect(),

src/ir/context.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2591,6 +2591,12 @@ If you encounter an error missing from this list, please file an issue or a PR!"
25912591
self.options().no_debug_types.matches(&name)
25922592
}
25932593

2594+
/// Check if `--no-default` flag is enabled for this item.
2595+
pub fn no_default_by_name(&self, item: &Item) -> bool {
2596+
let name = item.path_for_whitelisting(self)[1..].join("::");
2597+
self.options().no_default_types.matches(&name)
2598+
}
2599+
25942600
/// Check if `--no-hash` flag is enabled for this item.
25952601
pub fn no_hash_by_name(&self, item: &Item) -> bool {
25962602
let name = item.path_for_whitelisting(self)[1..].join("::");

src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ impl Builder {
305305
(&self.options.no_partialeq_types, "--no-partialeq"),
306306
(&self.options.no_copy_types, "--no-copy"),
307307
(&self.options.no_debug_types, "--no-debug"),
308+
(&self.options.no_default_types, "--no-default"),
308309
(&self.options.no_hash_types, "--no-hash"),
309310
];
310311

@@ -1453,6 +1454,13 @@ impl Builder {
14531454
self
14541455
}
14551456

1457+
/// Don't derive/impl `Default` for a given type. Regular
1458+
/// expressions are supported.
1459+
pub fn no_default<T: Into<String>>(mut self, arg: T) -> Self {
1460+
self.options.no_default_types.insert(arg.into());
1461+
self
1462+
}
1463+
14561464
/// Don't derive `Hash` for a given type. Regular
14571465
/// expressions are supported.
14581466
pub fn no_hash<T: Into<String>>(mut self, arg: T) -> Builder {
@@ -1752,6 +1760,9 @@ struct BindgenOptions {
17521760
/// The set of types that we should not derive `Debug` for.
17531761
no_debug_types: RegexSet,
17541762

1763+
/// The set of types that we should not derive/impl `Default` for.
1764+
no_default_types: RegexSet,
1765+
17551766
/// The set of types that we should not derive `Hash` for.
17561767
no_hash_types: RegexSet,
17571768

@@ -1793,6 +1804,7 @@ impl BindgenOptions {
17931804
&mut self.no_partialeq_types,
17941805
&mut self.no_copy_types,
17951806
&mut self.no_debug_types,
1807+
&mut self.no_default_types,
17961808
&mut self.no_hash_types,
17971809
];
17981810
let record_matches = self.record_matches;
@@ -1893,6 +1905,7 @@ impl Default for BindgenOptions {
18931905
no_partialeq_types: Default::default(),
18941906
no_copy_types: Default::default(),
18951907
no_debug_types: Default::default(),
1908+
no_default_types: Default::default(),
18961909
no_hash_types: Default::default(),
18971910
array_pointers_in_arguments: false,
18981911
wasm_import_module_name: None,

src/options.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,13 @@ where
451451
.takes_value(true)
452452
.multiple(true)
453453
.number_of_values(1),
454+
Arg::with_name("no-default")
455+
.long("no-default")
456+
.help("Avoid deriving/implement Default for types matching <regex>.")
457+
.value_name("regex")
458+
.takes_value(true)
459+
.multiple(true)
460+
.number_of_values(1),
454461
Arg::with_name("no-hash")
455462
.long("no-hash")
456463
.help("Avoid deriving Hash for types matching <regex>.")
@@ -871,6 +878,12 @@ where
871878
}
872879
}
873880

881+
if let Some(no_default) = matches.values_of("no-default") {
882+
for regex in no_default {
883+
builder = builder.no_default(regex);
884+
}
885+
}
886+
874887
if let Some(no_hash) = matches.values_of("no-hash") {
875888
for regex in no_hash {
876889
builder = builder.no_hash(regex);
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#![allow(
2+
dead_code,
3+
non_snake_case,
4+
non_camel_case_types,
5+
non_upper_case_globals
6+
)]
7+
8+
/// <div rustbindgen nodefault></div>
9+
#[repr(C)]
10+
#[derive(Debug, Copy, Clone)]
11+
pub struct DefaultButWait {
12+
pub whatever: ::std::os::raw::c_int,
13+
}
14+
#[repr(C)]
15+
#[derive(Debug, Copy, Clone)]
16+
pub struct DefaultButWaitDerived {
17+
pub whatever: DefaultButWait,
18+
}
19+
impl Default for DefaultButWaitDerived {
20+
fn default() -> Self {
21+
unsafe { ::std::mem::zeroed() }
22+
}
23+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#![allow(
2+
dead_code,
3+
non_snake_case,
4+
non_camel_case_types,
5+
non_upper_case_globals
6+
)]
7+
8+
#[repr(C)]
9+
pub struct Generic<T> {
10+
pub t: [T; 40usize],
11+
pub _phantom_0: ::std::marker::PhantomData<::std::cell::UnsafeCell<T>>,
12+
}
13+
impl<T> Default for Generic<T> {
14+
fn default() -> Self {
15+
unsafe { ::std::mem::zeroed() }
16+
}
17+
}
18+
#[repr(C)]
19+
pub struct NoDefault<T> {
20+
pub t: [T; 40usize],
21+
pub _phantom_0: ::std::marker::PhantomData<::std::cell::UnsafeCell<T>>,
22+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#![allow(
2+
dead_code,
3+
non_snake_case,
4+
non_camel_case_types,
5+
non_upper_case_globals
6+
)]
7+
8+
#[repr(C)]
9+
#[repr(align(4))]
10+
#[derive(Debug, Copy, Clone)]
11+
pub struct NoDefault {
12+
pub _bindgen_opaque_blob: u32,
13+
}
14+
#[test]
15+
fn bindgen_test_layout_NoDefault() {
16+
assert_eq!(
17+
::std::mem::size_of::<NoDefault>(),
18+
4usize,
19+
concat!("Size of: ", stringify!(NoDefault))
20+
);
21+
assert_eq!(
22+
::std::mem::align_of::<NoDefault>(),
23+
4usize,
24+
concat!("Alignment of ", stringify!(NoDefault))
25+
);
26+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#![allow(
2+
dead_code,
3+
non_snake_case,
4+
non_camel_case_types,
5+
non_upper_case_globals
6+
)]
7+
8+
#[repr(C)]
9+
#[derive(Debug, Copy, Clone)]
10+
pub struct NoDefault {
11+
pub i: ::std::os::raw::c_int,
12+
}
13+
#[test]
14+
fn bindgen_test_layout_NoDefault() {
15+
assert_eq!(
16+
::std::mem::size_of::<NoDefault>(),
17+
4usize,
18+
concat!("Size of: ", stringify!(NoDefault))
19+
);
20+
assert_eq!(
21+
::std::mem::align_of::<NoDefault>(),
22+
4usize,
23+
concat!("Alignment of ", stringify!(NoDefault))
24+
);
25+
assert_eq!(
26+
unsafe { &(*(::std::ptr::null::<NoDefault>())).i as *const _ as usize },
27+
0usize,
28+
concat!(
29+
"Offset of field: ",
30+
stringify!(NoDefault),
31+
"::",
32+
stringify!(i)
33+
)
34+
);
35+
}

tests/headers/no_default.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
/** <div rustbindgen nodefault></div> */
3+
template<typename T>
4+
class DefaultButWait {
5+
int whatever;
6+
};
7+
8+
template<typename T>
9+
class DefaultButWaitDerived {
10+
DefaultButWait<T> whatever;
11+
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// bindgen-flags: --no-default "NoDefault"
2+
3+
template<typename T>
4+
class Generic {
5+
T t[40];
6+
};
7+
8+
template<typename T>
9+
class NoDefault {
10+
T t[40];
11+
};

tests/headers/no_default_opaque.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// bindgen-flags: --opaque-type "NoDefault" --no-default "NoDefault"
2+
3+
class NoDefault {
4+
int i;
5+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// bindgen-flags: --whitelist-type "NoDefault" --no-default "NoDefault"
2+
3+
class NoDefault {
4+
int i;
5+
};

0 commit comments

Comments
 (0)