Skip to content

Add --no-debug <regex> flag #1858

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

Merged
merged 1 commit into from
Aug 3, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- [Treating a Type as an Opaque Blob of Bytes](./opaque.md)
- [Replacing One Type with Another](./replacing-types.md)
- [Preventing the Derivation of `Copy` and `Clone`](./nocopy.md)
- [Preventing the Derivation of `Debug`](./nodebug.md)
- [Generating Bindings to C++](./cpp.md)
- [Generating Bindings to Objective-c](./objc.md)
- [Using Unions](./using-unions.md)
Expand Down
40 changes: 40 additions & 0 deletions book/src/nodebug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Preventing the Derivation of `Debug`

`bindgen` will attempt to derive the `Debug` traits on a best-effort
basis. Sometimes, it might not understand that although adding `#[derive(Debug)]` to a translated type definition will compile, it still shouldn't do
that for reasons it can't know. In these cases, the `nodebug` annotation can be
used to prevent bindgen to autoderive the `Debug` traits for a type.

### Library

* [`bindgen::Builder::no_debug`](https://docs.rs/bindgen/0.54.2/bindgen/struct.Builder.html#method.no_debug)

### Command Line

* `--no-debug <regex>`

### Annotations

```c
/**
* Although bindgen can't know, this enum is not safe to format the output.
* the value may be combined with multiple bits in many C/C++ cases,
* for example:
*
* <div rustbindgen nodebug></div>
*/
enum AVRounding {
AV_ROUND_ZERO = 0,
AV_ROUND_INF = 1,
AV_ROUND_DOWN = 2,
AV_ROUND_UP = 3,
AV_ROUND_NEAR_INF = 5,
AV_ROUND_PASS_MINMAX = 8192,
};

// Prototype
int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding) av_const;

// Call
int64_t pts = av_rescale_rnd(40000, 3600, 90000, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
```
8 changes: 5 additions & 3 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ bitflags! {
fn derives_of_item(item: &Item, ctx: &BindgenContext) -> DerivableTraits {
let mut derivable_traits = DerivableTraits::empty();

if item.can_derive_debug(ctx) {
if item.can_derive_debug(ctx) && !item.annotations().disallow_debug() {
derivable_traits |= DerivableTraits::DEBUG;
}

Expand Down Expand Up @@ -1885,8 +1885,10 @@ impl CodeGenerator for CompInfo {

let derivable_traits = derives_of_item(item, ctx);
if !derivable_traits.contains(DerivableTraits::DEBUG) {
needs_debug_impl =
ctx.options().derive_debug && ctx.options().impl_debug
needs_debug_impl = ctx.options().derive_debug &&
ctx.options().impl_debug &&
!ctx.no_debug_by_name(item) &&
!item.annotations().disallow_debug();
}

if !derivable_traits.contains(DerivableTraits::DEFAULT) {
Expand Down
1 change: 1 addition & 0 deletions src/ir/analysis/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,7 @@ impl DeriveTrait {
fn not_by_name(&self, ctx: &BindgenContext, item: &Item) -> bool {
match self {
DeriveTrait::Copy => ctx.no_copy_by_name(item),
DeriveTrait::Debug => ctx.no_debug_by_name(item),
DeriveTrait::Hash => ctx.no_hash_by_name(item),
DeriveTrait::PartialEqOrPartialOrd => {
ctx.no_partialeq_by_name(item)
Expand Down
9 changes: 9 additions & 0 deletions src/ir/annotations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ pub struct Annotations {
/// Manually disable deriving copy/clone on this type. Only applies to
/// struct or union types.
disallow_copy: bool,
/// Manually disable deriving debug on this type.
disallow_debug: 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 @@ -78,6 +80,7 @@ impl Default for Annotations {
hide: false,
use_instead_of: None,
disallow_copy: false,
disallow_debug: false,
private_fields: None,
accessor_kind: None,
constify_enum_variant: false,
Expand Down Expand Up @@ -147,6 +150,11 @@ impl Annotations {
self.disallow_copy
}

/// Should we avoid implementing the `Debug` trait?
pub fn disallow_debug(&self) -> bool {
self.disallow_debug
}

/// Should the fields be private?
pub fn private_fields(&self) -> Option<bool> {
self.private_fields
Expand All @@ -172,6 +180,7 @@ impl Annotations {
"opaque" => self.opaque = true,
"hide" => self.hide = true,
"nocopy" => self.disallow_copy = true,
"nodebug" => self.disallow_debug = 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
Original file line number Diff line number Diff line change
Expand Up @@ -2635,6 +2635,12 @@ If you encounter an error missing from this list, please file an issue or a PR!"
self.options().no_copy_types.matches(&name)
}

/// Check if `--no-debug` flag is enabled for this item.
pub fn no_debug_by_name(&self, item: &Item) -> bool {
let name = item.path_for_whitelisting(self)[1..].join("::");
self.options().no_debug_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
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ impl Builder {
(&self.options.whitelisted_vars, "--whitelist-var"),
(&self.options.no_partialeq_types, "--no-partialeq"),
(&self.options.no_copy_types, "--no-copy"),
(&self.options.no_debug_types, "--no-debug"),
(&self.options.no_hash_types, "--no-hash"),
];

Expand Down Expand Up @@ -1410,6 +1411,13 @@ impl Builder {
self
}

/// Don't derive `Debug` for a given type. Regular
/// expressions are supported.
pub fn no_debug<T: Into<String>>(mut self, arg: T) -> Self {
self.options.no_debug_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 @@ -1691,6 +1699,9 @@ struct BindgenOptions {
/// The set of types that we should not derive `Copy` for.
no_copy_types: RegexSet,

/// The set of types that we should not derive `Debug` for.
no_debug_types: RegexSet,

/// The set of types that we should not derive `Hash` for.
no_hash_types: RegexSet,

Expand Down Expand Up @@ -1727,6 +1738,7 @@ impl BindgenOptions {
&mut self.new_type_alias_deref,
&mut self.no_partialeq_types,
&mut self.no_copy_types,
&mut self.no_debug_types,
&mut self.no_hash_types,
];
let record_matches = self.record_matches;
Expand Down Expand Up @@ -1824,6 +1836,7 @@ impl Default for BindgenOptions {
rustfmt_configuration_file: None,
no_partialeq_types: Default::default(),
no_copy_types: Default::default(),
no_debug_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
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,13 @@ where
.takes_value(true)
.multiple(true)
.number_of_values(1),
Arg::with_name("no-debug")
.long("no-debug")
.help("Avoid deriving Debug 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 @@ -831,6 +838,12 @@ where
}
}

if let Some(no_debug) = matches.values_of("no-debug") {
for regex in no_debug {
builder = builder.no_debug(regex);
}
}

if let Some(no_hash) = matches.values_of("no-hash") {
for regex in no_hash {
builder = builder.no_hash(regex);
Expand Down
13 changes: 13 additions & 0 deletions tests/expectations/tests/no_debug.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#![allow(
dead_code,
non_snake_case,
non_camel_case_types,
non_upper_case_globals
)]

/// <div rustbindgen nodebug></div>
#[repr(C)]
#[derive(Default, Copy, Clone)]
pub struct DebugButWait {
pub whatever: ::std::os::raw::c_int,
}
32 changes: 32 additions & 0 deletions tests/expectations/tests/no_debug_bypass_impl_debug.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#![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() }
}
}
impl<T> ::std::fmt::Debug for Generic<T> {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "Generic {{ t: Array with length 40 }}")
}
}
#[repr(C)]
pub struct NoDebug<T> {
pub t: [T; 40usize],
pub _phantom_0: ::std::marker::PhantomData<::std::cell::UnsafeCell<T>>,
}
impl<T> Default for NoDebug<T> {
fn default() -> Self {
unsafe { ::std::mem::zeroed() }
}
}
26 changes: 26 additions & 0 deletions tests/expectations/tests/no_debug_opaque.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#![allow(
dead_code,
non_snake_case,
non_camel_case_types,
non_upper_case_globals
)]

#[repr(C)]
#[repr(align(4))]
#[derive(Default, Copy, Clone)]
pub struct NoDebug {
pub _bindgen_opaque_blob: u32,
}
#[test]
fn bindgen_test_layout_NoDebug() {
assert_eq!(
::std::mem::size_of::<NoDebug>(),
4usize,
concat!("Size of: ", stringify!(NoDebug))
);
assert_eq!(
::std::mem::align_of::<NoDebug>(),
4usize,
concat!("Alignment of ", stringify!(NoDebug))
);
}
30 changes: 30 additions & 0 deletions tests/expectations/tests/no_debug_whitelisted.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#![allow(
dead_code,
non_snake_case,
non_camel_case_types,
non_upper_case_globals
)]

#[repr(C)]
#[derive(Default, Copy, Clone)]
pub struct NoDebug {
pub i: ::std::os::raw::c_int,
}
#[test]
fn bindgen_test_layout_NoDebug() {
assert_eq!(
::std::mem::size_of::<NoDebug>(),
4usize,
concat!("Size of: ", stringify!(NoDebug))
);
assert_eq!(
::std::mem::align_of::<NoDebug>(),
4usize,
concat!("Alignment of ", stringify!(NoDebug))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<NoDebug>())).i as *const _ as usize },
0usize,
concat!("Offset of field: ", stringify!(NoDebug), "::", stringify!(i))
);
}
6 changes: 6 additions & 0 deletions tests/headers/no_debug.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

/** <div rustbindgen nodebug></div> */
template<typename T>
class DebugButWait {
int whatever;
};
11 changes: 11 additions & 0 deletions tests/headers/no_debug_bypass_impl_debug.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// bindgen-flags: --no-debug "NoDebug" --impl-debug

template<typename T>
class Generic {
T t[40];
};

template<typename T>
class NoDebug {
T t[40];
};
5 changes: 5 additions & 0 deletions tests/headers/no_debug_opaque.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// bindgen-flags: --opaque-type "NoDebug" --no-debug "NoDebug"

class NoDebug {
int i;
};
5 changes: 5 additions & 0 deletions tests/headers/no_debug_whitelisted.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// bindgen-flags: --whitelist-type "NoDebug" --no-debug "NoDebug"

class NoDebug {
int i;
};