Skip to content
Open
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
43 changes: 43 additions & 0 deletions bindgen-tests/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,49 @@ fn test_header_contents() {
assert_eq!(expected, actual);
}

const HIDDEN_FUNCTIONS_HEADER: &str = r#"
__attribute__((visibility("hidden"))) int hidden_fn(void);
__attribute__((visibility("protected"))) int protected_fn(void);
int visible_fn(void);
"#;

fn hidden_functions_builder() -> Builder {
builder()
.allowlist_function("visible_fn")
.allowlist_function("hidden_fn")
.allowlist_function("protected_fn")
.header_contents("hidden.h", HIDDEN_FUNCTIONS_HEADER)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please reuse the existing test framework instead. I.e. add a header to bindgen-tests/tests/headers and the generated file to bindgen-tests/tests/expectations. That way we can test the whole content.


#[test]
fn hidden_functions_are_skipped_by_default() {
let bindings = hidden_functions_builder().generate().unwrap().to_string();

assert!(bindings.contains("pub fn visible_fn"));
assert!(!bindings.contains("pub fn hidden_fn"));
}

#[test]
fn hidden_functions_can_be_generated_when_enabled() {
let default_bindings =
hidden_functions_builder().generate().unwrap().to_string();

let hidden_enabled = hidden_functions_builder()
.generate_hidden_functions(true)
.generate()
.unwrap()
.to_string();

assert!(hidden_enabled.contains("pub fn hidden_fn"));
assert!(!default_bindings.contains("pub fn hidden_fn"));

// Clang on Mach-O downgrades `protected` visibility to `default`,
// so the symbol might already be present without the new option.
if !default_bindings.contains("pub fn protected_fn") {
assert!(hidden_enabled.contains("pub fn protected_fn"));
}
}

#[test]
fn test_multiple_header_calls_in_builder() {
let actual = builder()
Expand Down
6 changes: 5 additions & 1 deletion bindgen/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,11 @@ impl ClangSubItemParser for Function {

debug!("Function::parse({cursor:?}, {:?})", cursor.cur_type());
let visibility = cursor.visibility();
if visibility != CXVisibility_Default {
let allow_hidden = context.options().generate_hidden_functions &&
(visibility == CXVisibility_Hidden ||
visibility == CXVisibility_Protected);

if visibility != CXVisibility_Default && !allow_hidden {
return Err(ParseError::Continue);
}
if cursor.access_specifier() == CX_CXXPrivate &&
Expand Down
12 changes: 12 additions & 0 deletions bindgen/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2329,4 +2329,16 @@ options! {
},
as_args: "--generate-private-functions",
},
/// Whether to allow generating functions that are marked with hidden or protected visibility.
generate_hidden_functions: bool {
methods: {
/// Set whether to generate functions that are not externally visible according to clang.
pub fn generate_hidden_functions(mut self, doit: bool) -> Self {
self.options.generate_hidden_functions = doit;
self
}

},
as_args: "--generate-hidden-functions",
},
}