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);
+}