Support `cpp_name` annotation for struct/enum.

PiperOrigin-RevId: 659351942
Change-Id: Ida7b662388e243489f98512ed882d60152b91ffd
diff --git a/bazel/llvm.bzl b/bazel/llvm.bzl
index 99d30d8..30dcde5 100644
--- a/bazel/llvm.bzl
+++ b/bazel/llvm.bzl
@@ -53,7 +53,7 @@
             executable = False,
         )
 
-LLVM_COMMIT_SHA = "90617e99bb17303b351351681a70394c312e0e58"
+LLVM_COMMIT_SHA = "a0a9bf5152507beacd2a72dda42d054391494c4a"
 
 def llvm_loader_repository_dependencies():
     # This *declares* the dependency, but it won't actually be *downloaded* unless it's used.
diff --git a/cc_bindings_from_rs/bindings.rs b/cc_bindings_from_rs/bindings.rs
index f7c0d46..d0f0eb3 100644
--- a/cc_bindings_from_rs/bindings.rs
+++ b/cc_bindings_from_rs/bindings.rs
@@ -279,6 +279,16 @@
     /// For example, if a type has `#[__crubit::annotate(cpp_type="x::y")]`,
     /// then cpp_type will be `Some(x::y)`.
     cpp_type: Option<Symbol>,
+
+    /// The C++ name to use for the symbol.
+    ///
+    /// For example, the following struct
+    /// ```
+    /// #[__crubit::annotate(cpp_name="Bar")]
+    /// struct Foo { ... }
+    /// ```
+    /// will be generated as a C++ struct named `Bar` instead of `Foo`.
+    cpp_name: Option<Symbol>,
 }
 
 impl FullyQualifiedName {
@@ -291,11 +301,13 @@
 
         // Crash OK: these attributes are introduced by crubit itself, and "should
         // never" be malformed.
-        let cpp_type = crubit_attr::get(tcx, def_id).unwrap().cpp_type;
+        let attributes = crubit_attr::get(tcx, def_id).unwrap();
+        let cpp_type = attributes.cpp_type;
 
         let mut full_path = tcx.def_path(def_id).data; // mod_path + name
         let name = full_path.pop().expect("At least the item's name should be present");
         let name = name.data.get_opt_name();
+        let cpp_name = attributes.cpp_name.map(|s| Symbol::intern(s.as_str())).or(name);
 
         let mod_path = NamespaceQualifier::new(
             full_path
@@ -304,7 +316,7 @@
                 .map(|s| Rc::<str>::from(s.as_str())),
         );
 
-        Self { krate, mod_path, name, cpp_type }
+        Self { krate, mod_path, name, cpp_type, cpp_name }
     }
 
     fn format_for_cc(&self) -> Result<TokenStream> {
@@ -313,8 +325,9 @@
             return Ok(quote! {#path});
         }
 
-        let name =
-            self.name.as_ref().expect("`format_for_cc` can't be called on name-less item kinds");
+        let name = self.cpp_name.as_ref().unwrap_or_else(|| {
+            self.name.as_ref().expect("`format_for_cc` can't be called on name-less item kinds")
+        });
 
         let top_level_ns = format_cc_ident(self.krate.as_str())?;
         let ns_path = self.mod_path.format_for_cc()?;
@@ -1383,7 +1396,7 @@
         let struct_name = match struct_name.as_ref() {
             None => quote! {},
             Some(fully_qualified_name) => {
-                let name = fully_qualified_name.name.expect("Structs always have a name");
+                let name = fully_qualified_name.cpp_name.expect("Structs always have a name");
                 let name = format_cc_ident(name.as_str())
                     .expect("Caller of format_fn should verify struct via format_adt_core");
                 quote! { #name :: }
@@ -1580,7 +1593,9 @@
     assert!(self_ty.is_adt());
     assert!(is_directly_public(tcx, def_id), "Caller should verify");
 
-    let item_name = tcx.item_name(def_id);
+    let attribute = crubit_attr::get(tcx, def_id).unwrap();
+
+    let item_name = attribute.cpp_name.unwrap_or_else(|| tcx.item_name(def_id));
     let rs_fully_qualified_name = format_ty_for_rs(tcx, self_ty)?;
     let cc_short_name =
         format_cc_ident(item_name.as_str()).context("Error formatting item name")?;
@@ -3858,6 +3873,41 @@
         });
     }
 
+    #[test]
+    fn test_format_struct_cpp_name() {
+        let test_src = r#"
+                #![feature(register_tool)]
+                #![register_tool(__crubit)]
+
+                #[__crubit::annotate(cpp_name="Bar")]
+                pub struct Foo {
+                    pub x: i32,
+                }
+            "#;
+        test_format_item(test_src, "Foo", |result| {
+            let result = result.unwrap().unwrap();
+            let main_api = &result.main_api;
+            assert!(!main_api.prereqs.is_empty());
+
+            assert_rs_matches!(
+                result.rs_details,
+                quote! {
+                    const _: () = assert!(::std::mem::size_of::<::rust_out::Foo>() == 4);
+                    const _: () = assert!(::std::mem::align_of::<::rust_out::Foo>() == 4);
+                    const _: () = assert!(::core::mem::offset_of!(::rust_out::Foo, x) == 0);
+                }
+            );
+
+            assert_cc_matches!(
+                main_api.tokens,
+                quote! {
+                    struct CRUBIT_INTERNAL_RUST_TYPE(":: rust_out :: Foo") alignas(4)
+                    [[clang::trivial_abi]] Bar final
+                }
+            );
+        });
+    }
+
     /// `test_format_item_fn_const` tests how bindings for an `const fn` are
     /// generated.
     ///
diff --git a/cc_bindings_from_rs/test/attribute/BUILD b/cc_bindings_from_rs/test/attribute/BUILD
new file mode 100644
index 0000000..665d6d0
--- /dev/null
+++ b/cc_bindings_from_rs/test/attribute/BUILD
@@ -0,0 +1,33 @@
+"""End-to-end tests of `cc_bindings_from_rs`, focusing on union-related
+bindings."""
+
+load(
+    "@rules_rust//rust:defs.bzl",
+    "rust_library",
+)
+load(
+    "//cc_bindings_from_rs/bazel_support:cc_bindings_from_rust_rule.bzl",
+    "cc_bindings_from_rust",
+)
+load("//common:crubit_wrapper_macros_oss.bzl", "crubit_cc_test")
+
+package(default_applicable_licenses = ["//:license"])
+
+rust_library(
+    name = "cpp_name",
+    srcs = ["cpp_name.rs"],
+)
+
+cc_bindings_from_rust(
+    name = "cpp_name_cc_api",
+    crate = ":cpp_name",
+)
+
+crubit_cc_test(
+    name = "cpp_name_test",
+    srcs = ["cpp_name_test.cc"],
+    deps = [
+        ":cpp_name_cc_api",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
diff --git a/cc_bindings_from_rs/test/attribute/cpp_name.rs b/cc_bindings_from_rs/test/attribute/cpp_name.rs
new file mode 100644
index 0000000..6952c4d
--- /dev/null
+++ b/cc_bindings_from_rs/test/attribute/cpp_name.rs
@@ -0,0 +1,17 @@
+// Part of the Crubit project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#![feature(register_tool)]
+#![register_tool(__crubit)]
+
+#[__crubit::annotate(cpp_name = "Replaced")]
+pub struct Original {
+    pub x: i32,
+}
+
+impl Original {
+    pub fn create() -> Self {
+        Self { x: 42 }
+    }
+}
diff --git a/cc_bindings_from_rs/test/attribute/cpp_name_test.cc b/cc_bindings_from_rs/test/attribute/cpp_name_test.cc
new file mode 100644
index 0000000..1b57618
--- /dev/null
+++ b/cc_bindings_from_rs/test/attribute/cpp_name_test.cc
@@ -0,0 +1,20 @@
+// Part of the Crubit project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include <type_traits>
+#include <utility>
+
+#include "gtest/gtest.h"
+#include "cc_bindings_from_rs/test/attribute/cpp_name_cc_api.h"
+
+namespace crubit {
+namespace {
+
+TEST(CppNameTest, RenameStruct) {
+  cpp_name::Replaced replaced = cpp_name::Replaced::create();
+  EXPECT_EQ(replaced.x, 42);
+}
+
+}  // namespace
+}  // namespace crubit
\ No newline at end of file
diff --git a/docs/rust/fine_tuning_bindings.md b/docs/rust/fine_tuning_bindings.md
index 36a072d..383e461 100644
--- a/docs/rust/fine_tuning_bindings.md
+++ b/docs/rust/fine_tuning_bindings.md
@@ -28,7 +28,7 @@
 std::int32_t Create(); // named `Create` instead of `new`.
 ```
 
-Currently this attribute works on functions only (See b/349070421).
+Currently this attribute works on functions and structs (See b/349070421).
 
 ## `cpp_type`