Skip to content
Merged
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
40 changes: 40 additions & 0 deletions bindgen-tests/tests/expectations/tests/issue-3441.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions bindgen-tests/tests/headers/issue-3441.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// bindgen-flags: -- -std=c++23
// bindgen-min-clang-version: 18

struct S {
int value(this S self, int y);
int reference([[maybe_unused]] this S& self, int y);
int regular(int y);
};
26 changes: 26 additions & 0 deletions bindgen-tests/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,17 @@ fn compare_generated_header(
builder: BuilderState,
check_roundtrip: bool,
) -> Result<(), Error> {
let skip_for_clang_version = if let Some(minimum) =
builder.minimum_clang_version
{
matches!(clang_version().parsed, Some((major, _)) if major < minimum)
} else {
false
};
if skip_for_clang_version {
return Ok(());
}

let file_name = header.file_name().ok_or_else(|| {
Error::new(ErrorKind::Other, "compare_generated_header expects a file")
})?;
Expand Down Expand Up @@ -241,6 +252,7 @@ fn builder() -> Builder {
struct BuilderState {
builder: Builder,
parse_callbacks: Option<String>,
minimum_clang_version: Option<u32>,
}

impl BuilderState {
Expand All @@ -259,6 +271,7 @@ impl BuilderState {
Some(BuilderState {
builder,
parse_callbacks: self.parse_callbacks,
minimum_clang_version: self.minimum_clang_version,
})
} else {
None
Expand All @@ -277,6 +290,7 @@ fn create_bindgen_builder(header: &Path) -> Result<BuilderState, Error> {
// Scoop up bindgen-flags from test header
let mut flags = Vec::with_capacity(2);
let mut parse_callbacks = None;
let mut minimum_clang_version = None;

for line in reader.lines() {
let line = line?;
Expand All @@ -302,6 +316,17 @@ fn create_bindgen_builder(header: &Path) -> Result<BuilderState, Error> {
let parse_cb =
line.split("bindgen-parse-callbacks: ").last().unwrap();
parse_callbacks = Some(parse_cb.to_owned());
} else if line.contains("bindgen-min-clang-version: ") {
let version =
line.split("bindgen-min-clang-version: ").last().unwrap();
minimum_clang_version = Some(version.parse().map_err(|err| {
Error::new(
ErrorKind::InvalidData,
format!(
"Invalid minimum libclang version '{version}': {err}"
),
)
})?);
}
}

Expand Down Expand Up @@ -349,6 +374,7 @@ fn create_bindgen_builder(header: &Path) -> Result<BuilderState, Error> {
Ok(BuilderState {
builder,
parse_callbacks,
minimum_clang_version,
})
}

Expand Down
14 changes: 14 additions & 0 deletions bindgen/clang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,20 @@ impl Cursor {
unsafe { clang_CXXMethod_isStatic(self.x) != 0 }
}

/// Is this cursor's referent a C++23 explicit-object member function?
pub(crate) fn method_is_explicit_object(&self) -> bool {
debug_assert_eq!(self.kind(), CXCursor_CXXMethod);

// libclang has no direct API for this. The first parameter's extent
// starts at `this`, even when attributes precede the parameter.
self.args()
.and_then(|args| args.into_iter().next())
.and_then(|arg| arg.tokens().iter().next())
.is_some_and(|token| {
token.kind == CXToken_Keyword && token.spelling() == b"this"
})
}

/// Is this cursor's referent a member function that is declared `const`?
pub(crate) fn method_is_const(&self) -> bool {
unsafe { clang_CXXMethod_isConst(self.x) != 0 }
Expand Down
8 changes: 6 additions & 2 deletions bindgen/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3164,6 +3164,7 @@ impl Method {
MethodKind::Destructor |
MethodKind::VirtualDestructor { .. } => cc.destructors(),
MethodKind::Static |
MethodKind::ExplicitObject |
MethodKind::Normal |
MethodKind::Virtual { .. } => cc.methods(),
}
Expand Down Expand Up @@ -3237,7 +3238,10 @@ impl Method {
let mut args = utils::fnsig_arguments(ctx, signature);
let mut ret = utils::fnsig_return_ty(ctx, signature);

if !self.is_static() && !self.is_constructor() {
if !self.is_static() &&
!self.is_explicit_object() &&
!self.is_constructor()
{
args[0] = if self.is_const() {
quote! { &self }
} else {
Expand Down Expand Up @@ -3271,7 +3275,7 @@ impl Method {
let mut __bindgen_tmp = ::#prefix::mem::MaybeUninit::uninit()
};
stmts.push(tmp_variable_decl);
} else if !self.is_static() {
} else if !self.is_static() && !self.is_explicit_object() {
assert!(!exprs.is_empty());
exprs[0] = quote! {
self
Expand Down
11 changes: 10 additions & 1 deletion bindgen/ir/comp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub(crate) enum MethodKind {
},
/// A static method.
Static,
/// A method with an explicit object parameter.
ExplicitObject,
/// A normal method.
Normal,
/// A virtual method.
Expand Down Expand Up @@ -72,7 +74,7 @@ impl MethodKind {
}
}

/// A struct representing a C++ method, either static, normal, or virtual.
/// A struct representing a C++ method.
#[derive(Debug)]
pub(crate) struct Method {
kind: MethodKind,
Expand Down Expand Up @@ -121,6 +123,11 @@ impl Method {
self.kind == MethodKind::Static
}

/// Does this method have an explicit object parameter?
pub(crate) fn is_explicit_object(&self) -> bool {
self.kind == MethodKind::ExplicitObject
}

/// Get the ID for the `Function` signature for this method.
pub(crate) fn signature(&self) -> FunctionId {
self.signature
Expand Down Expand Up @@ -1581,6 +1588,8 @@ impl CompInfo {
MethodKind::Virtual {
pure_virtual: cur.method_is_pure_virtual(),
}
} else if cur.method_is_explicit_object() {
MethodKind::ExplicitObject
} else {
MethodKind::Normal
};
Expand Down
8 changes: 6 additions & 2 deletions bindgen/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ impl FunctionKind {
})
} else if cursor.method_is_static() {
FunctionKind::Method(MethodKind::Static)
} else if cursor.method_is_explicit_object() {
FunctionKind::Method(MethodKind::ExplicitObject)
} else {
FunctionKind::Method(MethodKind::Normal)
}
Expand Down Expand Up @@ -528,8 +530,10 @@ impl FunctionSig {
if is_method || is_constructor || is_destructor {
let is_const = is_method && cursor.method_is_const();
let is_virtual = is_method && cursor.method_is_virtual();
let is_static = is_method && cursor.method_is_static();
if !is_static &&
let has_implicit_object = is_method &&
!cursor.method_is_static() &&
!cursor.method_is_explicit_object();
if (!is_method || has_implicit_object) &&
(!is_virtual ||
ctx.options().use_specific_virtual_function_receiver)
{
Expand Down
1 change: 1 addition & 0 deletions bindgen/ir/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,7 @@ impl Item {
) => cc.destructors(),
FunctionKind::Method(
MethodKind::Static |
MethodKind::ExplicitObject |
MethodKind::Normal |
MethodKind::Virtual { .. },
) => cc.methods(),
Expand Down
Loading