Skip to content

Commit 0780f80

Browse files
author
Jethro Beekman
committed
Add callback to check derives for blocklisted types
Fixes #1454 #2003
1 parent 2a46e29 commit 0780f80

File tree

6 files changed

+148
-9
lines changed

6 files changed

+148
-9
lines changed

src/callbacks.rs

+19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! A public API for more fine-grained customization of bindgen behavior.
22
3+
pub use crate::ir::analysis::DeriveTrait;
4+
pub use crate::ir::derive::CanDerive as ImplementsTrait;
35
pub use crate::ir::enum_ty::{EnumVariantCustomBehavior, EnumVariantValue};
46
pub use crate::ir::int::IntKind;
57
use std::fmt;
@@ -76,4 +78,21 @@ pub trait ParseCallbacks: fmt::Debug + UnwindSafe {
7678

7779
/// This will be called on every file inclusion, with the full path of the included file.
7880
fn include_file(&self, _filename: &str) {}
81+
82+
/// This will be called to determine whether a particular blocklisted type
83+
/// implements a trait or not. This will be used to implement traits on
84+
/// other types containing the blocklisted type.
85+
///
86+
/// * `None`: use the default behavior
87+
/// * `Some(ImplementsTrait::Yes)`: `_name` implements `_derive_trait`
88+
/// * `Some(ImplementsTrait::Manually)`: any type including `_name` can't
89+
/// derive `_derive_trait` but can implemented it manually
90+
/// * `Some(ImplementsTrait::No)`: `_name` doesn't implement `_derive_trait`
91+
fn blocklisted_type_implements_trait(
92+
&self,
93+
_name: &str,
94+
_derive_trait: DeriveTrait,
95+
) -> Option<ImplementsTrait> {
96+
None
97+
}
7998
}

src/ir/analysis/derive.rs

+19-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::ir::ty::{Type, TypeKind};
1616
use crate::{Entry, HashMap, HashSet};
1717

1818
/// Which trait to consider when doing the `CannotDerive` analysis.
19-
#[derive(Debug, Copy, Clone)]
19+
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
2020
pub enum DeriveTrait {
2121
/// The `Copy` trait.
2222
Copy,
@@ -139,11 +139,24 @@ impl<'ctx> CannotDerive<'ctx> {
139139

140140
fn constrain_type(&mut self, item: &Item, ty: &Type) -> CanDerive {
141141
if !self.ctx.allowlisted_items().contains(&item.id()) {
142-
trace!(
143-
" cannot derive {} for blocklisted type",
144-
self.derive_trait
145-
);
146-
return CanDerive::No;
142+
let can_derive = self
143+
.ctx
144+
.blocklisted_type_implements_trait(item, self.derive_trait);
145+
match can_derive {
146+
CanDerive::Yes => trace!(
147+
" blocklisted type explicitly implements {}",
148+
self.derive_trait
149+
),
150+
CanDerive::Manually => trace!(
151+
" blocklisted type requires manual implementation of {}",
152+
self.derive_trait
153+
),
154+
CanDerive::No => trace!(
155+
" cannot derive {} for blocklisted type",
156+
self.derive_trait
157+
),
158+
}
159+
return can_derive;
147160
}
148161

149162
if self.derive_trait.not_by_name(self.ctx, &item) {

src/ir/context.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use cexpr;
2828
use clang_sys;
2929
use proc_macro2::{Ident, Span};
3030
use std::borrow::Cow;
31-
use std::cell::Cell;
31+
use std::cell::{Cell, RefCell};
3232
use std::collections::HashMap as StdHashMap;
3333
use std::iter::IntoIterator;
3434
use std::mem;
@@ -380,6 +380,10 @@ pub struct BindgenContext {
380380
/// computed after parsing our IR, and before running any of our analyses.
381381
allowlisted: Option<ItemSet>,
382382

383+
/// Cache for calls to `ParseCallbacks::blocklisted_type_implements_trait`
384+
blocklisted_types_implement_traits:
385+
RefCell<HashMap<DeriveTrait, HashMap<ItemId, CanDerive>>>,
386+
383387
/// The set of `ItemId`s that are allowlisted for code generation _and_ that
384388
/// we should generate accounting for the codegen options.
385389
///
@@ -560,6 +564,7 @@ If you encounter an error missing from this list, please file an issue or a PR!"
560564
options,
561565
generated_bindgen_complex: Cell::new(false),
562566
allowlisted: None,
567+
blocklisted_types_implement_traits: Default::default(),
563568
codegen_items: None,
564569
used_template_parameters: None,
565570
need_bitfield_allocation: Default::default(),
@@ -2205,6 +2210,37 @@ If you encounter an error missing from this list, please file an issue or a PR!"
22052210
self.allowlisted.as_ref().unwrap()
22062211
}
22072212

2213+
/// Check whether a particular blocklisted type implements a trait or not.
2214+
/// Results may be cached.
2215+
pub fn blocklisted_type_implements_trait(
2216+
&self,
2217+
item: &Item,
2218+
derive_trait: DeriveTrait,
2219+
) -> CanDerive {
2220+
assert!(self.in_codegen_phase());
2221+
assert!(self.current_module == self.root_module);
2222+
2223+
let cb = match self.options.parse_callbacks {
2224+
Some(ref cb) => cb,
2225+
None => return CanDerive::No,
2226+
};
2227+
2228+
*self
2229+
.blocklisted_types_implement_traits
2230+
.borrow_mut()
2231+
.entry(derive_trait)
2232+
.or_default()
2233+
.entry(item.id())
2234+
.or_insert_with(|| {
2235+
item.expect_type()
2236+
.name()
2237+
.and_then(|name| {
2238+
cb.blocklisted_type_implements_trait(name, derive_trait)
2239+
})
2240+
.unwrap_or(CanDerive::No)
2241+
})
2242+
}
2243+
22082244
/// Get a reference to the set of items we should generate.
22092245
pub fn codegen_items(&self) -> &ItemSet {
22102246
assert!(self.in_codegen_phase());
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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)]
10+
pub struct extern_type;
11+
12+
#[repr(C)]
13+
#[derive(Debug)]
14+
pub struct local_type {
15+
pub inner: extern_type,
16+
}
17+
#[test]
18+
fn bindgen_test_layout_local_type() {
19+
assert_eq!(
20+
::std::mem::size_of::<local_type>(),
21+
0usize,
22+
concat!("Size of: ", stringify!(local_type))
23+
);
24+
assert_eq!(
25+
::std::mem::align_of::<local_type>(),
26+
1usize,
27+
concat!("Alignment of ", stringify!(local_type))
28+
);
29+
assert_eq!(
30+
unsafe {
31+
&(*(::std::ptr::null::<local_type>())).inner as *const _ as usize
32+
},
33+
0usize,
34+
concat!(
35+
"Offset of field: ",
36+
stringify!(local_type),
37+
"::",
38+
stringify!(inner)
39+
)
40+
);
41+
}

tests/headers/issue-1454.h

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// bindgen-flags: --no-recursive-allowlist --allowlist-type "local_type" --with-derive-hash --no-derive-copy --no-derive-default --raw-line "#[repr(C)] #[derive(Debug)] pub struct extern_type;"
2+
// bindgen-parse-callbacks: blocklisted-type-implements-trait
3+
4+
struct extern_type {};
5+
6+
typedef struct
7+
{
8+
struct extern_type inner;
9+
}
10+
local_type;

tests/parse_callbacks/mod.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use bindgen::callbacks::ParseCallbacks;
1+
use bindgen::callbacks::*;
22

33
#[derive(Debug)]
44
struct EnumVariantRename;
@@ -8,15 +8,35 @@ impl ParseCallbacks for EnumVariantRename {
88
&self,
99
_enum_name: Option<&str>,
1010
original_variant_name: &str,
11-
_variant_value: bindgen::callbacks::EnumVariantValue,
11+
_variant_value: EnumVariantValue,
1212
) -> Option<String> {
1313
Some(format!("RENAMED_{}", original_variant_name))
1414
}
1515
}
1616

17+
#[derive(Debug)]
18+
struct BlocklistedTypeImplementsTrait;
19+
20+
impl ParseCallbacks for BlocklistedTypeImplementsTrait {
21+
fn blocklisted_type_implements_trait(
22+
&self,
23+
_name: &str,
24+
derive_trait: DeriveTrait,
25+
) -> Option<ImplementsTrait> {
26+
if derive_trait == DeriveTrait::Hash {
27+
Some(ImplementsTrait::No)
28+
} else {
29+
Some(ImplementsTrait::Yes)
30+
}
31+
}
32+
}
33+
1734
pub fn lookup(cb: &str) -> Box<dyn ParseCallbacks> {
1835
match cb {
1936
"enum-variant-rename" => Box::new(EnumVariantRename),
37+
"blocklisted-type-implements-trait" => {
38+
Box::new(BlocklistedTypeImplementsTrait)
39+
}
2040
_ => panic!("Couldn't find name ParseCallbacks: {}", cb),
2141
}
2242
}

0 commit comments

Comments
 (0)