diff --git a/book/src/allowlisting.md b/book/src/allowlisting.md index 2e28d80ff6..edf93df0ef 100644 --- a/book/src/allowlisting.md +++ b/book/src/allowlisting.md @@ -19,12 +19,14 @@ transitively used by a definition that matches them. * [`bindgen::Builder::allowlist_type`](https://docs.rs/bindgen/latest/bindgen/struct.Builder.html#method.allowlist_type) * [`bindgen::Builder::allowlist_function`](https://docs.rs/bindgen/latest/bindgen/struct.Builder.html#method.allowlist_function) * [`bindgen::Builder::allowlist_var`](https://docs.rs/bindgen/latest/bindgen/struct.Builder.html#method.allowlist_var) +* [`bindgen::Builder::allowlist_file`](https://docs.rs/bindgen/latest/bindgen/struct.Builder.html#method.allowlist_file) ### Command Line * `--allowlist-type ` * `--allowlist-function ` * `--allowlist-var ` +* `--allowlist-file ` ### Annotations diff --git a/src/ir/context.rs b/src/ir/context.rs index b2e6f98542..9afd16d6a6 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -2296,7 +2296,8 @@ If you encounter an error missing from this list, please file an issue or a PR!" // game. if self.options().allowlisted_types.is_empty() && self.options().allowlisted_functions.is_empty() && - self.options().allowlisted_vars.is_empty() + self.options().allowlisted_vars.is_empty() && + self.options().allowlisted_files.is_empty() { return true; } @@ -2307,6 +2308,23 @@ If you encounter an error missing from this list, please file an issue or a PR!" return true; } + // Items with a source location in an explicitly allowlisted file + // are always included. + if !self.options().allowlisted_files.is_empty() { + if let Some(location) = item.location() { + let (file, _, _, _) = location.location(); + if let Some(filename) = file.name() { + if self + .options() + .allowlisted_files + .matches(&filename) + { + return true; + } + } + } + } + let name = item.path_for_allowlisting(self)[1..].join("::"); debug!("allowlisted_items: testing {:?}", name); match *item.kind() { diff --git a/src/ir/item.rs b/src/ir/item.rs index 8692575fe9..aed575ca11 100644 --- a/src/ir/item.rs +++ b/src/ir/item.rs @@ -530,6 +530,11 @@ impl Item { &mut self.kind } + /// Where in the source is this item located? + pub fn location(&self) -> Option<&clang::SourceLocation> { + self.location.as_ref() + } + /// Get an identifier that differentiates this item from its siblings. /// /// This should stay relatively stable in the face of code motion outside or diff --git a/src/lib.rs b/src/lib.rs index 39f55f04ff..ea5c61b405 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -313,6 +313,7 @@ impl Builder { (&self.options.allowlisted_functions, "--allowlist-function"), (&self.options.allowlisted_types, "--allowlist-type"), (&self.options.allowlisted_vars, "--allowlist-var"), + (&self.options.allowlisted_files, "--allowlist-file"), (&self.options.no_partialeq_types, "--no-partialeq"), (&self.options.no_copy_types, "--no-copy"), (&self.options.no_debug_types, "--no-debug"), @@ -908,6 +909,12 @@ impl Builder { self } + /// Allowlist the given file so that its contents appear in the generated bindings. + pub fn allowlist_file>(mut self, arg: T) -> Builder { + self.options.allowlisted_files.insert(arg); + self + } + /// Deprecated: use allowlist_var instead. #[deprecated(note = "use allowlist_var instead")] pub fn whitelist_var>(self, arg: T) -> Builder { @@ -1705,6 +1712,9 @@ struct BindgenOptions { /// Allowlisted variables. See docs for `allowlisted_types` for more. allowlisted_vars: RegexSet, + /// The set of files whose contents should be allowlisted. + allowlisted_files: RegexSet, + /// The default style of code to generate for enums default_enum_style: codegen::EnumVariation, @@ -1984,6 +1994,7 @@ impl BindgenOptions { &mut self.allowlisted_vars, &mut self.allowlisted_types, &mut self.allowlisted_functions, + &mut self.allowlisted_files, &mut self.blocklisted_types, &mut self.blocklisted_functions, &mut self.blocklisted_items, @@ -2042,6 +2053,7 @@ impl Default for BindgenOptions { allowlisted_types: Default::default(), allowlisted_functions: Default::default(), allowlisted_vars: Default::default(), + allowlisted_files: Default::default(), default_enum_style: Default::default(), bitfield_enums: Default::default(), newtype_enums: Default::default(), diff --git a/src/options.rs b/src/options.rs index 67bcda74fc..04d42ed5c3 100644 --- a/src/options.rs +++ b/src/options.rs @@ -418,6 +418,14 @@ where .takes_value(true) .multiple_occurrences(true) .number_of_values(1), + Arg::new("allowlist-file") + .alias("allowlist-file") + .long("allowlist-file") + .help("Allowlist all contents of .") + .value_name("path") + .takes_value(true) + .multiple_occurrences(true) + .number_of_values(1), Arg::new("verbose") .long("verbose") .help("Print verbose error messages."), @@ -871,6 +879,12 @@ where } } + if let Some(hidden_files) = matches.values_of("allowlist-file") { + for file in hidden_files { + builder = builder.allowlist_file(file); + } + } + if let Some(args) = matches.values_of("clang-args") { for arg in args { builder = builder.clang_arg(arg); diff --git a/tests/expectations/tests/allowlist-file.rs b/tests/expectations/tests/allowlist-file.rs new file mode 100644 index 0000000000..2c2660a606 --- /dev/null +++ b/tests/expectations/tests/allowlist-file.rs @@ -0,0 +1,154 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +pub const SOME_DEFUN: u32 = 123; +extern "C" { + #[link_name = "\u{1}_Z12SomeFunctionv"] + pub fn SomeFunction(); +} +extern "C" { + pub static mut someVar: ::std::os::raw::c_int; +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct someClass { + pub _address: u8, +} +#[test] +fn bindgen_test_layout_someClass() { + assert_eq!( + ::std::mem::size_of::(), + 1usize, + concat!("Size of: ", stringify!(someClass)) + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(someClass)) + ); +} +extern "C" { + #[link_name = "\u{1}_ZN9someClass16somePublicMethodEi"] + pub fn someClass_somePublicMethod( + this: *mut someClass, + foo: ::std::os::raw::c_int, + ); +} +impl someClass { + #[inline] + pub unsafe fn somePublicMethod(&mut self, foo: ::std::os::raw::c_int) { + someClass_somePublicMethod(self, foo) + } +} +extern "C" { + pub fn ExternFunction(); +} +extern "C" { + #[link_name = "\u{1}_ZN3foo18NamespacedFunctionEv"] + pub fn foo_NamespacedFunction(); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct StructWithAllowlistedDefinition { + pub other: *mut StructWithAllowlistedFwdDecl, +} +#[test] +fn bindgen_test_layout_StructWithAllowlistedDefinition() { + assert_eq!( + ::std::mem::size_of::(), + 8usize, + concat!("Size of: ", stringify!(StructWithAllowlistedDefinition)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(StructWithAllowlistedDefinition)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).other + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(StructWithAllowlistedDefinition), + "::", + stringify!(other) + ) + ); +} +impl Default for StructWithAllowlistedDefinition { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct StructWithAllowlistedFwdDecl { + pub b: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_StructWithAllowlistedFwdDecl() { + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(StructWithAllowlistedFwdDecl)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(StructWithAllowlistedFwdDecl)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).b + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(StructWithAllowlistedFwdDecl), + "::", + stringify!(b) + ) + ); +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct AllowlistMe { + pub foo: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_AllowlistMe() { + assert_eq!( + ::std::mem::size_of::(), + 4usize, + concat!("Size of: ", stringify!(AllowlistMe)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(AllowlistMe)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).foo as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(AllowlistMe), + "::", + stringify!(foo) + ) + ); +} diff --git a/tests/headers/allowlist-file.hpp b/tests/headers/allowlist-file.hpp new file mode 100644 index 0000000000..b0354a047f --- /dev/null +++ b/tests/headers/allowlist-file.hpp @@ -0,0 +1,21 @@ +// bindgen-flags: --allowlist-file ".*/allowlisted/file.*" --allowlist-type AllowlistMe -- -Itests/headers + + +// Forward declaration of struct that's defined in an allowlisted file. +struct StructWithAllowlistedDefinition; + +#include "allowlisted/file.hpp" + +// Actual definition of struct that has a forward declaration in an allowlisted file. +struct StructWithAllowlistedFwdDecl { + int b; +}; + +class Ignored { + char c; +}; + +// Also have an explicitly allowlisted type +class AllowlistMe { + int foo; +}; diff --git a/tests/headers/allowlisted/file.hpp b/tests/headers/allowlisted/file.hpp new file mode 100644 index 0000000000..5f360e5ea9 --- /dev/null +++ b/tests/headers/allowlisted/file.hpp @@ -0,0 +1,22 @@ +void SomeFunction(); +extern int someVar; +#define SOME_DEFUN 123 + +class someClass { + void somePrivateMethod(); +public: + void somePublicMethod(int foo); +}; + +extern "C" void ExternFunction(); + +namespace foo { + void NamespacedFunction(); +} + + +struct StructWithAllowlistedFwdDecl; + +struct StructWithAllowlistedDefinition { + StructWithAllowlistedFwdDecl* other; +};