Repository: mozilla/cbindgen (language: rust)

Issue:
Conditionally compiled `extern "C" {` blocks do not get `#if defined(` guards
While conditional compilation works fine *within* `extern "C"` blocks, annotating an entire `extern "C"` block with `#[cfg()]` is valid in Rust, but doesn't work in cbindgen.

For example, for the following Rust project:

```toml
# Cargo.toml
[package]
name = "extern_block_conditional"
version = "0.1.0"
authors = ["cbindgen"]

[lib]
name = "extern_block_conditional"
crate-type = ["lib", "dylib"]
features = [ "myfeature" ]
```

```toml
# cbindgen.toml
[defines]
"feature = myfeature" = "DEFINED"
```

```rust
// src/lib.rs
#[repr(C)]
struct Normal {
    x: i32,
    y: f32,
}

#[cfg(feature = "myfeature")]
extern "C" {
    fn foo() -> i32;

    fn bar(a: Normal);
}
```

I would expect the following C output:

```c
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct {
  int32_t x;
  float y;
} Normal;

#if defined(DEFINED)
extern int32_t foo(void);

extern void bar(Normal a);
#endif
```

But in fact, writing this as a test case in this project, the output I see is:

```c
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

typedef struct {
  int32_t x;
  float y;
} Normal;

extern int32_t foo(void);

extern void bar(Normal a);
```

cc @katef


Discussion / hints:

Fix patch:
diff --git a/src/bindgen/parser.rs b/src/bindgen/parser.rs
index 96db4025d..ddc3672a8 100644
--- a/src/bindgen/parser.rs
+++ b/src/bindgen/parser.rs
@@ -605,6 +605,7 @@ impl Parse {
             return;
         }
 
+        let mod_cfg = Cfg::append(mod_cfg, Cfg::load(&item.attrs));
         for foreign_item in &item.items {
             if let syn::ForeignItem::Fn(ref function) = *foreign_item {
                 if !config
@@ -618,7 +619,14 @@ impl Parse {
                     return;
                 }
                 let path = Path::new(function.sig.ident.unraw().to_string());
-                match Function::load(path, None, &function.sig, true, &function.attrs, mod_cfg) {
+                match Function::load(
+                    path,
+                    None,
+                    &function.sig,
+                    true,
+                    &function.attrs,
+                    mod_cfg.as_ref(),
+                ) {
                     Ok(func) => {
                         info!("Take {}::{}.", crate_name, &function.sig.ident);
 


Test patch:
diff --git a/tests/expectations/cfg.c b/tests/expectations/cfg.c
index 0194bb1c1..5f4eb9d6a 100644
--- a/tests/expectations/cfg.c
+++ b/tests/expectations/cfg.c
@@ -78,6 +78,11 @@ typedef struct {
   ;
 } ConditionalField;
 
+typedef struct {
+  int32_t x;
+  float y;
+} Normal;
+
 #if (defined(PLATFORM_UNIX) && defined(X11))
 void root(FooHandle a, C c);
 #endif
@@ -87,3 +92,11 @@ void root(BarHandle a, C c);
 #endif
 
 void cond(ConditionalField a);
+
+#if defined(PLATFORM_WIN)
+extern int32_t foo(void);
+#endif
+
+#if defined(PLATFORM_WIN)
+extern void bar(Normal a);
+#endif
diff --git a/tests/expectations/cfg.compat.c b/tests/expectations/cfg.compat.c
index 2092e0a15..8c01f08e5 100644
--- a/tests/expectations/cfg.compat.c
+++ b/tests/expectations/cfg.compat.c
@@ -96,6 +96,11 @@ typedef struct {
   ;
 } ConditionalField;
 
+typedef struct {
+  int32_t x;
+  float y;
+} Normal;
+
 #ifdef __cplusplus
 extern "C" {
 #endif // __cplusplus
@@ -110,6 +115,14 @@ void root(BarHandle a, C c);
 
 void cond(ConditionalField a);
 
+#if defined(PLATFORM_WIN)
+extern int32_t foo(void);
+#endif
+
+#if defined(PLATFORM_WIN)
+extern void bar(Normal a);
+#endif
+
 #ifdef __cplusplus
 } // extern "C"
 #endif // __cplusplus
diff --git a/tests/expectations/cfg.cpp b/tests/expectations/cfg.cpp
index 24937cf24..54b2eccc0 100644
--- a/tests/expectations/cfg.cpp
+++ b/tests/expectations/cfg.cpp
@@ -202,6 +202,20 @@ struct ConditionalField {
   ;
 };
 
+struct Normal {
+  int32_t x;
+  float y;
+
+  bool operator==(const Normal& other) const {
+    return x == other.x &&
+           y == other.y;
+  }
+  bool operator!=(const Normal& other) const {
+    return x != other.x ||
+           y != other.y;
+  }
+};
+
 extern "C" {
 
 #if (defined(PLATFORM_UNIX) && defined(X11))
@@ -214,4 +228,12 @@ void root(BarHandle a, C c);
 
 void cond(ConditionalField a);
 
+#if defined(PLATFORM_WIN)
+extern int32_t foo();
+#endif
+
+#if defined(PLATFORM_WIN)
+extern void bar(Normal a);
+#endif
+
 } // extern "C"
diff --git a/tests/expectations/cfg.pyx b/tests/expectations/cfg.pyx
index c4da23372..b3280da25 100644
--- a/tests/expectations/cfg.pyx
+++ b/tests/expectations/cfg.pyx
@@ -58,6 +58,10 @@ cdef extern from *:
   ctypedef struct ConditionalField:
     int32_t field;
 
+  ctypedef struct Normal:
+    int32_t x;
+    float y;
+
   IF (PLATFORM_UNIX and X11):
     void root(FooHandle a, C c);
 
@@ -65,3 +69,9 @@ cdef extern from *:
     void root(BarHandle a, C c);
 
   void cond(ConditionalField a);
+
+  IF PLATFORM_WIN:
+    extern int32_t foo();
+
+  IF PLATFORM_WIN:
+    extern void bar(Normal a);
diff --git a/tests/expectations/cfg_both.c b/tests/expectations/cfg_both.c
index 120a1ad1f..fb0553f14 100644
--- a/tests/expectations/cfg_both.c
+++ b/tests/expectations/cfg_both.c
@@ -78,6 +78,11 @@ typedef struct ConditionalField {
   ;
 } ConditionalField;
 
+typedef struct Normal {
+  int32_t x;
+  float y;
+} Normal;
+
 #if (defined(PLATFORM_UNIX) && defined(X11))
 void root(struct FooHandle a, union C c);
 #endif
@@ -87,3 +92,11 @@ void root(struct BarHandle a, union C c);
 #endif
 
 void cond(struct ConditionalField a);
+
+#if defined(PLATFORM_WIN)
+extern int32_t foo(void);
+#endif
+
+#if defined(PLATFORM_WIN)
+extern void bar(struct Normal a);
+#endif
diff --git a/tests/expectations/cfg_both.compat.c b/tests/expectations/cfg_both.compat.c
index 998e5bde7..1a2da53d1 100644
--- a/tests/expectations/cfg_both.compat.c
+++ b/tests/expectations/cfg_both.compat.c
@@ -96,6 +96,11 @@ typedef struct ConditionalField {
   ;
 } ConditionalField;
 
+typedef struct Normal {
+  int32_t x;
+  float y;
+} Normal;
+
 #ifdef __cplusplus
 extern "C" {
 #endif // __cplusplus
@@ -110,6 +115,14 @@ void root(struct BarHandle a, union C c);
 
 void cond(struct ConditionalField a);
 
+#if defined(PLATFORM_WIN)
+extern int32_t foo(void);
+#endif
+
+#if defined(PLATFORM_WIN)
+extern void bar(struct Normal a);
+#endif
+
 #ifdef __cplusplus
 } // extern "C"
 #endif // __cplusplus
diff --git a/tests/expectations/cfg_tag.c b/tests/expectations/cfg_tag.c
index 893526574..a2bdc24d8 100644
--- a/tests/expectations/cfg_tag.c
+++ b/tests/expectations/cfg_tag.c
@@ -78,6 +78,11 @@ struct ConditionalField {
   ;
 };
 
+struct Normal {
+  int32_t x;
+  float y;
+};
+
 #if (defined(PLATFORM_UNIX) && defined(X11))
 void root(struct FooHandle a, union C c);
 #endif
@@ -87,3 +92,11 @@ void root(struct BarHandle a, union C c);
 #endif
 
 void cond(struct ConditionalField a);
+
+#if defined(PLATFORM_WIN)
+extern int32_t foo(void);
+#endif
+
+#if defined(PLATFORM_WIN)
+extern void bar(struct Normal a);
+#endif
diff --git a/tests/expectations/cfg_tag.compat.c b/tests/expectations/cfg_tag.compat.c
index ceaebc72b..48768ab45 100644
--- a/tests/expectations/cfg_tag.compat.c
+++ b/tests/expectations/cfg_tag.compat.c
@@ -96,6 +96,11 @@ struct ConditionalField {
   ;
 };
 
+struct Normal {
+  int32_t x;
+  float y;
+};
+
 #ifdef __cplusplus
 extern "C" {
 #endif // __cplusplus
@@ -110,6 +115,14 @@ void root(struct BarHandle a, union C c);
 
 void cond(struct ConditionalField a);
 
+#if defined(PLATFORM_WIN)
+extern int32_t foo(void);
+#endif
+
+#if defined(PLATFORM_WIN)
+extern void bar(struct Normal a);
+#endif
+
 #ifdef __cplusplus
 } // extern "C"
 #endif // __cplusplus
diff --git a/tests/expectations/cfg_tag.pyx b/tests/expectations/cfg_tag.pyx
index 3c1b871b2..a258c64f5 100644
--- a/tests/expectations/cfg_tag.pyx
+++ b/tests/expectations/cfg_tag.pyx
@@ -58,6 +58,10 @@ cdef extern from *:
   cdef struct ConditionalField:
     int32_t field;
 
+  cdef struct Normal:
+    int32_t x;
+    float y;
+
   IF (PLATFORM_UNIX and X11):
     void root(FooHandle a, C c);
 
@@ -65,3 +69,9 @@ cdef extern from *:
     void root(BarHandle a, C c);
 
   void cond(ConditionalField a);
+
+  IF PLATFORM_WIN:
+    extern int32_t foo();
+
+  IF PLATFORM_WIN:
+    extern void bar(Normal a);
diff --git a/tests/rust/cfg.rs b/tests/rust/cfg.rs
index 903c156fd..1737e37e6 100644
--- a/tests/rust/cfg.rs
+++ b/tests/rust/cfg.rs
@@ -62,3 +62,17 @@ pub extern "C" fn root(a: BarHandle, c: C)
 #[no_mangle]
 pub extern "C" fn cond(a: ConditionalField)
 { }
+
+// src/lib.rs
+#[repr(C)]
+struct Normal {
+    x: i32,
+    y: f32,
+}
+
+#[cfg(windows)]
+extern "C" {
+    fn foo() -> i32;
+
+    fn bar(a: Normal);
+}