diff --git a/bindgen-tests/tests/expectations/tests/allow-item-callback.rs b/bindgen-tests/tests/expectations/tests/allow-item-callback.rs new file mode 100644 index 0000000000..c5656084e6 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/allow-item-callback.rs @@ -0,0 +1,48 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct allowed_my_struct { + pub a: ::std::os::raw::c_int, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of allowed_my_struct"][::std::mem::size_of::() - 4usize]; + [ + "Alignment of allowed_my_struct", + ][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: allowed_my_struct::a", + ][::std::mem::offset_of!(allowed_my_struct, a) - 0usize]; +}; +#[repr(C)] +#[derive(Copy, Clone)] +pub union allowed_my_union { + pub a: ::std::os::raw::c_int, + pub b: f64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of allowed_my_union"][::std::mem::size_of::() - 8usize]; + [ + "Alignment of allowed_my_union", + ][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: allowed_my_union::a", + ][::std::mem::offset_of!(allowed_my_union, a) - 0usize]; + [ + "Offset of field: allowed_my_union::b", + ][::std::mem::offset_of!(allowed_my_union, b) - 0usize]; +}; +impl Default for allowed_my_union { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +pub const allowed_my_enum_ALLOWED_MY_ENUM_A: allowed_my_enum = 0; +pub const allowed_my_enum_ALLOWED_MY_ENUM_B: allowed_my_enum = 1; +pub type allowed_my_enum = ::std::os::raw::c_uint; +pub const allowed_my_const: ::std::os::raw::c_int = 10; diff --git a/bindgen-tests/tests/expectations/tests/block-item-callback.rs b/bindgen-tests/tests/expectations/tests/block-item-callback.rs new file mode 100644 index 0000000000..fbf2a4350a --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/block-item-callback.rs @@ -0,0 +1,52 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct non_blocked_my_struct { + pub a: ::std::os::raw::c_int, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of non_blocked_my_struct", + ][::std::mem::size_of::() - 4usize]; + [ + "Alignment of non_blocked_my_struct", + ][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: non_blocked_my_struct::a", + ][::std::mem::offset_of!(non_blocked_my_struct, a) - 0usize]; +}; +#[repr(C)] +#[derive(Copy, Clone)] +pub union non_blocked_my_union { + pub a: ::std::os::raw::c_int, + pub b: f64, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of non_blocked_my_union", + ][::std::mem::size_of::() - 8usize]; + [ + "Alignment of non_blocked_my_union", + ][::std::mem::align_of::() - 8usize]; + [ + "Offset of field: non_blocked_my_union::a", + ][::std::mem::offset_of!(non_blocked_my_union, a) - 0usize]; + [ + "Offset of field: non_blocked_my_union::b", + ][::std::mem::offset_of!(non_blocked_my_union, b) - 0usize]; +}; +impl Default for non_blocked_my_union { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +pub const non_blocked_my_enum_NON_BLOCKED_MY_ENUM_A: non_blocked_my_enum = 0; +pub const non_blocked_my_enum_NON_BLOCKED_MY_ENUM_B: non_blocked_my_enum = 1; +pub type non_blocked_my_enum = ::std::os::raw::c_uint; +pub const non_blocked_my_const: ::std::os::raw::c_int = 10; diff --git a/bindgen-tests/tests/headers/allow-item-callback.h b/bindgen-tests/tests/headers/allow-item-callback.h new file mode 100644 index 0000000000..04610f5c6b --- /dev/null +++ b/bindgen-tests/tests/headers/allow-item-callback.h @@ -0,0 +1,33 @@ +// bindgen-parse-callbacks: allow-item + +struct allowed_my_struct { + int a; +}; + +union allowed_my_union { + int a; + double b; +}; + +enum allowed_my_enum { + ALLOWED_MY_ENUM_A, + ALLOWED_MY_ENUM_B, +}; + +static const int allowed_my_const = 10; + +struct non_allowed_my_struct { + int a; +}; + +union non_allowed_my_union { + int a; + double b; +}; + +enum non_allowed_my_enum { + NON_ALLOWED_MY_ENUM_A, + NON_ALLOWED_MY_ENUM_B, +}; + +static const int non_allowed_my_const = 10; diff --git a/bindgen-tests/tests/headers/block-item-callback.h b/bindgen-tests/tests/headers/block-item-callback.h new file mode 100644 index 0000000000..82ad1fba4b --- /dev/null +++ b/bindgen-tests/tests/headers/block-item-callback.h @@ -0,0 +1,33 @@ +// bindgen-parse-callbacks: block-item + +struct blocked_my_struct { + int a; +}; + +union blocked_my_union { + int a; + double b; +}; + +enum blocked_my_enum { + BLOCKED_MY_ENUM_A, + BLOCKED_MY_ENUM_B, +}; + +static const int blocked_my_const = 10; + +struct non_blocked_my_struct { + int a; +}; + +union non_blocked_my_union { + int a; + double b; +}; + +enum non_blocked_my_enum { + NON_BLOCKED_MY_ENUM_A, + NON_BLOCKED_MY_ENUM_B, +}; + +static const int non_blocked_my_const = 10; diff --git a/bindgen-tests/tests/parse_callbacks/mod.rs b/bindgen-tests/tests/parse_callbacks/mod.rs index 5fe8d90d4c..f75b777e7a 100644 --- a/bindgen-tests/tests/parse_callbacks/mod.rs +++ b/bindgen-tests/tests/parse_callbacks/mod.rs @@ -159,6 +159,24 @@ impl ParseCallbacks for OperatorRename { } } +#[derive(Debug)] +struct AllowItem; + +impl ParseCallbacks for AllowItem { + fn allow_item(&self, item: ItemInfo) -> bool { + item.name.starts_with("allowed_") + } +} + +#[derive(Debug)] +struct BlockItem; + +impl ParseCallbacks for BlockItem { + fn block_item(&self, item: ItemInfo) -> bool { + item.name.starts_with("blocked_") + } +} + pub fn lookup(cb: &str) -> Box { match cb { "enum-variant-rename" => Box::new(EnumVariantRename), @@ -168,6 +186,8 @@ pub fn lookup(cb: &str) -> Box { "wrap-as-variadic-fn" => Box::new(WrapAsVariadicFn), "type-visibility" => Box::new(TypeVisibility), "operator-rename" => Box::new(OperatorRename), + "allow-item" => Box::new(AllowItem), + "block-item" => Box::new(BlockItem), call_back => { if let Some(prefix) = call_back.strip_prefix("remove-function-prefix-") diff --git a/bindgen/callbacks.rs b/bindgen/callbacks.rs index 8ad06b3375..75720d9c07 100644 --- a/bindgen/callbacks.rs +++ b/bindgen/callbacks.rs @@ -171,6 +171,24 @@ pub trait ParseCallbacks: fmt::Debug { /// This will get called everytime an item (currently struct, union, and alias) is found with some information about it fn new_item_found(&self, _id: DiscoveredItemId, _item: DiscoveredItem) {} + /// Generate bindings for the given item. + /// + /// This method is called after processing the `allowlist_*` options. + /// + /// The default implementation is to allow the given item. + fn allow_item(&self, _item: ItemInfo) -> bool { + true + } + + /// Block bindings for the given item. + /// + /// This method is called after processing the `blocklist_*` options. + /// + /// The default implementation is to not block the given item. + fn block_item(&self, _item: ItemInfo) -> bool { + false + } + // TODO add callback for ResolvedTypeRef } diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 433fc85f01..9e7f92b4a7 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -22,7 +22,7 @@ use super::BindgenOptions; use crate::callbacks::{ AttributeInfo, DeriveInfo, DiscoveredItem, DiscoveredItemId, FieldInfo, - TypeKind as DeriveTypeKind, + ItemInfo, ItemKind as CallbackItemKind, TypeKind as DeriveTypeKind, }; use crate::codegen::error::Error; use crate::ir::analysis::{HasVtable, Sizedness}; @@ -4942,7 +4942,14 @@ fn objc_method_codegen( // Item::process_before_codegen; however, ObjC methods are not currently // made into function items. let name = format!("{rust_class_name}::{prefix}{}", method.rust_name()); - if ctx.options().blocklisted_items.matches(name) { + let item_info = ItemInfo { + name: &name, + kind: CallbackItemKind::Function, + }; + if ctx.options().blocklisted_items.matches(&name) || + ctx.options() + .for_any_callback(|cb| cb.block_item(item_info)) + { return; } @@ -5259,6 +5266,7 @@ pub(crate) mod utils { use super::helpers::BITFIELD_UNIT; use super::serialize::CSerialize; use super::{error, CodegenError, CodegenResult, ToRustTyOrOpaque}; + use crate::callbacks::{ItemInfo, ItemKind as CallbackItemKind}; use crate::ir::context::BindgenContext; use crate::ir::context::TypeId; use crate::ir::function::{Abi, ClangAbi, FunctionSig}; @@ -5389,8 +5397,14 @@ pub(crate) mod utils { ctx: &BindgenContext, result: &mut Vec, ) { + let item_info = ItemInfo { + name: BITFIELD_UNIT, + kind: CallbackItemKind::Type, + }; if ctx.options().blocklisted_items.matches(BITFIELD_UNIT) || - ctx.options().blocklisted_types.matches(BITFIELD_UNIT) + ctx.options().blocklisted_types.matches(BITFIELD_UNIT) || + ctx.options() + .for_any_callback(|cb| cb.block_item(item_info)) { return; } diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs index 6adbcd5989..bf3e6ae84d 100644 --- a/bindgen/ir/context.rs +++ b/bindgen/ir/context.rs @@ -21,6 +21,7 @@ use super::traversal::{self, Edge, ItemTraversal}; use super::ty::{FloatKind, Type, TypeKind}; use crate::clang::{self, ABIKind, Cursor}; use crate::codegen::CodegenError; +use crate::ir::item::ItemCanonicalName; use crate::BindgenOptions; use crate::{Entry, HashMap, HashSet}; @@ -2519,6 +2520,14 @@ If you encounter an error missing from this list, please file an issue or a PR!" } } }) + .filter(|&(_, item)| { + let item_info = crate::callbacks::ItemInfo { + name: &item.canonical_name(self), + kind: item.callback_item_kind(), + }; + self.options() + .for_all_callbacks(|cb| cb.allow_item(item_info)) + }) .map(|(id, _)| id) .collect::>(); diff --git a/bindgen/ir/item.rs b/bindgen/ir/item.rs index d38879f390..ac4e913cee 100644 --- a/bindgen/ir/item.rs +++ b/bindgen/ir/item.rs @@ -653,6 +653,10 @@ impl Item { let path = self.path_for_allowlisting(ctx); let name = path[1..].join("::"); + let item_info = ItemInfo { + name: &name, + kind: self.callback_item_kind(), + }; ctx.options().blocklisted_items.matches(&name) || match self.kind { ItemKind::Type(..) => { @@ -667,7 +671,9 @@ impl Item { } // TODO: Add namespace blocklisting? ItemKind::Module(..) => false, - } + } || + ctx.options() + .for_any_callback(|cb| cb.block_item(item_info)) } /// Take out item `NameOptions` @@ -818,6 +824,16 @@ impl Item { } } + /// Get the callback item kind of this item. + pub(crate) fn callback_item_kind(&self) -> crate::callbacks::ItemKind { + match self.kind() { + ItemKind::Module(..) => crate::callbacks::ItemKind::Module, + ItemKind::Type(..) => crate::callbacks::ItemKind::Type, + ItemKind::Function(..) => crate::callbacks::ItemKind::Function, + ItemKind::Var(..) => crate::callbacks::ItemKind::Var, + } + } + /// Get the canonical name without taking into account the replaces /// annotation. /// @@ -925,14 +941,7 @@ impl Item { let name = if opt.user_mangled == UserMangled::Yes { let item_info = ItemInfo { name: &name, - kind: match self.kind() { - ItemKind::Module(..) => crate::callbacks::ItemKind::Module, - ItemKind::Type(..) => crate::callbacks::ItemKind::Type, - ItemKind::Function(..) => { - crate::callbacks::ItemKind::Function - } - ItemKind::Var(..) => crate::callbacks::ItemKind::Var, - }, + kind: self.callback_item_kind(), }; ctx.options() .last_callback(|callbacks| callbacks.item_name(item_info)) diff --git a/bindgen/lib.rs b/bindgen/lib.rs index e4294c505a..22795e52d5 100644 --- a/bindgen/lib.rs +++ b/bindgen/lib.rs @@ -591,6 +591,20 @@ impl BindgenOptions { self.parse_callbacks.iter().for_each(|cb| f(cb.as_ref())); } + fn for_all_callbacks( + &self, + f: impl Fn(&dyn callbacks::ParseCallbacks) -> bool, + ) -> bool { + self.parse_callbacks.iter().all(|cb| f(cb.as_ref())) + } + + fn for_any_callback( + &self, + f: impl Fn(&dyn callbacks::ParseCallbacks) -> bool, + ) -> bool { + self.parse_callbacks.iter().any(|cb| f(cb.as_ref())) + } + fn process_comment(&self, comment: &str) -> String { let comment = comment::preprocess(comment); self.last_callback(|cb| cb.process_comment(&comment))