From 1ddc66afac93b3965ff5403b174755a28d3d1dfc Mon Sep 17 00:00:00 2001 From: Martin Boehme Date: Thu, 29 Apr 2021 09:10:31 +0200 Subject: [PATCH] Don't generate bindings for deleted member functions. See https://github.com/rust-lang/rust-bindgen/issues/2043 for details. --- src/clang.rs | 19 ++++ src/ir/function.rs | 4 + tests/expectations/tests/deleted-function.rs | 91 ++++++++++++++++++++ tests/headers/deleted-function.hpp | 35 ++++++++ 4 files changed, 149 insertions(+) create mode 100644 tests/expectations/tests/deleted-function.rs create mode 100644 tests/headers/deleted-function.hpp diff --git a/src/clang.rs b/src/clang.rs index 661250894e..033e101f9c 100644 --- a/src/clang.rs +++ b/src/clang.rs @@ -469,6 +469,25 @@ impl Cursor { unsafe { clang_Cursor_isFunctionInlined(self.x) != 0 } } + /// Is the referent a defaulted function? + pub fn is_defaulted_function(&self) -> bool { + unsafe { clang_CXXMethod_isDefaulted(self.x) != 0 } + } + + /// Is the referent a deleted function? + pub fn is_deleted_function(&self) -> bool { + // Unfortunately, libclang doesn't yet have an API for checking if a + // member function is deleted, but the following should be a good + // enough approximation. + // Deleted functions are implicitly inline according to paragraph 4 of + // [dcl.fct.def.delete] in the C++ standard. Normal inline functions + // have a definition in the same translation unit, so if this is an + // inline function without a definition, and it's not a defaulted + // function, we can reasonably safely conclude that it's a deleted + // function. + self.is_inlined_function() && self.definition().is_none() && !self.is_defaulted_function() + } + /// Get the width of this cursor's referent bit field, or `None` if the /// referent is not a bit field. pub fn bit_width(&self) -> Option { diff --git a/src/ir/function.rs b/src/ir/function.rs index c0938b653d..1319a650e9 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -597,6 +597,10 @@ impl ClangSubItemParser for Function { return Err(ParseError::Continue); } + if cursor.is_deleted_function() { + return Err(ParseError::Continue); + } + if !context.options().generate_inline_functions && cursor.is_inlined_function() { diff --git a/tests/expectations/tests/deleted-function.rs b/tests/expectations/tests/deleted-function.rs new file mode 100644 index 0000000000..96967bb461 --- /dev/null +++ b/tests/expectations/tests/deleted-function.rs @@ -0,0 +1,91 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct A { + pub _address: u8, +} +#[test] +fn bindgen_test_layout_A() { + assert_eq!( + ::std::mem::size_of::(), + 1usize, + concat!("Size of: ", stringify!(A)) + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(A)) + ); +} +extern "C" { + #[link_name = "\u{1}_ZN1A17inline_definitionEv"] + pub fn A_inline_definition(this: *mut A); +} +extern "C" { + #[link_name = "\u{1}_ZN1A22out_of_line_definitionEv"] + pub fn A_out_of_line_definition(this: *mut A); +} +impl A { + #[inline] + pub unsafe fn inline_definition(&mut self) { + A_inline_definition(self) + } + #[inline] + pub unsafe fn out_of_line_definition(&mut self) { + A_out_of_line_definition(self) + } +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct B { + pub _address: u8, +} +#[test] +fn bindgen_test_layout_B() { + assert_eq!( + ::std::mem::size_of::(), + 1usize, + concat!("Size of: ", stringify!(B)) + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(B)) + ); +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct C { + pub _address: u8, +} +#[test] +fn bindgen_test_layout_C() { + assert_eq!( + ::std::mem::size_of::(), + 1usize, + concat!("Size of: ", stringify!(C)) + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(C)) + ); +} +extern "C" { + #[link_name = "\u{1}_ZN1CC1ERS_"] + pub fn C_C(this: *mut C, arg1: *mut C); +} +impl C { + #[inline] + pub unsafe fn new(arg1: *mut C) -> Self { + let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit(); + C_C(__bindgen_tmp.as_mut_ptr(), arg1); + __bindgen_tmp.assume_init() + } +} diff --git a/tests/headers/deleted-function.hpp b/tests/headers/deleted-function.hpp new file mode 100644 index 0000000000..61848a0ae9 --- /dev/null +++ b/tests/headers/deleted-function.hpp @@ -0,0 +1,35 @@ +// bindgen-flags: --generate-inline-functions -- -std=c++11 + +class A { +public: + // Deleted function should not get a binding. + void deleted() = delete; + + // Inline functions should get bindings, whether they are defined inline + // (in the class) or out of line. + inline void inline_definition() {} + inline void out_of_line_definition(); + + // TODO: This is an edge case that we get wrong: An inline function + // without a definition in the same translation unit should still get a + // binding. We currently can't distinguish this case from a deleted member + // function because libclang doesn't provide a direct way to query for + // deleted member functions. This seems acceptable, however, as an inline + // function without a definition in the same translation unit is unlikely + // to be useful in practice. + inline void inline_without_definition(); +}; + +void A::out_of_line_definition() {} + +class B { +public: + // Deleted copy constructor should not get a binding. + B(B&) = delete; +}; + +class C { +public: + // Defaulted copy constructor should get a binding. + C(C&) = default; +}; \ No newline at end of file