During function generation, always emit an `UnsupportedItem` instead of crashing.

This serves two goals:

1. Simplifies the API.
2. Prevents crashes, instead producing errors in the output.

I think it's an easy win, and that both of these are preferable outcomes, but LMK if I'm wrong.

PiperOrigin-RevId: 438532450
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index 3552ed6..f8192f0 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -219,19 +219,16 @@
     })
 }
 
-#[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`.
-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)?))
-    };
-
+///
+/// Return values:
+///
+///  * `Err(_)`: something went wrong importing this function.
+///  * `Ok(None)`: the function imported as "nothing". (For example, a defaulted
+///    destructor might be mapped to no `Drop` impl at all.)
+///  * `Ok((rs_api, rs_thunk, function_id))`: The Rust function definition,
+///    thunk FFI definition, and function ID.
+fn generate_func(func: &Func, ir: &IR) -> Result<Option<(RsSnippet, RsSnippet, FunctionId)>> {
     let lifetime_to_name = HashMap::<LifetimeId, String>::from_iter(
         func.lifetime_params.iter().map(|l| (l.id, l.name.clone())),
     );
@@ -375,20 +372,16 @@
                         );
                     }
                     _ => {
-                        return make_unsupported_result(
-                            "operator== where lhs doesn't refer to a record",
-                        );
+                        bail!("operator== where lhs doesn't refer to a record",);
                     }
                 },
                 _ => {
-                    return make_unsupported_result(
-                        "operator== where operands are not const references",
-                    );
+                    bail!("operator== where operands are not const references",);
                 }
             };
         }
         UnqualifiedIdentifier::Operator(_) => {
-            return make_unsupported_result("Bindings for this kind of operator are not supported");
+            bail!("Bindings for this kind of operator are not supported");
         }
         UnqualifiedIdentifier::Identifier(id) => {
             func_name = make_rs_ident(&id.identifier);
@@ -419,7 +412,7 @@
             let record =
                 maybe_record.ok_or_else(|| anyhow!("Destructors must be member functions."))?;
             if !should_implement_drop(record) {
-                return Ok(GeneratedFunc::None);
+                return Ok(None);
             }
             impl_kind = ImplKind::new_trait(
                 TraitName::Other(quote! {Drop}),
@@ -443,7 +436,7 @@
             if has_pointer_params {
                 // TODO(b/216648347): Allow this outside of traits (e.g. after supporting
                 // translating C++ constructors into static methods in Rust).
-                return make_unsupported_result(
+                bail!(
                     "Unsafe constructors (e.g. with no elided or explicit lifetimes) \
                     are intentionally not supported",
                 );
@@ -465,7 +458,7 @@
                     }
                     _ => {
                         // TODO(b/216648347): Support bindings for other constructors.
-                        return make_unsupported_result(
+                        bail!(
                             "Only single-parameter constructors for T: !Unpin are supported for now",
                         );
                     }
@@ -485,7 +478,7 @@
                         if param_type_kinds[1].is_shared_ref_to(record) {
                             // Copy constructor
                             if should_derive_clone(record) {
-                                return Ok(GeneratedFunc::None);
+                                return Ok(None);
                             } else {
                                 impl_kind = ImplKind::new_trait(
                                     TraitName::UnpinConstructor(quote! {Clone}),
@@ -503,16 +496,12 @@
                             );
                             func_name = make_rs_ident("from");
                         } else {
-                            return make_unsupported_result(
-                                "Not yet supported type of constructor parameter",
-                            );
+                            bail!("Not yet supported type of constructor parameter",);
                         }
                     }
                     _ => {
                         // TODO(b/216648347): Support bindings for other constructors.
-                        return make_unsupported_result(
-                            "More than 1 constructor parameter is not supported yet",
-                        );
+                        bail!("More than 1 constructor parameter is not supported yet",);
                     }
                 }
             }
@@ -760,11 +749,7 @@
         }
     };
 
-    Ok(GeneratedFunc::Some {
-        api_func: RsSnippet { features, tokens: api_func },
-        thunk: thunk.into(),
-        function_id,
-    })
+    Ok(Some((RsSnippet { features, tokens: api_func }, thunk.into(), function_id)))
 }
 
 fn generate_doc_comment(comment: &Option<String>) -> TokenStream {
@@ -1124,12 +1109,13 @@
     overloaded_funcs: &HashSet<FunctionId>,
 ) -> Result<GeneratedItem> {
     let generated_item = match item {
-        Item::Func(func) => match generate_func(func, ir)? {
-            GeneratedFunc::None => GeneratedItem { ..Default::default() },
-            GeneratedFunc::Unsupported(unsupported) => {
-                GeneratedItem { item: generate_unsupported(&unsupported)?, ..Default::default() }
-            }
-            GeneratedFunc::Some { api_func, thunk, function_id } => {
+        Item::Func(func) => match generate_func(func, ir) {
+            Err(e) => GeneratedItem {
+                item: generate_unsupported(&make_unsupported_fn(func, ir, format!("{e}"))?)?,
+                ..Default::default()
+            },
+            Ok(None) => GeneratedItem::default(),
+            Ok(Some((api_func, thunk, function_id))) => {
                 if overloaded_funcs.contains(&function_id) {
                     GeneratedItem {
                         item: generate_unsupported(&make_unsupported_fn(
@@ -1225,7 +1211,7 @@
     let mut seen_funcs = HashSet::new();
     let mut overloaded_funcs = HashSet::new();
     for func in ir.functions() {
-        if let GeneratedFunc::Some { function_id, .. } = generate_func(func, ir)? {
+        if let Ok(Some((.., function_id))) = generate_func(func, ir) {
             if !seen_funcs.insert(function_id.clone()) {
                 overloaded_funcs.insert(function_id);
             }
@@ -3674,11 +3660,12 @@
         // like this (note the mismatched 'a and 'b lifetimes):
         //     fn from<'b>(i: &'a i32) -> Self
         // After this CL, this scenario will result in an explicit error.
-        let err = generate_rs_api(&ir).unwrap_err();
-        let msg = format!("{}", err);
-        assert!(
-            msg.contains("The lifetime of `__this` is unexpectedly also used by another parameter")
-        );
+        let rs_api = generate_rs_api(&ir)?;
+        assert_rs_not_matches!(rs_api, quote! {impl From});
+        let rs_api_str = tokens_to_string(rs_api)?;
+        assert!(rs_api_str.contains(
+            "// The lifetime of `__this` is unexpectedly also used by another parameter"
+        ));
         Ok(())
     }