Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --no-default <regex> flag #1930

Merged
merged 1 commit into from Nov 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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;
};