From 38801967ce06d67a107b7d8aae87618deba348b1 Mon Sep 17 00:00:00 2001
From: Christian Poveda <git@pvdrz.com>
Date: Mon, 2 Dec 2024 11:04:52 -0500
Subject: [PATCH 1/2] test: check attribute/derive ordering

---
 Cargo.lock                                    | 21 +++++++++++++++
 Cargo.toml                                    |  1 +
 bindgen-tests/tests/expectations/Cargo.toml   |  1 +
 .../tests/derive-and-attribute-order.rs       |  9 +++++++
 .../headers/derive-and-attribute-order.h      |  7 +++++
 bindgen-tests/tests/parse_callbacks/mod.rs    | 27 ++++++++++++++++++-
 6 files changed, 65 insertions(+), 1 deletion(-)
 create mode 100644 bindgen-tests/tests/expectations/tests/derive-and-attribute-order.rs
 create mode 100644 bindgen-tests/tests/headers/derive-and-attribute-order.h

diff --git a/Cargo.lock b/Cargo.lock
index 142112a213..e3d06d9722 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -550,6 +550,26 @@ dependencies = [
  "windows-sys 0.48.0",
 ]
 
+[[package]]
+name = "serde"
+version = "1.0.215"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.215"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.90",
+]
+
 [[package]]
 name = "shlex"
 version = "1.3.0"
@@ -620,6 +640,7 @@ dependencies = [
  "block",
  "libloading",
  "objc",
+ "serde",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index cd5e4e6223..9494919f67 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -42,6 +42,7 @@ quickcheck = "1.0"
 quote = { version = "1", default-features = false }
 regex = { version = "1.5.3", default-features = false }
 rustc-hash = "2.1.0"
+serde = { version = "1", features = ["derive"] }
 shlex = "1"
 similar = "2.2.1"
 syn = "2.0"
diff --git a/bindgen-tests/tests/expectations/Cargo.toml b/bindgen-tests/tests/expectations/Cargo.toml
index 975fd16678..78c91a9c95 100644
--- a/bindgen-tests/tests/expectations/Cargo.toml
+++ b/bindgen-tests/tests/expectations/Cargo.toml
@@ -15,6 +15,7 @@ edition.workspace = true
 block.workspace = true
 libloading.workspace = true
 objc.workspace = true
+serde.workspace = true
 
 # Both of these sections need to be copied here from the workspace because
 # Cargo currently does not allow overriding workspace settings in a member
diff --git a/bindgen-tests/tests/expectations/tests/derive-and-attribute-order.rs b/bindgen-tests/tests/expectations/tests/derive-and-attribute-order.rs
new file mode 100644
index 0000000000..02afb8d203
--- /dev/null
+++ b/bindgen-tests/tests/expectations/tests/derive-and-attribute-order.rs
@@ -0,0 +1,9 @@
+#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, serde::Serialize)]
+#[serde(rename_all = "UPPERCASE")]
+pub struct color {
+    pub red: ::std::os::raw::c_int,
+    pub green: ::std::os::raw::c_int,
+    pub blue: ::std::os::raw::c_int,
+}
diff --git a/bindgen-tests/tests/headers/derive-and-attribute-order.h b/bindgen-tests/tests/headers/derive-and-attribute-order.h
new file mode 100644
index 0000000000..7a71a4f69b
--- /dev/null
+++ b/bindgen-tests/tests/headers/derive-and-attribute-order.h
@@ -0,0 +1,7 @@
+// bindgen-flags: --no-layout-tests
+// bindgen-parse-callbacks: derive-uppercase-serialize=color
+typedef struct {
+    int red;
+    int green;
+    int blue;
+} color;
diff --git a/bindgen-tests/tests/parse_callbacks/mod.rs b/bindgen-tests/tests/parse_callbacks/mod.rs
index 7aca0fd1a1..b3d3d1b7b3 100644
--- a/bindgen-tests/tests/parse_callbacks/mod.rs
+++ b/bindgen-tests/tests/parse_callbacks/mod.rs
@@ -146,6 +146,27 @@ impl ParseCallbacks for WrapAsVariadicFn {
     }
 }
 
+#[derive(Debug)]
+struct DeriveTransparentSerialize(String);
+
+impl ParseCallbacks for DeriveTransparentSerialize {
+    fn add_derives(&self, info: &DeriveInfo<'_>) -> Vec<String> {
+        if info.name == &self.0 {
+            vec!["serde::Serialize".to_owned()]
+        } else {
+            vec![]
+        }
+    }
+
+    fn add_attributes(&self, info: &AttributeInfo<'_>) -> Vec<String> {
+        if info.name == &self.0 {
+            vec!["#[serde(rename_all = \"UPPERCASE\")]".to_owned()]
+        } else {
+            vec![]
+        }
+    }
+}
+
 pub fn lookup(cb: &str) -> Box<dyn ParseCallbacks> {
     match cb {
         "enum-variant-rename" => Box::new(EnumVariantRename),
@@ -155,7 +176,11 @@ pub fn lookup(cb: &str) -> Box<dyn ParseCallbacks> {
         "wrap-as-variadic-fn" => Box::new(WrapAsVariadicFn),
         "type-visibility" => Box::new(TypeVisibility),
         call_back => {
-            if let Some(prefix) =
+            if let Some(name) =
+                call_back.strip_prefix("derive-uppercase-serialize=")
+            {
+                Box::new(DeriveTransparentSerialize(name.to_owned()))
+            } else if let Some(prefix) =
                 call_back.strip_prefix("remove-function-prefix-")
             {
                 let lnopc = RemovePrefixParseCallback::new(prefix);

From 879cbcbeb8d048941059901468763aeedd107fb3 Mon Sep 17 00:00:00 2001
From: Christian Poveda <git@pvdrz.com>
Date: Mon, 2 Dec 2024 11:04:52 -0500
Subject: [PATCH 2/2] test: disable formatting on demand

---
 .../tests/derive-and-attribute-order.rs       | 12 +++------
 .../headers/derive-and-attribute-order.h      |  1 +
 bindgen-tests/tests/tests.rs                  | 26 ++++++++++++++-----
 3 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/bindgen-tests/tests/expectations/tests/derive-and-attribute-order.rs b/bindgen-tests/tests/expectations/tests/derive-and-attribute-order.rs
index 02afb8d203..ecb81f3f93 100644
--- a/bindgen-tests/tests/expectations/tests/derive-and-attribute-order.rs
+++ b/bindgen-tests/tests/expectations/tests/derive-and-attribute-order.rs
@@ -1,9 +1,5 @@
+
 #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
-#[repr(C)]
-#[derive(Debug, Default, Copy, Clone, serde::Serialize)]
-#[serde(rename_all = "UPPERCASE")]
-pub struct color {
-    pub red: ::std::os::raw::c_int,
-    pub green: ::std::os::raw::c_int,
-    pub blue: ::std::os::raw::c_int,
-}
+
+
+# [repr (C)] # [derive (Debug , Default , Copy , Clone , serde :: Serialize)] # [serde (rename_all = "UPPERCASE")] pub struct color { pub red : :: std :: os :: raw :: c_int , pub green : :: std :: os :: raw :: c_int , pub blue : :: std :: os :: raw :: c_int , }
\ No newline at end of file
diff --git a/bindgen-tests/tests/headers/derive-and-attribute-order.h b/bindgen-tests/tests/headers/derive-and-attribute-order.h
index 7a71a4f69b..f404b3abd1 100644
--- a/bindgen-tests/tests/headers/derive-and-attribute-order.h
+++ b/bindgen-tests/tests/headers/derive-and-attribute-order.h
@@ -1,5 +1,6 @@
 // bindgen-flags: --no-layout-tests
 // bindgen-parse-callbacks: derive-uppercase-serialize=color
+// bindgen-skip-formatting
 typedef struct {
     int red;
     int green;
diff --git a/bindgen-tests/tests/tests.rs b/bindgen-tests/tests/tests.rs
index 0b3ebe1533..40486eda40 100644
--- a/bindgen-tests/tests/tests.rs
+++ b/bindgen-tests/tests/tests.rs
@@ -187,17 +187,23 @@ fn compare_generated_header(
             looked_at,
         ),
     };
-
+    let do_formatting = builder.do_formatting;
     let (builder, roundtrip_builder) = builder.into_builder(check_roundtrip)?;
 
     // We skip the generate() error here so we get a full diff below
     let actual = match builder.generate() {
-        Ok(bindings) => format_code(bindings.to_string()).map_err(|err| {
-            Error::new(
-                ErrorKind::Other,
-                format!("Cannot parse the generated bindings: {err}"),
-            )
-        })?,
+        Ok(bindings) => {
+            if do_formatting {
+                format_code(bindings.to_string()).map_err(|err| {
+                    Error::new(
+                        ErrorKind::Other,
+                        format!("Cannot parse the generated bindings: {err}"),
+                    )
+                })?
+            } else {
+                bindings.to_string()
+            }
+        }
         Err(_) => "/* error generating bindings */\n".into(),
     };
 
@@ -237,6 +243,7 @@ fn builder() -> Builder {
 struct BuilderState {
     builder: Builder,
     parse_callbacks: Option<String>,
+    do_formatting: bool,
 }
 
 impl BuilderState {
@@ -255,6 +262,7 @@ impl BuilderState {
             Some(BuilderState {
                 builder,
                 parse_callbacks: self.parse_callbacks,
+                do_formatting: self.do_formatting,
             })
         } else {
             None
@@ -273,6 +281,7 @@ fn create_bindgen_builder(header: &Path) -> Result<BuilderState, Error> {
     // Scoop up bindgen-flags from test header
     let mut flags = Vec::with_capacity(2);
     let mut parse_callbacks = None;
+    let mut do_formatting = true;
 
     for line in reader.lines() {
         let line = line?;
@@ -298,6 +307,8 @@ fn create_bindgen_builder(header: &Path) -> Result<BuilderState, Error> {
             let parse_cb =
                 line.split("bindgen-parse-callbacks: ").last().unwrap();
             parse_callbacks = Some(parse_cb.to_owned());
+        } else if line.contains("bindgen-skip-formatting") {
+            do_formatting = false;
         }
     }
 
@@ -345,6 +356,7 @@ fn create_bindgen_builder(header: &Path) -> Result<BuilderState, Error> {
     Ok(BuilderState {
         builder,
         parse_callbacks,
+        do_formatting,
     })
 }