diff --git a/bindgen-tests/tests/expectations/tests/3119_overlapping_alias_comment.rs b/bindgen-tests/tests/expectations/tests/3119_overlapping_alias_comment.rs new file mode 100644 index 0000000000..70008e72d1 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/3119_overlapping_alias_comment.rs @@ -0,0 +1,8 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +/** This is a forward declared struct alias with overlapping names + and documentation.*/ +#[repr(C)] +#[derive(Debug)] +pub struct Struct { + _unused: [u8; 0], +} diff --git a/bindgen-tests/tests/headers/3119_overlapping_alias_comment.h b/bindgen-tests/tests/headers/3119_overlapping_alias_comment.h new file mode 100644 index 0000000000..daa6c83df3 --- /dev/null +++ b/bindgen-tests/tests/headers/3119_overlapping_alias_comment.h @@ -0,0 +1,7 @@ +// bindgen-flags: --no-layout-tests + +/** + * This is a forward declared struct alias with overlapping names + * and documentation. + */ +typedef struct Struct Struct; diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs index 8e4163df5e..1fb0ef4b3c 100644 --- a/bindgen/ir/context.rs +++ b/bindgen/ir/context.rs @@ -1848,7 +1848,10 @@ If you encounter an error missing from this list, please file an issue or a PR!" ty: &clang::Type, location: Option, ) -> Option { - use clang_sys::{CXCursor_TypeAliasTemplateDecl, CXCursor_TypeRef}; + use clang_sys::{ + CXCursor_TypeAliasTemplateDecl, CXCursor_TypeRef, + CXCursor_TypedefDecl, + }; debug!("builtin_or_resolved_ty: {ty:?}, {location:?}, {with_id:?}, {parent_id:?}"); if let Some(decl) = ty.canonical_declaration(location.as_ref()) { @@ -1864,6 +1867,18 @@ If you encounter an error missing from this list, please file an issue or a PR!" // * we have already parsed and resolved this type, and // there's nothing left to do. if let Some(location) = location { + // When a hidden `typedef struct Foo Foo;` carries docs, the + // alias is not emitted in codegen and those docs would + // otherwise be lost. Attach the docs to the already-resolved + // target type here. + if location.kind() == CXCursor_TypedefDecl { + self.inherit_typedef_comment( + id, + *decl.cursor(), + location, + ); + } + if decl.cursor().is_template_like() && *ty != decl.cursor().cur_type() { @@ -1899,6 +1914,28 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.build_builtin_ty(ty) } + fn inherit_typedef_comment( + &mut self, + resolved_type: TypeId, + decl: Cursor, + typedef_cursor: Cursor, + ) { + let Some(comment) = typedef_cursor.raw_comment() else { + return; + }; + + // Only inherit docs for hidden overlapping aliases, e.g. + // `typedef struct Foo Foo;`. + if typedef_cursor.spelling() != decl.spelling() { + return; + } + + let item_id: ItemId = resolved_type.into(); + if let Some(item) = self.items[item_id.0].as_mut() { + item.set_comment_if_none(comment); + } + } + /// Make a new item that is a resolved type reference to the `wrapped_id`. /// /// This is unfortunately a lot of bloat, but is needed to properly track diff --git a/bindgen/ir/item.rs b/bindgen/ir/item.rs index eea02cce6c..77b4d1bbb1 100644 --- a/bindgen/ir/item.rs +++ b/bindgen/ir/item.rs @@ -516,6 +516,13 @@ impl Item { .map(|comment| ctx.options().process_comment(comment)) } + /// Set this item's raw comment if it does not already have one. + pub(crate) fn set_comment_if_none(&mut self, comment: String) { + if self.comment.is_none() { + self.comment = Some(comment); + } + } + /// What kind of item is this? pub(crate) fn kind(&self) -> &ItemKind { &self.kind