diff --git a/bindgen-tests/tests/tests.rs b/bindgen-tests/tests/tests.rs index 6e3c358d3e..cd21f5f9ba 100644 --- a/bindgen-tests/tests/tests.rs +++ b/bindgen-tests/tests/tests.rs @@ -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) +} + +#[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() diff --git a/bindgen/ir/function.rs b/bindgen/ir/function.rs index 6f8c00fa89..d17f803d6f 100644 --- a/bindgen/ir/function.rs +++ b/bindgen/ir/function.rs @@ -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 && diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs index b9b33a850b..a1915d85c2 100644 --- a/bindgen/options/mod.rs +++ b/bindgen/options/mod.rs @@ -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", + }, }