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.
diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md
index e2347fb..16291c3 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 0000000..37896f6
--- /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 <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],
+ }
+ }
+}
+```
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 754c2c8..15aea22 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -118,7 +118,7 @@
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 @@
}
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 48c544f..6c50fa6 100644
--- a/src/ir/analysis/derive.rs
+++ b/src/ir/analysis/derive.rs
@@ -446,11 +446,11 @@
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 da4ef4f..12664f6 100644
--- a/src/ir/annotations.rs
+++ b/src/ir/annotations.rs
@@ -40,6 +40,8 @@
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 @@
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 @@
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 @@
"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 bb0c3f9..1a54375 100644
--- a/src/ir/context.rs
+++ b/src/ir/context.rs
@@ -2591,6 +2591,12 @@
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 37c301d..09410b5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -298,6 +298,7 @@
(&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 @@
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 @@
/// 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 @@
&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 @@
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 8c85aba..657c3c9 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -451,6 +451,13 @@
.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 @@
}
}
+ 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 0000000..27ea303
--- /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
+)]
+
+/// <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() }
+ }
+}
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 0000000..4b97e3f
--- /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<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>>,
+}
diff --git a/tests/expectations/tests/no_default_opaque.rs b/tests/expectations/tests/no_default_opaque.rs
new file mode 100644
index 0000000..3c92855
--- /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::<NoDefault>(),
+ 4usize,
+ concat!("Size of: ", stringify!(NoDefault))
+ );
+ assert_eq!(
+ ::std::mem::align_of::<NoDefault>(),
+ 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 0000000..980f157
--- /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::<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)
+ )
+ );
+}
diff --git a/tests/headers/no_default.hpp b/tests/headers/no_default.hpp
new file mode 100644
index 0000000..79d25fb
--- /dev/null
+++ b/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;
+};
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 0000000..0f83390
--- /dev/null
+++ b/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];
+};
diff --git a/tests/headers/no_default_opaque.hpp b/tests/headers/no_default_opaque.hpp
new file mode 100644
index 0000000..3245d8f
--- /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 0000000..12676be
--- /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;
+};