Allow `generate_func` to emit comments for unsupported items / functions.

PiperOrigin-RevId: 426513920
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index 38f9e4f..a4972e2 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -88,6 +88,7 @@
 ///   tokens: quote!{vec![].into_raw_parts()},
 /// }
 /// ```
+#[derive(Clone, Debug)]
 struct RsSnippet {
     /// Rust feature flags used by this snippet.
     features: BTreeSet<Ident>,
@@ -148,7 +149,7 @@
 }
 
 /// Uniquely identifies a generated Rust function.
-#[derive(Clone, PartialEq, Eq, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
 struct FunctionId {
     // If the function is on a trait impl, contains the name of the Self type for
     // which the trait is being implemented.
@@ -174,7 +175,7 @@
             format!("~{}", record.expect("destructor must be associated with a record"))
         }
         UnqualifiedIdentifier::Constructor => {
-            format!("~{}", record.expect("constructor must be associated with a record"))
+            record.expect("constructor must be associated with a record").to_string()
         }
     };
 
@@ -185,14 +186,27 @@
     }
 }
 
+fn make_unsupported_fn(func: &Func, ir: &IR, message: impl ToString) -> Result<UnsupportedItem> {
+    Ok(UnsupportedItem {
+        name: cxx_function_name(func, ir)?,
+        message: message.to_string(),
+        source_loc: func.source_loc.clone(),
+    })
+}
+
+#[derive(Clone, Debug)]
+enum GeneratedFunc {
+    None, // No explicit function needed (e.g. when deriving Drop).
+    Unsupported(UnsupportedItem),
+    Some { api_func: RsSnippet, thunk: RsSnippet, function_id: FunctionId },
+}
+
 /// Generates Rust source code for a given `Func`.
-///
-/// Returns None if no code was generated for the function; otherwise, returns
-/// a tuple containing:
-/// - The generated function or trait impl
-/// - The thunk
-/// - A `FunctionId` identifying the generated Rust function
-fn generate_func(func: &Func, ir: &IR) -> Result<Option<(RsSnippet, RsSnippet, FunctionId)>> {
+fn generate_func(func: &Func, ir: &IR) -> Result<GeneratedFunc> {
+    let make_unsupported_result = |msg: &str| -> Result<GeneratedFunc> {
+        Ok(GeneratedFunc::Unsupported(make_unsupported_fn(func, ir, msg)?))
+    };
+
     let mangled_name = &func.mangled_name;
     let thunk_ident = thunk_ident(func);
     let doc_comment = generate_doc_comment(&func.doc_comment);
@@ -248,18 +262,13 @@
     let format_first_param_as_self: bool;
     match &func.name {
         UnqualifiedIdentifier::Identifier(id) if id.identifier == "operator==" => {
-            match (param_type_kinds.get(0), param_type_kinds.get(1)) {
+            if param_type_kinds.len() != 2 {
+                bail!("Unexpected number of parameters in operator==: {:?}", func);
+            }
+            match (&param_type_kinds[0], &param_type_kinds[1]) {
                 (
-                    Some(RsTypeKind::Reference {
-                        referent: lhs,
-                        mutability: Mutability::Const,
-                        ..
-                    }),
-                    Some(RsTypeKind::Reference {
-                        referent: rhs,
-                        mutability: Mutability::Const,
-                        ..
-                    }),
+                    RsTypeKind::Reference { referent: lhs, mutability: Mutability::Const, .. },
+                    RsTypeKind::Reference { referent: rhs, mutability: Mutability::Const, .. },
                 ) => match **lhs {
                     RsTypeKind::Record(lhs_record) => {
                         let lhs: Ident = make_rs_ident(&lhs_record.identifier.identifier);
@@ -271,13 +280,13 @@
                             record_name: lhs,
                         };
                     }
-                    _ => return Ok(None),
+                    _ => return make_unsupported_result("operator== where lhs doesn't refer to a record"),
                 },
-                _ => return Ok(None),
+                _ => return make_unsupported_result("operator== where operands are not const references"),
             };
         }
         UnqualifiedIdentifier::Identifier(id) if id.identifier.starts_with("operator") => {
-            return Ok(None);
+            return make_unsupported_result("Bindings for this kind of operator are not supported");
         }
         UnqualifiedIdentifier::Identifier(id) => {
             func_name = make_rs_ident(&id.identifier);
@@ -305,7 +314,7 @@
             let record =
                 maybe_record.ok_or_else(|| anyhow!("Destructors must be member functions."))?;
             if !should_implement_drop(record) {
-                return Ok(None);
+                return Ok(GeneratedFunc::None);
             }
             let record_name = maybe_record_name
                 .clone()
@@ -329,12 +338,17 @@
 
             if !record.is_unpin() {
                 // TODO: Handle <internal link>
-                return Ok(None);
+                return make_unsupported_result(
+                    "Bindings for constructors of non-trivial types are not supported yet",
+                );
             }
             if is_unsafe {
                 // TODO(b/216648347): Allow this outside of traits (e.g. after supporting
                 // translating C++ constructors into static methods in Rust).
-                return Ok(None);
+                return make_unsupported_result(
+                    "Unsafe constructors (e.g. with no elided or explicit lifetimes) \
+                    are intentionally not supported",
+                );
             }
 
             let record_name = maybe_record_name
@@ -352,7 +366,7 @@
                     if param_type_kinds[1].is_shared_ref_to(record) {
                         // Copy constructor
                         if should_derive_clone(record) {
-                            return Ok(None);
+                            return Ok(GeneratedFunc::None);
                         } else {
                             impl_kind = ImplKind::Trait { trait_name: quote! {Clone}, record_name };
                             func_name = make_rs_ident("clone");
@@ -367,12 +381,12 @@
                         func_name = make_rs_ident("from");
                         format_first_param_as_self = false;
                     } else {
-                        return Ok(None);
+                        return make_unsupported_result("Not yet supported type of constructor parameter");
                     }
                 }
                 _ => {
                     // TODO(b/216648347): Support bindings for other constructors.
-                    return Ok(None);
+                    return make_unsupported_result("More than 1 constructor parameter is not supported yet");
                 }
             }
         }
@@ -556,7 +570,7 @@
         }
     };
 
-    Ok(Some((api_func.into(), thunk.into(), function_id)))
+    Ok(GeneratedFunc::Some { api_func: api_func.into(), thunk: thunk.into(), function_id })
 }
 
 fn generate_doc_comment(comment: &Option<String>) -> TokenStream {
@@ -856,7 +870,7 @@
     let mut seen_funcs = HashSet::new();
     let mut overloaded_funcs = HashSet::new();
     for func in ir.functions() {
-        if let Some((_, _, function_id)) = generate_func(func, ir)? {
+        if let GeneratedFunc::Some { function_id, .. } = generate_func(func, ir)? {
             if !seen_funcs.insert(function_id.clone()) {
                 overloaded_funcs.insert(function_id);
             }
@@ -865,22 +879,26 @@
 
     for item in ir.items() {
         match item {
-            Item::Func(func) => {
-                if let Some((snippet, thunk, function_id)) = generate_func(func, ir)? {
+            Item::Func(func) => match generate_func(func, ir)? {
+                GeneratedFunc::None => (),
+                GeneratedFunc::Unsupported(unsupported) => {
+                    items.push(generate_unsupported(&unsupported)?)
+                }
+                GeneratedFunc::Some { api_func, thunk, function_id } => {
                     if overloaded_funcs.contains(&function_id) {
-                        items.push(generate_unsupported(&UnsupportedItem {
-                            name: cxx_function_name(func, ir)?,
-                            message: "Cannot generate bindings for overloaded function".to_string(),
-                            source_loc: func.source_loc.clone(),
-                        })?);
+                        items.push(generate_unsupported(&make_unsupported_fn(
+                            func,
+                            ir,
+                            "Cannot generate bindings for overloaded function",
+                        )?)?);
                         continue;
                     }
-                    features.extend(snippet.features);
+                    features.extend(api_func.features);
                     features.extend(thunk.features);
-                    items.push(snippet.tokens);
+                    items.push(api_func.tokens);
                     thunks.push(thunk.tokens);
                 }
-            }
+            },
             Item::Record(record) => {
                 if !ir.is_current_target(&record.owning_target)
                     && !ir.is_stdlib_target(&record.owning_target)