From 2db8777572717612e6291716dfb435c113345c60 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Tue, 11 Oct 2022 10:49:59 -0500 Subject: [PATCH] Handle the `const struct *` and `struct *` patterns. Given that C keeps a different namespace for `struct`s and aliases. The following patterns ```c typedef const struct foo { void *inner; } *foo; typedef struct bar { void *inner; } *bar; ``` are valid C code and produces both a `struct` and a pointer called `foo` and `bar` in different namespaces. Given that Rust does not make this distinction, we add the `_ptr` prefix to the pointer type aliases to avoid any name collisions. --- .../tests/expectations/tests/struct_ptr.rs | 40 +++++++++++++++++++ bindgen-tests/tests/headers/struct_ptr.h | 12 ++++++ bindgen/ir/ty.rs | 18 +++++++++ 3 files changed, 70 insertions(+) create mode 100644 bindgen-tests/tests/expectations/tests/struct_ptr.rs create mode 100644 bindgen-tests/tests/headers/struct_ptr.h diff --git a/bindgen-tests/tests/expectations/tests/struct_ptr.rs b/bindgen-tests/tests/expectations/tests/struct_ptr.rs new file mode 100644 index 0000000000..d348435f84 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/struct_ptr.rs @@ -0,0 +1,40 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct foo { + pub bar: ::std::os::raw::c_char, +} +#[test] +fn bindgen_test_layout_foo() { + const UNINIT: ::std::mem::MaybeUninit = + ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 1usize, + concat!("Size of: ", stringify!(foo)) + ); + assert_eq!( + ::std::mem::align_of::(), + 1usize, + concat!("Alignment of ", stringify!(foo)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).bar) as usize - ptr as usize }, + 0usize, + concat!("Offset of field: ", stringify!(foo), "::", stringify!(bar)) + ); +} +pub type foo_ptr = *const foo; +extern "C" { + pub fn takes_ptr(arg1: foo_ptr); +} +extern "C" { + pub fn takes_struct(arg1: foo); +} diff --git a/bindgen-tests/tests/headers/struct_ptr.h b/bindgen-tests/tests/headers/struct_ptr.h new file mode 100644 index 0000000000..656f2aece5 --- /dev/null +++ b/bindgen-tests/tests/headers/struct_ptr.h @@ -0,0 +1,12 @@ +typedef const struct foo { + char inner; +} *foo; + +typedef struct bar { + char inner; +} *bar; + +void takes_foo_ptr(foo); +void takes_foo_struct(struct foo); +void takes_bar_ptr(bar); +void takes_bar_struct(struct bar); diff --git a/bindgen/ir/ty.rs b/bindgen/ir/ty.rs index 6a3fd0e8c8..a430b1cd26 100644 --- a/bindgen/ir/ty.rs +++ b/bindgen/ir/ty.rs @@ -1089,6 +1089,7 @@ impl Type { } CXType_Typedef => { let inner = cursor.typedef_type().expect("Not valid Type?"); + let inner_spelling = inner.spelling(); let inner = Item::from_ty_or_ref(inner, location, None, ctx); if inner == potential_id { @@ -1099,6 +1100,23 @@ impl Type { // within the clang parsing. TypeKind::Opaque } else { + // Check if this type definition is an alias to a pointer of a `const + // struct` with the same name and add the `_ptr` suffix to it to avoid name + // collisions. + if !ctx.options().c_naming { + if let Some(inner_name) = inner_spelling + .strip_prefix("const struct ") + .or_else(|| { + inner_spelling.strip_prefix("struct ") + }) + .and_then(|s| s.strip_suffix(" *")) + { + if inner_name == name { + name += "_ptr"; + } + } + } + TypeKind::Alias(inner) } }