diff --git a/crates/libs/rdl/src/clang/cx.rs b/crates/libs/rdl/src/clang/cx.rs index a54ee317820..ffc711f4f01 100644 --- a/crates/libs/rdl/src/clang/cx.rs +++ b/crates/libs/rdl/src/clang/cx.rs @@ -289,6 +289,10 @@ impl Cursor { pub fn result_type(&self) -> Type { Type(unsafe { clang_getCursorResultType(self.0) }) } + + pub fn language(&self) -> CXLanguageKind { + unsafe { clang_getCursorLanguage(self.0) } + } } pub struct Type(CXType); diff --git a/crates/libs/rdl/src/clang/fn.rs b/crates/libs/rdl/src/clang/fn.rs index c0b22d0853c..af59eac32c4 100644 --- a/crates/libs/rdl/src/clang/fn.rs +++ b/crates/libs/rdl/src/clang/fn.rs @@ -7,10 +7,16 @@ pub struct Fn { pub library: String, pub params: Vec, pub return_type: metadata::Type, + pub extern_c: bool, } impl Fn { - pub fn parse(cursor: Cursor, namespace: &str, library: &str) -> Result { + pub fn parse( + cursor: Cursor, + namespace: &str, + library: &str, + extern_c: bool, + ) -> Result { let name = cursor.name(); let return_type = cursor.result_type().to_type(namespace); @@ -32,6 +38,7 @@ impl Fn { library: library.to_string(), params, return_type, + extern_c, }) } @@ -53,9 +60,15 @@ impl Fn { } }; + let abi = if self.extern_c { + quote! { "C" } + } else { + quote! {} + }; + Ok(quote! { #[library(#library)] - extern fn #name(#(#params),*) #return_type; + extern #abi fn #name(#(#params),*) #return_type; }) } } diff --git a/crates/libs/rdl/src/clang/mod.rs b/crates/libs/rdl/src/clang/mod.rs index c1f954deb5b..c604f5ec51f 100644 --- a/crates/libs/rdl/src/clang/mod.rs +++ b/crates/libs/rdl/src/clang/mod.rs @@ -203,7 +203,32 @@ impl Clang { } } CXCursor_FunctionDecl if !child.is_definition() => { - collector.insert(Item::Fn(Fn::parse(child, &self.namespace, &self.library)?)); + collector.insert(Item::Fn(Fn::parse( + child, + &self.namespace, + &self.library, + false, + )?)); + } + // Recurse into `extern "C" { }` or `extern "C++" { }` blocks so + // that function declarations inside them are collected with the + // correct ABI annotation. + CXCursor_LinkageSpec => { + for inner in child.children() { + if !inner.is_from_main_file() { + continue; + } + if inner.kind() == CXCursor_FunctionDecl && !inner.is_definition() { + // A function inside `extern "C" { }` has C language linkage. + let extern_c = inner.language() == CXLanguage_C; + collector.insert(Item::Fn(Fn::parse( + inner, + &self.namespace, + &self.library, + extern_c, + )?)); + } + } } CXCursor_MacroDefinition => { if let Some(c) = Const::parse(child, &self.namespace, tu)? { diff --git a/crates/tests/libs/clang/roundtrip/fn_extern_c.h b/crates/tests/libs/clang/roundtrip/fn_extern_c.h new file mode 100644 index 00000000000..3f2d6a430c2 --- /dev/null +++ b/crates/tests/libs/clang/roundtrip/fn_extern_c.h @@ -0,0 +1,5 @@ +extern "C" +{ + unsigned int GetTickCount(); + void SetLastErrorEx(unsigned a, signed b); +} diff --git a/crates/tests/libs/clang/roundtrip/fn_extern_c.rdl b/crates/tests/libs/clang/roundtrip/fn_extern_c.rdl new file mode 100644 index 00000000000..0f4967f1002 --- /dev/null +++ b/crates/tests/libs/clang/roundtrip/fn_extern_c.rdl @@ -0,0 +1,7 @@ +#[win32] +mod Test { + #[library("test.dll")] + extern "C" fn GetTickCount() -> u32; + #[library("test.dll")] + extern "C" fn SetLastErrorEx(a: u32, b: i32); +}