Return multiple snippets from `format_adt`.

This is the 2nd CL out of a series of 3 CLs that work toward returning
separate snippets from `format_fn` for A) declaration of the main API
function, and B) thunk declaration, function definition, and other
implementation details.  After the 3 CLs land, adding support for
static methods should be fairly straightforward.

This CL also has a side effect of moving C++ assertions to the end of
the generated header (grouping public APIs together, at the top of
the header).

PiperOrigin-RevId: 508774419
diff --git a/cc_bindings_from_rs/bindings.rs b/cc_bindings_from_rs/bindings.rs
index 4bf9ec8..fea0063 100644
--- a/cc_bindings_from_rs/bindings.rs
+++ b/cc_bindings_from_rs/bindings.rs
@@ -107,7 +107,7 @@
     Ok(Output { h_body, rs_body })
 }
 
-#[derive(Debug, Default)]
+#[derive(Clone, Debug, Default)]
 struct CcPrerequisites {
     /// Set of `#include`s that a `CcSnippet` depends on.  For example if
     /// `CcSnippet::tokens` expands to `std::int32_t`, then `includes`
@@ -513,11 +513,7 @@
     /// - A C++ declaration of an `extern "C"` thunk,
     /// - A Rust implementation of an `extern "C"` thunk,
     /// - C++ or Rust assertions about struct size and aligment.
-    ///
-    /// TODO(b/260725279): Split results of `format_fn` and `format_adt` into
-    /// multiple snippets - one `MainApi` snippet and 0-N `ImplDetails`
-    /// snippets,
-    _ImplDetails,
+    ImplDetails,
 }
 
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
@@ -537,12 +533,18 @@
 
 /// A C++ snippet (e.g. function declaration for `..._cc_api.h`) and a Rust
 /// snippet (e.g. a thunk definition for `..._cc_api_impl.rs`).
-#[derive(Debug)]
+#[derive(Debug, Default)]
 struct MixedSnippet {
     cc: CcSnippet,
     rs: TokenStream,
 }
 
+impl From<CcSnippet> for MixedSnippet {
+    fn from(cc: CcSnippet) -> Self {
+        Self { cc, rs: quote! {} }
+    }
+}
+
 /// Formats a function with the given `local_def_id`.
 ///
 /// Will panic if `local_def_id`
@@ -550,6 +552,9 @@
 /// - doesn't identify a function,
 /// - has generic parameters of any kind - lifetime parameters (see also
 ///   b/258235219), type parameters, or const parameters.
+///
+/// TODO(b/260725279): Split results of `format_fn` into MainApi and
+/// ImplDetails.
 fn format_fn(input: &Input, local_def_id: LocalDefId) -> Result<(SnippetKey, MixedSnippet)> {
     let tcx = input.tcx;
     let def_id: DefId = local_def_id.to_def_id(); // Convert LocalDefId to DefId.
@@ -887,7 +892,7 @@
 /// represented by `core`.  This function is infallible - after
 /// `format_adt_core` returns success we have committed to emitting C++ bindings
 /// for the ADT.
-fn format_adt(tcx: TyCtxt, core: &AdtCoreBindings) -> (SnippetKey, MixedSnippet) {
+fn format_adt(tcx: TyCtxt, core: &AdtCoreBindings) -> Vec<(SnippetKey, MixedSnippet)> {
     // `format_adt` should only be called for local ADTs.
     let local_def_id = core.def_id.expect_local();
 
@@ -913,10 +918,10 @@
 
     let alignment = Literal::u64_unsuffixed(core.alignment_in_bytes);
     let size = Literal::u64_unsuffixed(core.size_in_bytes);
-    let cc = {
+    let cc_name = &core.cc_name;
+    let main_api = {
         let doc_comment = format_doc_comment(tcx, core.def_id.expect_local());
         let keyword = &core.keyword;
-        let cc_name = &core.cc_name;
         let core = &core.core;
 
         let mut prereqs = CcPrerequisites::default();
@@ -942,24 +947,33 @@
                         // TODO(b/258233850): Emit individual fields.
                         unsigned char opaque_blob_of_bytes[#size];
                 };
-                static_assert(
-                    sizeof(#cc_name) == #size,
-                    "Verify that struct layout didn't change since this header got generated");
-                static_assert(
-                    alignof(#cc_name) == #alignment,
-                    "Verify that struct layout didn't change since this header got generated");
             },
         }
     };
-    let rs = {
-        let rs_name = &core.rs_name;
-        quote! {
-            const _: () = assert!(::std::mem::size_of::<#rs_name>() == #size);
-            const _: () = assert!(::std::mem::align_of::<#rs_name>() == #alignment);
-        }
+    let impl_details = {
+        let mut cc = CcSnippet::new(quote! {
+            static_assert(
+                sizeof(#cc_name) == #size,
+                "Verify that struct layout didn't change since this header got generated");
+            static_assert(
+                alignof(#cc_name) == #alignment,
+                "Verify that struct layout didn't change since this header got generated");
+        });
+        cc.prereqs.defs.insert(local_def_id);
+        let rs = {
+            let rs_name = &core.rs_name;
+            quote! {
+                const _: () = assert!(::std::mem::size_of::<#rs_name>() == #size);
+                const _: () = assert!(::std::mem::align_of::<#rs_name>() == #alignment);
+            }
+        };
+        MixedSnippet { cc, rs }
     };
 
-    (SnippetKey { def_id: local_def_id, kind: SnippetKind::MainApi }, MixedSnippet { cc, rs })
+    vec![
+        (SnippetKey { def_id: local_def_id, kind: SnippetKind::MainApi }, main_api.into()),
+        (SnippetKey { def_id: local_def_id, kind: SnippetKind::ImplDetails }, impl_details),
+    ]
 }
 
 /// Formats the forward declaration of an algebraic data type (an ADT - a
@@ -1018,14 +1032,14 @@
 /// can be ignored. Returns an `Err` if the definition couldn't be formatted.
 ///
 /// Will panic if `def_id` is invalid (i.e. doesn't identify a HIR item).
-fn format_item(input: &Input, def_id: LocalDefId) -> Result<Option<(SnippetKey, MixedSnippet)>> {
+fn format_item(input: &Input, def_id: LocalDefId) -> Result<Vec<(SnippetKey, MixedSnippet)>> {
     let tcx = input.tcx;
 
     // TODO(b/262052635): When adding support for re-exports we may need to change
     // `is_directly_public` below into `is_exported`.  (OTOH such change *alone* is
     // undesirable, because it would mean exposing items from a private module.)
     if !tcx.effective_visibilities(()).is_directly_public(def_id) {
-        return Ok(None);
+        return Ok(vec![]);
     }
 
     match input.tcx.hir().expect_item(def_id) {
@@ -1039,13 +1053,15 @@
             // required changes may cascade into `format_fn`'s usage of `no_bound_vars`.
             bail!("Generics are not supported yet (b/259749023 and b/259749095)");
         },
-        Item { kind: ItemKind::Fn(..), .. } => format_fn(input, def_id).map(Some),
+        Item { kind: ItemKind::Fn(..), .. } =>
+            format_fn(input, def_id)
+                .map(|snippet| vec![snippet]),
         Item { kind: ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..), .. } =>
             format_adt_core(tcx, def_id.to_def_id())
-                .map(|core| Some(format_adt(tcx, &core))),
+                .map(|core| format_adt(tcx, &core)),
         Item { kind: ItemKind::Impl(_), .. } |  // Handled by `format_adt`
         Item { kind: ItemKind::Mod(_), .. } =>  // Handled by `format_crate`
-            Ok(None),
+            Ok(vec![]),
         Item { kind, .. } => bail!("Unsupported rustc_hir::hir::ItemKind: {}", kind.descr()),
     }
 }
@@ -1065,10 +1081,7 @@
     let msg = format!("Error generating bindings for `{name}` defined at {source_loc}: {err:#}");
     let cc = CcSnippet::new(quote! { __NEWLINE__ __NEWLINE__ __COMMENT__ #msg __NEWLINE__ });
 
-    (
-        SnippetKey { def_id: local_def_id, kind: SnippetKind::MainApi },
-        MixedSnippet { cc, rs: quote! {} },
-    )
+    (SnippetKey { def_id: local_def_id, kind: SnippetKind::MainApi }, cc.into())
 }
 
 /// Formats all public items from the Rust crate being compiled.
@@ -1077,10 +1090,11 @@
     let mut bindings: HashMap<SnippetKey, MixedSnippet> = tcx
         .hir()
         .items()
-        .filter_map(|item_id| {
+        .flat_map(|item_id| {
             let def_id: LocalDefId = item_id.owner_id.def_id;
             format_item(input, def_id)
-                .unwrap_or_else(|err| Some(format_unsupported_def(tcx, def_id, err)))
+                .unwrap_or_else(|err| vec![format_unsupported_def(tcx, def_id, err)])
+                .into_iter()
         })
         .fold(HashMap::new(), |mut map, (key, value)| {
             let old_item = map.insert(key, value);
@@ -1179,10 +1193,7 @@
 
 #[cfg(test)]
 pub mod tests {
-    use super::{
-        format_cc_thunk_arg, format_item, format_ret_ty_for_cc, format_ty_for_cc, format_ty_for_rs,
-        generate_bindings, Input, MixedSnippet, Output,
-    };
+    use super::*;
 
     use anyhow::Result;
     use itertools::Itertools;
@@ -1415,28 +1426,28 @@
                             // `test_format_item_struct_with_fields`.
                             ...
                         };
-                        static_assert(sizeof(S) == ..., ...);
-                        static_assert(alignof(S) == ..., ...);
-
                         ...
-
                         namespace __crubit_internal {
                             extern "C" bool ...(::rust_out::S s);
                         }
                         ...
                         inline bool f(::rust_out::S s) { ... }
+                        ...
+                        static_assert(sizeof(S) == ..., ...);
+                        static_assert(alignof(S) == ..., ...);
+                        ...
                     }  // namespace rust_out
                 }
             );
             assert_rs_matches!(
                 bindings.rs_body,
                 quote! {
-                    const _: () = assert!(::std::mem::size_of::<::rust_out::S>() == ...);
-                    const _: () = assert!(::std::mem::align_of::<::rust_out::S>() == ...);
-                    ...
                     #[no_mangle]
                     extern "C"
                     fn ...(s: ::rust_out::S) -> bool { ...  }
+                    ...
+                    const _: () = assert!(::std::mem::size_of::<::rust_out::S>() == ...);
+                    const _: () = assert!(::std::mem::align_of::<::rust_out::S>() == ...);
                 }
             );
         });
@@ -1821,11 +1832,11 @@
                 pub extern "C" fn public_function() {}
             "#;
         test_format_item(test_src, "public_function", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
-            assert!(result.rs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
+            assert!(main_api.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     extern "C" void public_function();
                 }
@@ -1849,11 +1860,11 @@
                 pub extern "C" fn explicit_unit_return_type() -> () {}
             "#;
         test_format_item(test_src, "explicit_unit_return_type", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
-            assert!(result.rs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
+            assert!(main_api.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     extern "C" void explicit_unit_return_type();
                 }
@@ -1874,11 +1885,11 @@
             // attribute.
             // TODO(b/254507801): Expect `crubit::Never` instead (see the bug for more
             // details).
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
-            assert!(result.rs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
+            assert!(main_api.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     extern "C" void never_returning_function();
                 }
@@ -1897,11 +1908,12 @@
                 pub extern "C" fn public_function(x: f64, y: f64) -> f64 { x + y }
             "#;
         test_format_item(test_src, "public_function", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
-            assert!(result.rs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_mixed_snippet(&result);
+            assert!(main_api.cc.prereqs.is_empty());
+            assert!(main_api.rs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.cc.tokens,
                 quote! {
                     namespace __crubit_internal {
                         extern "C" double ...(double x, double y);
@@ -1922,11 +1934,12 @@
                 pub extern "C" fn public_function(x: f64, y: f64) -> f64 { x + y }
             "#;
         test_format_item(test_src, "public_function", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
-            assert!(result.rs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_mixed_snippet(&result);
+            assert!(main_api.cc.prereqs.is_empty());
+            assert!(main_api.rs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.cc.tokens,
                 quote! {
                     namespace __crubit_internal {
                         extern "C" double export_name(double x, double y);
@@ -1970,10 +1983,11 @@
         test_format_item(test_src, "foo", |result| {
             // TODO(b/254095787): Update test expectations below once `const fn` from Rust
             // is translated into a `consteval` C++ function.
-            let result = result.unwrap().unwrap();
-            assert!(!result.cc.prereqs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_mixed_snippet(&result);
+            assert!(!main_api.cc.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.cc.tokens,
                 quote! {
                     namespace __crubit_internal {
                         extern "C" std::int32_t ...( std::int32_t i);
@@ -1985,7 +1999,7 @@
                 }
             );
             assert_rs_matches!(
-                result.rs,
+                main_api.rs,
                 quote! {
                     #[no_mangle]
                     extern "C"
@@ -2007,11 +2021,11 @@
                 pub extern "C-unwind" fn may_throw() {}
             "#;
         test_format_item(test_src, "may_throw", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
-            assert!(result.rs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
+            assert!(main_api.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     extern "C" void may_throw();
                 }
@@ -2028,17 +2042,18 @@
                 pub struct S(i32);
             "#;
         test_format_item(test_src, "foo", |result| {
-            let result = result.unwrap().unwrap();
+            let result = result.unwrap();
+            let main_api = get_main_api_mixed_snippet(&result);
 
             // Minimal coverage, just to double-check that the test setup works.
             //
             // Note that this is a definition, and therefore `S` should be defined
             // earlier (not just forward declared).
-            assert_cc_matches!(result.cc.tokens, quote! { S foo(std::int32_t _i) { ... }});
+            assert_cc_matches!(main_api.cc.tokens, quote! { S foo(std::int32_t _i) { ... }});
 
             // Main checks: `CcPrerequisites::includes`.
             assert_cc_matches!(
-                format_cc_includes(&result.cc.prereqs.includes),
+                format_cc_includes(&main_api.cc.prereqs.includes),
                 quote! { include <cstdint> }
             );
 
@@ -2047,8 +2062,8 @@
             // Verifying the actual def_id is tricky, because `test_format_item` doesn't
             // expose `tcx` to the verification function (and therefore calling
             // `find_def_id_by_name` is not easily possible).
-            assert_eq!(1, result.cc.prereqs.defs.len());
-            assert_eq!(0, result.cc.prereqs.fwd_decls.len());
+            assert_eq!(1, main_api.cc.prereqs.defs.len());
+            assert_eq!(0, main_api.cc.prereqs.fwd_decls.len());
         });
     }
 
@@ -2070,22 +2085,23 @@
                 pub struct S(bool);
             "#;
         test_format_item(test_src, "foo", |result| {
-            let result = result.unwrap().unwrap();
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
 
             // Minimal coverage, just to double-check that the test setup works.
             //
             // Note that this is only a function *declaration* (not a function definition -
             // there is no function body), and therefore `S` just needs to be
             // forward-declared earlier.
-            assert_cc_matches!(result.cc.tokens, quote! { extern "C" bool foo(::rust_out::S s); });
+            assert_cc_matches!(main_api.tokens, quote! { extern "C" bool foo(::rust_out::S s); });
 
             // Main checks: `CcPrerequisites::defs` and `CcPrerequisites::fwd_decls`.
             //
             // Verifying the actual def_id is tricky, because `test_format_item` doesn't
             // expose `tcx` to the verification function (and therefore calling
             // `find_def_id_by_name` is not easily possible).
-            assert_eq!(0, result.cc.prereqs.defs.len());
-            assert_eq!(1, result.cc.prereqs.fwd_decls.len());
+            assert_eq!(0, main_api.prereqs.defs.len());
+            assert_eq!(1, main_api.prereqs.fwd_decls.len());
         });
     }
 
@@ -2103,11 +2119,11 @@
                 pub extern "C" fn type_aliased_return() -> MyTypeAlias { 42.0 }
             "#;
         test_format_item(test_src, "type_aliased_return", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
-            assert!(result.rs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
+            assert!(main_api.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     extern "C" double type_aliased_return();
                 }
@@ -2126,9 +2142,9 @@
             pub extern "C" fn fn_with_doc_comment_with_unmangled_name() {}
           "#;
         test_format_item(test_src, "fn_with_doc_comment_with_unmangled_name", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
-            assert!(result.rs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
+            assert!(main_api.prereqs.is_empty());
             let doc_comments = [
                 " Outer line doc.",
                 "",
@@ -2141,7 +2157,7 @@
             ]
             .join("\n");
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     __COMMENT__ #doc_comments
                     extern "C" void fn_with_doc_comment_with_unmangled_name();
@@ -2160,9 +2176,9 @@
             }
           "#;
         test_format_item(test_src, "fn_with_inner_doc_comment_with_unmangled_name", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
-            assert!(result.rs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
+            assert!(main_api.prereqs.is_empty());
             let doc_comments = [
                 " Outer doc comment.",
                 " Inner doc comment.",
@@ -2170,7 +2186,7 @@
             ]
             .join("\n\n");
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     __COMMENT__ #doc_comments
                     extern "C" void fn_with_inner_doc_comment_with_unmangled_name();
@@ -2186,13 +2202,14 @@
                 pub extern "C" fn fn_with_doc_comment_with_mangled_name() {}
             "#;
         test_format_item(test_src, "fn_with_doc_comment_with_mangled_name", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
-            assert!(result.rs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_mixed_snippet(&result);
+            assert!(main_api.cc.prereqs.is_empty());
+            assert!(main_api.rs.is_empty());
             let comment = " Doc comment of a function with mangled name.\n\n\
                            Generated from: <crubit_unittests.rs>;l=3";
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.cc.tokens,
                 quote! {
                     namespace __crubit_internal {
                         extern "C" void ...();
@@ -2335,10 +2352,11 @@
             // TODO(b/261074843): Re-add thunk name verification once we are using stable name
             // mangling (which may be coming in Q1 2023).  (This might mean reverting cl/492333432
             // + manual review and tweaks.)
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_mixed_snippet(&result);
+            assert!(main_api.cc.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.cc.tokens,
                 quote! {
                     namespace __crubit_internal {
                         extern "C" double ...(double x, double y);
@@ -2350,7 +2368,7 @@
                 }
             );
             assert_rs_matches!(
-                result.rs,
+                main_api.rs,
                 quote! {
                     #[no_mangle]
                     extern "C"
@@ -2384,10 +2402,11 @@
                 pub extern "vectorcall" fn add(x: f64, y: f64) -> f64 { x * y }
             "#;
         test_format_item(test_src, "add", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_mixed_snippet(&result);
+            assert!(main_api.cc.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.cc.tokens,
                 quote! {
                     namespace __crubit_internal {
                         extern "C" double ...(double x, double y);
@@ -2399,7 +2418,7 @@
                 }
             );
             assert_rs_matches!(
-                result.rs,
+                main_api.rs,
                 quote! {
                     #[no_mangle]
                     extern "C"
@@ -2434,11 +2453,11 @@
                 pub extern "C" fn foo(b: bool, f: f64) {}
             "#;
         test_format_item(test_src, "foo", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
-            assert!(result.rs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
+            assert!(main_api.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     ...
                     extern "C" void foo(bool b, double f);
@@ -2455,11 +2474,11 @@
                 pub extern "C" fn some_function(reinterpret_cast: f64) {}
             "#;
         test_format_item(test_src, "some_function", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
-            assert!(result.rs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
+            assert!(main_api.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     ...
                     extern "C" void some_function(double __param_0);
@@ -2474,10 +2493,11 @@
                 pub fn foo(_: f64, _: f64) {}
             "#;
         test_format_item(test_src, "foo", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_mixed_snippet(&result);
+            assert!(main_api.cc.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.cc.tokens,
                 quote! {
                     namespace __crubit_internal {
                         extern "C" void ...(
@@ -2490,7 +2510,7 @@
                 }
             );
             assert_rs_matches!(
-                result.rs,
+                main_api.rs,
                 quote! {
                     #[no_mangle]
                     extern "C" fn ...(__param_0: f64, __param_1: f64) -> () {
@@ -2516,9 +2536,10 @@
                 pub fn func(S{f1, f2}: S) -> i32 { f1 + f2 }
             "#;
         test_format_item(test_src, "func", |result| {
-            let result = result.unwrap().unwrap();
+            let result = result.unwrap();
+            let main_api = get_main_api_mixed_snippet(&result);
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.cc.tokens,
                 quote! {
                     namespace __crubit_internal {
                         extern "C" std::int32_t ...(::rust_out::S __param_0);
@@ -2530,7 +2551,7 @@
                 }
             );
             assert_rs_matches!(
-                result.rs,
+                main_api.rs,
                 quote! {
                     #[no_mangle]
                     extern "C" fn ...(__param_0: ::rust_out::S) -> i32 {
@@ -2599,10 +2620,12 @@
                 const _: () = assert!(std::mem::align_of::<SomeStruct>() == 4);
             "#;
         test_format_item(test_src, "SomeStruct", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
+            let impl_details = get_impl_details_snippet(&result);
+            assert!(main_api.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     ...
                     struct alignas(4) SomeStruct final {
@@ -2626,12 +2649,17 @@
                         private:
                             unsigned char opaque_blob_of_bytes[8];
                     };
+                }
+            );
+            assert_cc_matches!(
+                impl_details.cc.tokens,
+                quote! {
                     static_assert(sizeof(SomeStruct) == 8, ...);
                     static_assert(alignof(SomeStruct) == 4, ...);
                 }
             );
             assert_rs_matches!(
-                result.rs,
+                impl_details.rs,
                 quote! {
                     const _: () = assert!(::std::mem::size_of::<::rust_out::SomeStruct>() == 8);
                     const _: () = assert!(::std::mem::align_of::<::rust_out::SomeStruct>() == 4);
@@ -2650,10 +2678,12 @@
                 const _: () = assert!(std::mem::align_of::<TupleStruct>() == 4);
             "#;
         test_format_item(test_src, "TupleStruct", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
+            let impl_details = get_impl_details_snippet(&result);
+            assert!(main_api.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     ...
                     struct alignas(4) TupleStruct final {
@@ -2677,12 +2707,17 @@
                         private:
                             unsigned char opaque_blob_of_bytes[8];
                     };
+                }
+            );
+            assert_cc_matches!(
+                impl_details.cc.tokens,
+                quote! {
                     static_assert(sizeof(TupleStruct) == 8, ...);
                     static_assert(alignof(TupleStruct) == 4, ...);
                 }
             );
             assert_rs_matches!(
-                result.rs,
+                impl_details.rs,
                 quote! {
                     const _: () = assert!(::std::mem::size_of::<::rust_out::TupleStruct>() == 8);
                     const _: () = assert!(::std::mem::align_of::<::rust_out::TupleStruct>() == 4);
@@ -2787,10 +2822,12 @@
                 const _: () = assert!(std::mem::align_of::<SomeEnum>() == 1);
             "#;
         test_format_item(test_src, "SomeEnum", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
+            let impl_details = get_impl_details_snippet(&result);
+            assert!(main_api.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     ...
                     struct alignas(1) SomeEnum final {
@@ -2814,12 +2851,17 @@
                         private:
                             unsigned char opaque_blob_of_bytes[1];
                     };
+                }
+            );
+            assert_cc_matches!(
+                impl_details.cc.tokens,
+                quote! {
                     static_assert(sizeof(SomeEnum) == 1, ...);
                     static_assert(alignof(SomeEnum) == 1, ...);
                 }
             );
             assert_rs_matches!(
-                result.rs,
+                impl_details.rs,
                 quote! {
                     const _: () = assert!(::std::mem::size_of::<::rust_out::SomeEnum>() == 1);
                     const _: () = assert!(::std::mem::align_of::<::rust_out::SomeEnum>() == 1);
@@ -2842,10 +2884,12 @@
                 const _: () = assert!(std::mem::align_of::<Point>() == 4);
             "#;
         test_format_item(test_src, "Point", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
+            let impl_details = get_impl_details_snippet(&result);
+            assert!(main_api.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     ...
                     struct alignas(4) Point final {
@@ -2869,12 +2913,17 @@
                         private:
                             unsigned char opaque_blob_of_bytes[12];
                     };
+                }
+            );
+            assert_cc_matches!(
+                impl_details.cc.tokens,
+                quote! {
                     static_assert(sizeof(Point) == 12, ...);
                     static_assert(alignof(Point) == 4, ...);
                 }
             );
             assert_rs_matches!(
-                result.rs,
+                impl_details.rs,
                 quote! {
                     const _: () = assert!(::std::mem::size_of::<::rust_out::Point>() == 12);
                     const _: () = assert!(::std::mem::align_of::<::rust_out::Point>() == 4);
@@ -2910,10 +2959,12 @@
                 const _: () = assert!(std::mem::align_of::<SomeUnion>() == 8);
             "#;
         test_format_item(test_src, "SomeUnion", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
+            let impl_details = get_impl_details_snippet(&result);
+            assert!(main_api.prereqs.is_empty());
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     ...
                     struct alignas(8) SomeUnion final {
@@ -2937,12 +2988,17 @@
                         private:
                             unsigned char opaque_blob_of_bytes[8];
                     };
+                }
+            );
+            assert_cc_matches!(
+                impl_details.cc.tokens,
+                quote! {
                     static_assert(sizeof(SomeUnion) == 8, ...);
                     static_assert(alignof(SomeUnion) == 8, ...);
                 }
             );
             assert_rs_matches!(
-                result.rs,
+                impl_details.rs,
                 quote! {
                     const _: () = assert!(::std::mem::size_of::<::rust_out::SomeUnion>() == 8);
                     const _: () = assert!(::std::mem::align_of::<::rust_out::SomeUnion>() == 8);
@@ -2962,11 +3018,12 @@
             }
         "#;
         test_format_item(test_src, "SomeUnionWithDocs", |result| {
-            let result = result.unwrap().unwrap();
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
             let comment = " Doc for some union.\n\n\
                            Generated from: <crubit_unittests.rs>;l=3";
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     __COMMENT__ #comment
                     struct ... SomeUnionWithDocs final {
@@ -2987,11 +3044,12 @@
             }
         "#;
         test_format_item(test_src, "SomeEnumWithDocs", |result| {
-            let result = result.unwrap().unwrap();
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
             let comment = " Doc for some enum. \n\n\
                             Generated from: <crubit_unittests.rs>;l=3";
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     __COMMENT__ #comment
                     struct ... SomeEnumWithDocs final {
@@ -3013,11 +3071,12 @@
             }
         "#;
         test_format_item(test_src, "SomeStructWithDocs", |result| {
-            let result = result.unwrap().unwrap();
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
             let comment = "Doc for some struct.\n\n\
                            Generated from: <crubit_unittests.rs>;l=4";
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     __COMMENT__ #comment
                     struct ... SomeStructWithDocs final {
@@ -3036,11 +3095,12 @@
             pub struct SomeTupleStructWithDocs(i32);
         "#;
         test_format_item(test_src, "SomeTupleStructWithDocs", |result| {
-            let result = result.unwrap().unwrap();
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
             let comment = " Doc for some tuple struct.\n\n\
                            Generated from: <crubit_unittests.rs>;l=3";
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     __COMMENT__ #comment
                     struct ... SomeTupleStructWithDocs final {
@@ -3065,11 +3125,12 @@
             some_tuple_struct_macro_for_testing_source_loc!();
         "#;
         test_format_item(test_src, "SomeTupleStructMacroForTesingSourceLoc", |result| {
-            let result = result.unwrap().unwrap();
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
             let source_loc_comment = " Some doc on SomeTupleStructMacroForTesingSourceLoc.\n\n\
                                       Generated from: <crubit_unittests.rs>;l=5";
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     __COMMENT__ #source_loc_comment
                     struct ... SomeTupleStructMacroForTesingSourceLoc final {
@@ -3087,10 +3148,11 @@
             pub struct SomeTupleStructWithNoDocComment(i32);
         "#;
         test_format_item(test_src, "SomeTupleStructWithNoDocComment", |result| {
-            let result = result.unwrap().unwrap();
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
             let comment = "Generated from: <crubit_unittests.rs>;l=2";
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     __COMMENT__ #comment
                     struct ... SomeTupleStructWithNoDocComment final {
@@ -3147,13 +3209,14 @@
                 }
             "#;
         test_format_item(test_src, "SomeStruct", |result| {
-            let result = result.unwrap().unwrap();
-            assert!(result.cc.prereqs.is_empty());
+            let result = result.unwrap();
+            let main_api = get_main_api_snippet(&result);
+            assert!(main_api.prereqs.is_empty());
             let unsupported_msg = "Error generating bindings for `SomeStruct::CONST_VALUE` \
                                    defined at <crubit_unittests.rs>;l=5: \
                                    `impl` items are not supported yet";
             assert_cc_matches!(
-                result.cc.tokens,
+                main_api.tokens,
                 quote! {
                     ...
                     struct alignas(4) SomeStruct final {
@@ -3599,6 +3662,46 @@
         }
     }
 
+    // TODO(b/260725279): Remove this test helper (and use `get_main_api_snippet`)
+    // after `format_fn` gets refactored and returns separate MainApi (only C++)
+    // and ImplDetails (mix of C++ and Rust).
+    fn get_main_api_mixed_snippet(
+        format_item_result: &Vec<(SnippetKey, MixedSnippet)>,
+    ) -> &MixedSnippet {
+        format_item_result
+            .iter()
+            .filter_map(|(key, snippet)| match key.kind {
+                SnippetKind::MainApi => Some(snippet),
+                _ => None,
+            })
+            .exactly_one()
+            .expect("Expecting exactly 1 MainApi snippet")
+    }
+
+    fn get_main_api_snippet(format_item_result: &Vec<(SnippetKey, MixedSnippet)>) -> &CcSnippet {
+        let snippet = get_main_api_mixed_snippet(format_item_result);
+        assert!(snippet.rs.is_empty(), "MainApi should be C++-only");
+        &snippet.cc
+    }
+
+    /// Combines all ImplDetails snippets into a single MixedSnippet.
+    fn get_impl_details_snippet(
+        format_item_result: &Vec<(SnippetKey, MixedSnippet)>,
+    ) -> MixedSnippet {
+        format_item_result
+            .iter()
+            .filter_map(|(key, snippet)| match key.kind {
+                SnippetKind::ImplDetails => Some(snippet),
+                _ => None,
+            })
+            .fold(Default::default(), |mut acc, snippet| {
+                acc.cc.prereqs += snippet.cc.prereqs.clone();
+                acc.cc.tokens.extend(snippet.cc.tokens.clone());
+                acc.rs.extend(snippet.rs.clone());
+                acc
+            })
+    }
+
     /// Tests invoking `format_item` on the item with the specified `name` from
     /// the given Rust `source`.  Returns the result of calling
     /// `test_function` with `format_item`'s result as an argument.
@@ -3606,20 +3709,27 @@
     /// result from `format_item`.)
     fn test_format_item<F, T>(source: &str, name: &str, test_function: F) -> T
     where
-        F: FnOnce(Result<Option<MixedSnippet>, String>) -> T + Send,
+        F: FnOnce(Result<Vec<(SnippetKey, MixedSnippet)>, String>) -> T + Send,
         T: Send,
     {
         run_compiler_for_testing(source, |tcx| {
             let def_id = find_def_id_by_name(tcx, name);
             let result = format_item(&bindings_input_for_tests(tcx), def_id);
 
+            // Sort the vector of results to make the tests more deterministic.  Below (i.e. in
+            // tests) we use a somewhat arbitrary SnippetKey-based order.  The order of these
+            // intermediate results doesn't matter in prod, because they will later get toposorted
+            // based on CcPrerequisites.
+            let result = result.map(|mut vec| {
+                let cmp = preferred_snippet_order(tcx);
+                vec.sort_by(|(key1, _), (key2, _)| cmp(key1, key2));
+                vec
+            });
+
             // https://docs.rs/anyhow/latest/anyhow/struct.Error.html#display-representations says:
             // To print causes as well [...], use the alternate selector “{:#}”.
             let result = result.map_err(|anyhow_err| format!("{anyhow_err:#}"));
 
-            // Ignore SnippetKey for now.
-            let result = result.map(|opt| opt.map(|(_key, snippet)| snippet));
-
             test_function(result)
         })
     }
diff --git a/common/code_gen_utils.rs b/common/code_gen_utils.rs
index e3b895b..7f05505 100644
--- a/common/code_gen_utils.rs
+++ b/common/code_gen_utils.rs
@@ -155,7 +155,7 @@
 }
 
 /// `CcInclude` represents a single `#include ...` directive in C++.
-#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
 pub enum CcInclude {
     SystemHeader(&'static str),
     UserHeader(Rc<str>),