diff --git a/bindgen-tests/tests/expectations/tests/uncallable_functions.rs b/bindgen-tests/tests/expectations/tests/uncallable_functions.rs new file mode 100644 index 0000000000..fddb5d41fc --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/uncallable_functions.rs @@ -0,0 +1,46 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +#[repr(C)] +pub struct Test__bindgen_vtable { + pub Test_a: unsafe extern "C" fn(this: *mut Test), +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct Test { + pub vtable_: *const Test__bindgen_vtable, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of Test"][::std::mem::size_of::() - 8usize]; + ["Alignment of Test"][::std::mem::align_of::() - 8usize]; +}; +unsafe extern "C" { + #[link_name = "\u{1}_ZN4Test1bEv"] + pub fn Test_b(this: *mut Test); +} +unsafe extern "C" { + #[link_name = "\u{1}_ZN4Test1cEv"] + pub fn Test_c(this: *mut Test); +} +impl Default for Test { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +impl Test { + #[inline] + pub unsafe fn b(&mut self) { + Test_b(self) + } + #[inline] + pub unsafe fn c(&mut self) { + Test_c(self) + } +} +unsafe extern "C" { + #[link_name = "\u{1}_ZN4Test1aEv"] + pub fn Test_a(this: *mut ::std::os::raw::c_void); +} diff --git a/bindgen-tests/tests/headers/uncallable_functions.hpp b/bindgen-tests/tests/headers/uncallable_functions.hpp new file mode 100644 index 0000000000..9187470c65 --- /dev/null +++ b/bindgen-tests/tests/headers/uncallable_functions.hpp @@ -0,0 +1,9 @@ +// bindgen-flags: --generate-deleted-functions --generate-private-functions --generate-pure-virtual-functions --generate-inline-functions -- -x c++ -std=c++14 + +class Test { +public: + virtual void a() = 0; + void b() = delete; +private: + void c() {} +}; diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index f5518e432d..8c623ff952 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -4564,14 +4564,20 @@ impl CodeGenerator for Function { } } - // Pure virtual methods have no actual symbol, so we can't generate - // something meaningful for them. - let is_dynamic_function = match self.kind() { - FunctionKind::Method(ref method_kind) - if method_kind.is_pure_virtual() => - { - return None; + let is_pure_virtual = match self.kind() { + FunctionKind::Method(ref method_kind) => { + method_kind.is_pure_virtual() } + _ => false, + }; + if is_pure_virtual && !ctx.options().generate_pure_virtual_functions { + // Pure virtual methods have no actual symbol, so we can't generate + // something meaningful for them. Downstream code postprocessors + // might want to find out about them. + return None; + } + + let is_dynamic_function = match self.kind() { FunctionKind::Function => { ctx.options().dynamic_library_name.is_some() } diff --git a/bindgen/ir/function.rs b/bindgen/ir/function.rs index 83b748a5f4..a1a53ce03c 100644 --- a/bindgen/ir/function.rs +++ b/bindgen/ir/function.rs @@ -731,8 +731,9 @@ impl ClangSubItemParser for Function { if visibility != CXVisibility_Default { return Err(ParseError::Continue); } - - if cursor.access_specifier() == CX_CXXPrivate { + if cursor.access_specifier() == CX_CXXPrivate && + !context.options().generate_private_functions + { return Err(ParseError::Continue); } @@ -752,7 +753,9 @@ impl ClangSubItemParser for Function { return Err(ParseError::Continue); } - if cursor.is_deleted_function() { + if cursor.is_deleted_function() && + !context.options().generate_deleted_functions + { return Err(ParseError::Continue); } diff --git a/bindgen/options/cli.rs b/bindgen/options/cli.rs index 1efddb02f3..4f96263439 100644 --- a/bindgen/options/cli.rs +++ b/bindgen/options/cli.rs @@ -507,6 +507,18 @@ struct BindgenCommand { /// bitfields. This flag is ignored if the `--respect-cxx-access-specs` flag is used. #[arg(long, value_name = "VISIBILITY")] default_visibility: Option, + /// Whether to generate C++ functions marked with "=delete" even though they + /// can't be called. + #[arg(long)] + generate_deleted_functions: bool, + /// Whether to generate C++ "pure virtual" functions even though they can't + /// be called. + #[arg(long)] + generate_pure_virtual_functions: bool, + /// Whether to generate C++ private functions even though they can't + /// be called. + #[arg(long)] + generate_private_functions: bool, /// Whether to emit diagnostics or not. #[cfg(feature = "experimental")] #[arg(long, requires = "experimental")] @@ -653,6 +665,9 @@ where wrap_static_fns_path, wrap_static_fns_suffix, default_visibility, + generate_deleted_functions, + generate_pure_virtual_functions, + generate_private_functions, #[cfg(feature = "experimental")] emit_diagnostics, generate_shell_completions, @@ -943,6 +958,9 @@ where wrap_static_fns_path, wrap_static_fns_suffix, default_visibility, + generate_deleted_functions, + generate_pure_virtual_functions, + generate_private_functions, } ); diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs index 1a675401a4..c6b85519db 100644 --- a/bindgen/options/mod.rs +++ b/bindgen/options/mod.rs @@ -2193,4 +2193,58 @@ options! { }, as_args: "--clang-macro-fallback-build-dir", } + /// Whether to always report C++ "deleted" functions. + generate_deleted_functions: bool { + methods: { + /// Set whether to generate C++ functions even marked "=deleted" + /// + /// Although not useful to call these functions, downstream code + /// generators may need to know whether they've been deleted in + /// order to determine the relocatability of a C++ type + /// (specifically by virtue of which constructors exist.) + pub fn generate_deleted_functions(mut self, doit: bool) -> Self { + self.options.generate_deleted_functions = doit; + self + } + + }, + as_args: "--generate-deleted-functions", + }, + /// Whether to always report C++ "pure virtual" functions. + generate_pure_virtual_functions: bool { + methods: { + /// Set whether to generate C++ functions that are pure virtual. + /// + /// These functions can't be called, so the only reason + /// to generate them is if downstream postprocessors + /// need to know of their existence. This is necessary, + /// for instance, to determine whether a type itself is + /// pure virtual and thus can't be allocated. + /// Downstream code generators may choose to make code to + /// allow types to be allocated but need to avoid doing so + /// if the type contains pure virtual functions. + pub fn generate_pure_virtual_functions(mut self, doit: bool) -> Self { + self.options.generate_pure_virtual_functions = doit; + self + } + + }, + as_args: "--generate-pure-virtual-functions", + }, + /// Whether to always report C++ "private" functions. + generate_private_functions: bool { + methods: { + /// Set whether to generate C++ functions that are private. + /// + /// These functions can't be called, so the only reason + /// to generate them is if downstream postprocessors + /// need to know of their existence. + pub fn generate_private_functions(mut self, doit: bool) -> Self { + self.options.generate_private_functions = doit; + self + } + + }, + as_args: "--generate-private-functions", + }, }