Skip to content

Commit f819034

Browse files
committed
Add ParseCallbacks::allow_or_block_item().
Like `allowlist_item` and `blocklist_item` options, add new methods to `ParseCallbacks`, `ParseCallbacks::allow_item()` and `ParseCallbacks::block_item()`, to be able to allow and block items from being generated. `allowlist_*` and `blocklist_*` options work with regexes and are inserted in a RegexSet. There are two issues with this approach: 1. In some cases, we want to have more flexibility than just using regexes. If we want to have dynamic conditions to allow or block items, using regexes can be limited. 2. RegexSet scales linearly with the number of elements inserted. This means that if we have a huge number of items that we want to allow or block, regexes and RegexSet are not necessarily the most appropriate data structures. Using a new method in `ParseCallbacks` solves these two issues. We can manually decide the appropriate rules and data structure to match the items. This method takes precedences over the `allowlist_*` options. If at least one of the parse callbacks returns `Block`, the generation of the bindings for the item is blocked. If all the parse callbacks that don't return `None` return `Allow`, the bindings for the item are generated. If all the parse callbacks return `None` (the default implementation), the `allowlist_*` options are used instead.
1 parent 74a5f30 commit f819034

10 files changed

Lines changed: 351 additions & 15 deletions

File tree

bindgen-tests/tests/expectations/tests/allow-item-callback.rs

Lines changed: 65 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindgen-tests/tests/expectations/tests/block-item-callback.rs

Lines changed: 52 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// bindgen-flags: --allowlist-item 'list_allowed_.*'
2+
// bindgen-parse-callbacks: allow-item
3+
4+
struct allowed_my_struct {
5+
int a;
6+
};
7+
8+
union allowed_my_union {
9+
int a;
10+
double b;
11+
};
12+
13+
enum allowed_my_enum {
14+
ALLOWED_MY_ENUM_A,
15+
ALLOWED_MY_ENUM_B,
16+
};
17+
18+
static const int allowed_my_const = 10;
19+
20+
struct non_allowed_my_struct {
21+
int a;
22+
};
23+
24+
union non_allowed_my_union {
25+
int a;
26+
double b;
27+
};
28+
29+
enum non_allowed_my_enum {
30+
NON_ALLOWED_MY_ENUM_A,
31+
NON_ALLOWED_MY_ENUM_B,
32+
};
33+
34+
static const int non_allowed_my_const = 10;
35+
36+
struct list_allowed_my_struct {
37+
int a;
38+
};
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// bindgen-flags: --blocklist-item 'list_blocked_.*'
2+
// bindgen-parse-callbacks: block-item
3+
4+
struct blocked_my_struct {
5+
int a;
6+
};
7+
8+
union blocked_my_union {
9+
int a;
10+
double b;
11+
};
12+
13+
enum blocked_my_enum {
14+
BLOCKED_MY_ENUM_A,
15+
BLOCKED_MY_ENUM_B,
16+
};
17+
18+
static const int blocked_my_const = 10;
19+
20+
struct non_blocked_my_struct {
21+
int a;
22+
};
23+
24+
union non_blocked_my_union {
25+
int a;
26+
double b;
27+
};
28+
29+
enum non_blocked_my_enum {
30+
NON_BLOCKED_MY_ENUM_A,
31+
NON_BLOCKED_MY_ENUM_B,
32+
};
33+
34+
static const int non_blocked_my_const = 10;
35+
36+
struct list_blocked_my_struct {
37+
int a;
38+
};

bindgen-tests/tests/parse_callbacks/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,32 @@ impl ParseCallbacks for OperatorRename {
160160
}
161161
}
162162

163+
#[derive(Debug)]
164+
struct AllowItem;
165+
166+
impl ParseCallbacks for AllowItem {
167+
fn allow_or_block_item(&self, item: &ItemInfo) -> Option<AllowOrBlockItem> {
168+
if item.name.starts_with("allowed_") {
169+
Some(AllowOrBlockItem::Allow)
170+
} else {
171+
None
172+
}
173+
}
174+
}
175+
176+
#[derive(Debug)]
177+
struct BlockItem;
178+
179+
impl ParseCallbacks for BlockItem {
180+
fn allow_or_block_item(&self, item: &ItemInfo) -> Option<AllowOrBlockItem> {
181+
if item.name.starts_with("blocked_") {
182+
Some(AllowOrBlockItem::Block)
183+
} else {
184+
None
185+
}
186+
}
187+
}
188+
163189
pub fn lookup(cb: &str) -> Box<dyn ParseCallbacks> {
164190
match cb {
165191
"enum-variant-rename" => Box::new(EnumVariantRename),
@@ -169,6 +195,8 @@ pub fn lookup(cb: &str) -> Box<dyn ParseCallbacks> {
169195
"wrap-as-variadic-fn" => Box::new(WrapAsVariadicFn),
170196
"type-visibility" => Box::new(TypeVisibility),
171197
"operator-rename" => Box::new(OperatorRename),
198+
"allow-item" => Box::new(AllowItem),
199+
"block-item" => Box::new(BlockItem),
172200
call_back => {
173201
if let Some(prefix) =
174202
call_back.strip_prefix("remove-function-prefix-")

bindgen/callbacks.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ pub enum MacroParsingBehavior {
1919
Default,
2020
}
2121

22+
/// Enum to indicate if the bindings for a given should be generated or blocked.
23+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
24+
pub enum AllowOrBlockItem {
25+
/// Generate the bindings for the given item.
26+
Allow,
27+
28+
/// Block bindings for the given item.
29+
Block,
30+
}
31+
2232
/// A trait to allow configuring different kinds of types in different
2333
/// situations.
2434
pub trait ParseCallbacks: fmt::Debug {
@@ -206,6 +216,26 @@ pub trait ParseCallbacks: fmt::Debug {
206216
) {
207217
}
208218

219+
/// Generate or block the bindings for the given item.
220+
///
221+
/// This method takes precedences over the `allowlist_*` options.
222+
///
223+
/// If at least one of the parse callbacks returns `Block`, the generation of the bindings
224+
/// for the item is blocked.
225+
///
226+
/// If all the parse callbacks that don't return `None` return `Allow`, the bindings
227+
/// for the item are generated.
228+
///
229+
/// If all the parse callbacks return `None` (the default implementation), the `allowlist_*`
230+
/// options are used instead.
231+
///
232+
fn allow_or_block_item(
233+
&self,
234+
_item: &ItemInfo,
235+
) -> Option<AllowOrBlockItem> {
236+
None
237+
}
238+
209239
// TODO add callback for ResolvedTypeRef
210240
}
211241

bindgen/codegen/mod.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ use super::BindgenOptions;
2222

2323
use crate::callbacks::{
2424
AttributeInfo, DeriveInfo, DiscoveredItem, DiscoveredItemId,
25-
FieldAttributeInfo, FieldInfo, TypeKind as DeriveTypeKind,
25+
FieldAttributeInfo, FieldInfo, ItemInfo, ItemKind as CallbackItemKind,
26+
TypeKind as DeriveTypeKind,
2627
};
2728
use crate::codegen::error::Error;
2829
use crate::ir::analysis::{HasVtable, Sizedness};
@@ -5008,7 +5009,18 @@ fn objc_method_codegen(
50085009
// Item::process_before_codegen; however, ObjC methods are not currently
50095010
// made into function items.
50105011
let name = format!("{rust_class_name}::{prefix}{}", method.rust_name());
5011-
if ctx.options().blocklisted_items.matches(name) {
5012+
5013+
let item_info = ItemInfo {
5014+
name: &name,
5015+
kind: CallbackItemKind::Function,
5016+
};
5017+
5018+
if ctx
5019+
.options()
5020+
.cb_item_is_blocked(&item_info)
5021+
.unwrap_or_else(|| ctx.options().blocklisted_items.matches(&name))
5022+
{
5023+
// Item is blocked through the parse callbacks or `blocklisted_items`.
50125024
return;
50135025
}
50145026

@@ -5325,7 +5337,9 @@ pub(crate) mod utils {
53255337
use super::helpers::BITFIELD_UNIT;
53265338
use super::serialize::CSerialize;
53275339
use super::{error, CodegenError, CodegenResult, ToRustTyOrOpaque};
5328-
use crate::callbacks::DiscoveredItemId;
5340+
use crate::callbacks::{
5341+
DiscoveredItemId, ItemInfo, ItemKind as CallbackItemKind,
5342+
};
53295343
use crate::ir::context::BindgenContext;
53305344
use crate::ir::context::TypeId;
53315345
use crate::ir::function::{Abi, ClangAbi, FunctionSig};
@@ -5456,9 +5470,20 @@ pub(crate) mod utils {
54565470
ctx: &BindgenContext,
54575471
result: &mut Vec<proc_macro2::TokenStream>,
54585472
) {
5459-
if ctx.options().blocklisted_items.matches(BITFIELD_UNIT) ||
5460-
ctx.options().blocklisted_types.matches(BITFIELD_UNIT)
5473+
let item_info = ItemInfo {
5474+
name: BITFIELD_UNIT,
5475+
kind: CallbackItemKind::Type,
5476+
};
5477+
5478+
if ctx
5479+
.options()
5480+
.cb_item_is_blocked(&item_info)
5481+
.unwrap_or_else(|| {
5482+
ctx.options().blocklisted_items.matches(BITFIELD_UNIT) ||
5483+
ctx.options().blocklisted_types.matches(BITFIELD_UNIT)
5484+
})
54615485
{
5486+
// Item is blocked through the parse callbacks or blocklists.
54625487
return;
54635488
}
54645489

bindgen/ir/context.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use super::traversal::{self, Edge, ItemTraversal};
2121
use super::ty::{FloatKind, Type, TypeKind};
2222
use crate::clang::{self, ABIKind, Cursor};
2323
use crate::codegen::CodegenError;
24+
use crate::ir::item::ItemCanonicalName;
2425
use crate::BindgenOptions;
2526
use crate::{Entry, HashMap, HashSet};
2627

@@ -2406,6 +2407,18 @@ If you encounter an error missing from this list, please file an issue or a PR!"
24062407
// Only consider roots that are enabled for codegen.
24072408
.filter(|&(_, item)| item.is_enabled_for_codegen(self))
24082409
.filter(|&(_, item)| {
2410+
let item_info = crate::callbacks::ItemInfo {
2411+
name: &item.canonical_name(self),
2412+
kind: item.callback_item_kind(),
2413+
};
2414+
2415+
if let Some(is_cb_allow) =
2416+
self.options().cb_item_is_allowed(&item_info)
2417+
{
2418+
// Item is allowed or not with the parse callbacks.
2419+
return is_cb_allow;
2420+
}
2421+
24092422
// If nothing is explicitly allowlisted, then everything is fair
24102423
// game.
24112424
if self.options().allowlisted_types.is_empty() &&

0 commit comments

Comments
 (0)