Explicit names for anonymous parameters: `fn foo(_: f64, _: f64)`.

In theory, the names could be omitted in *some* C++ translations (e.g.
`void foo(double, double)`), but they are required in other scenarios:

1. Forwarding an argument to a Rust function with a different name
   (e.g. `#[export_name = ...]` attribute and/or Rust-ABI functions).
   `return ::__crubit_internal::export_name(need_param_name_here)`.

2. Irrefutable, destructuring pattern matching - e.g.:
   `fn foo((x, y): (i32, i32)) {}`
   (this CL doesn't yet provide test coverage for this scenario,
   because types that support destructuring are not yet supported).

PiperOrigin-RevId: 486708734
diff --git a/common/code_gen_utils.rs b/common/code_gen_utils.rs
index 53d618a..e441062 100644
--- a/common/code_gen_utils.rs
+++ b/common/code_gen_utils.rs
@@ -17,6 +17,8 @@
 
 /// Formats a C++ identifier. Panics when `ident` is a C++ reserved keyword.
 pub fn format_cc_ident(ident: &str) -> Result<TokenStream> {
+    ensure!(!ident.is_empty(), "Empty string is not a valid C++ identifier");
+
     // C++ doesn't have an equivalent of
     // https://doc.rust-lang.org/rust-by-example/compatibility/raw_identifiers.html and therefore
     // an error is returned when `ident` is a C++ reserved keyword.
@@ -279,6 +281,13 @@
     }
 
     #[test]
+    fn test_format_cc_ident_empty() {
+        let err = format_cc_ident("").expect_err("This test expects an error");
+        let msg = err.to_string();
+        assert_eq!(msg, "Empty string is not a valid C++ identifier");
+    }
+
+    #[test]
     fn test_format_cc_ident_qualified_identifiers() {
         // https://en.cppreference.com/w/cpp/language/identifiers#Qualified_identifiers