Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't generate bindings for deleted member functions #2044

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/clang.rs
Expand Up @@ -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<u32> {
Expand Down
4 changes: 4 additions & 0 deletions src/ir/function.rs
Expand Up @@ -597,6 +597,10 @@ impl ClangSubItemParser for Function {
return Err(ParseError::Continue);
}

if cursor.is_deleted_function() {
return Err(ParseError::Continue);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It might be worth to shuffle the logic a bit to do less work in the common case, something like:

if cursor.is_inlined_function() {
    if !context.options().generate_inline_functions {
        return Err(..);
    }
    if cursor.is_deleted_function() {
        return Err(..);
    }
}


if !context.options().generate_inline_functions &&
cursor.is_inlined_function()
{
Expand Down
91 changes: 91 additions & 0 deletions 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::<A>(),
1usize,
concat!("Size of: ", stringify!(A))
);
assert_eq!(
::std::mem::align_of::<A>(),
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::<B>(),
1usize,
concat!("Size of: ", stringify!(B))
);
assert_eq!(
::std::mem::align_of::<B>(),
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::<C>(),
1usize,
concat!("Size of: ", stringify!(C))
);
assert_eq!(
::std::mem::align_of::<C>(),
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()
}
}
35 changes: 35 additions & 0 deletions 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;
};