Add `CcPrerequisites::defs` field.

This CL adds the `CcPrerequisites::defs` field that tracks which C++
definitions need to appear before a given `CcSnippet`.  The field is
populated in `format_ty_for_cc` and covered by
`test_format_ty_for_cc...` and `test_format_def_fn...` tests, but is not
yet used to actually reorder the generated bindings - this will be done
in a follow-up CL.

PiperOrigin-RevId: 492477916
diff --git a/cc_bindings_from_rs/bindings.rs b/cc_bindings_from_rs/bindings.rs
index 2580fa4..2737c34 100644
--- a/cc_bindings_from_rs/bindings.rs
+++ b/cc_bindings_from_rs/bindings.rs
@@ -19,7 +19,7 @@
 use rustc_target::abi::Layout;
 use rustc_target::spec::abi::Abi;
 use rustc_target::spec::PanicStrategy;
-use std::collections::BTreeSet;
+use std::collections::{BTreeSet, HashSet};
 use std::iter::Sum;
 use std::ops::{Add, AddAssign};
 
@@ -110,21 +110,42 @@
     /// `CcSnippet::tokens` expands to `std::int32_t`, then `includes`
     /// need to cover the `#include <cstdint>`.
     includes: BTreeSet<CcInclude>,
-    // TODO(b/260268230): Cover `definitions` that need to appear before a `CcSnippet`.
+
+    /// Set of local definitions that a `CcSnippet` depends on.  For example if
+    /// `CcSnippet::tokens` expands to `void foo(S s)` then the definition
+    /// of `S` should have appeared earlier - in this case `defs` will
+    /// include the `LocalDefId` corresponding to `S`.
+    defs: HashSet<LocalDefId>,
+
+    /// Set of forward declarations that a `CcSnippet` depends on.  For example
+    /// if `CcSnippet::tokens` expands to `void foo(S* s)` then a forward
+    /// declaration of `S` should have appeared earlier - in this case
+    /// `fwd_decls` will include the `LocalDefId` corresponding to `S`.
+    /// Note that in this particular example the *definition* of `S` does
+    /// *not* need to appear earlier (and therefore `defs` will *not*
+    /// contain `LocalDefId` corresponding to `S`).
+    // TODO(b/260729464): Implement forward declarations support.
+    _fwd_decls: (),
 }
 
 impl CcPrerequisites {
     #[cfg(test)]
     fn is_empty(&self) -> bool {
-        self.includes.is_empty()
-        // TODO(b/260268230): Cover `definitions`.
+        self.includes.is_empty() && self.defs.is_empty()
     }
 }
 
 impl AddAssign for CcPrerequisites {
     fn add_assign(&mut self, mut rhs: Self) {
+        // `BTreeSet::append` is used because it _seems_ to be more efficient than
+        // calling `extend`.  This is because `extend` takes an iterator
+        // (processing each `rhs` include one-at-a-time) while `append` steals
+        // the whole backing data store from `rhs.includes`. OTOH, this is a bit
+        // speculative, since the (expected / guessed) performance difference is
+        // not documented at https://doc.rust-lang.org/std/collections/struct.BTreeSet.html#method.append
         self.includes.append(&mut rhs.includes);
-        // TODO(b/260268230): Cover `definitions`.
+
+        self.defs.extend(rhs.defs);
     }
 }
 
@@ -319,9 +340,9 @@
                 .with_context(|| format!(
                         "Failed to generate bindings for the definition of `{ty}`"))?;
 
-            let prereqs = if def_id.krate == LOCAL_CRATE {
-                // TODO(b/260268230): Add `def_id` to `prereqs.definitions`.
-                CcPrerequisites::default()
+            let mut prereqs = CcPrerequisites::default();
+            if def_id.krate == LOCAL_CRATE {
+                prereqs.defs.insert(def_id.expect_local());
             } else {
                 // TODO(b/258261328): Add `#include` of other crate's `..._cc_api.h`.
                 bail!("Cross-crate dependencies are not supported yet (b/258261328)");
@@ -332,13 +353,18 @@
                 prereqs
             }
         },
-        ty::TyKind::Foreign(..)
-        | ty::TyKind::Str
+        // TODO(b/260268230, b/260729464): When recursively processing nested types (e.g. an element type of an
+        // Array, a pointee type of a RawPtr, a referent of a Ref or Slice, a parameter type of an
+        // FnPtr, etc), one should also 1) propagate `CcPrerequisites::defs`, 2) cover
+        // `CcPrerequisites::defs` in `test_format_ty_for_cc...`.  For ptr/ref/slice it might be
+        // also desirable to separately track forward-declaration prerequisites.
         | ty::TyKind::Array(..)
         | ty::TyKind::Slice(..)
         | ty::TyKind::RawPtr(..)
         | ty::TyKind::Ref(..)
         | ty::TyKind::FnPtr(..)
+        | ty::TyKind::Str
+        | ty::TyKind::Foreign(..)
         | ty::TyKind::Dynamic(..)
         | ty::TyKind::Generator(..)
         | ty::TyKind::GeneratorWitness(..)
@@ -941,7 +967,8 @@
         // unique + ergonomic).
         let crate_name = format_cc_ident(tcx.crate_name(LOCAL_CRATE).as_str())?;
 
-        let CcSnippet { prereqs: CcPrerequisites { includes }, tokens } = cc;
+        // TODO(b/260268230): Use `CcPrerequisites::defs` to reorder the bindings.
+        let CcSnippet { prereqs: CcPrerequisites { includes, .. }, tokens } = cc;
         let includes = format_cc_includes(&includes);
         quote! {
             #includes __NEWLINE__
@@ -1461,6 +1488,34 @@
         });
     }
 
+    /// This test mainly verifies that `format_def` correctly propagates
+    /// `CcPrerequisites` of parameter types and return type.
+    #[test]
+    fn test_format_def_fn_with_cc_prerequisites() {
+        let test_src = r#"
+                pub struct S(i32);
+                pub fn foo(_i: i32) -> S { panic!("foo") }
+            "#;
+        test_format_def(test_src, "foo", |result| {
+            let result = result.unwrap();
+
+            // Minimal coverage, just to double-check that the test setup works.
+            assert_cc_matches!(result.cc.tokens, quote! { S foo(std::int32_t _i) { ... }});
+
+            // Main checks: `CcPrerequisites::includes`.
+            assert_cc_matches!(
+                format_cc_includes(&result.cc.prereqs.includes),
+                quote! { include <cstdint> }
+            );
+            // Main checks: `CcPrerequisites::defs`.
+            //
+            // Verifying the actual def_id is tricky, becayse `test_format_def` 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());
+        });
+    }
+
     #[test]
     fn test_format_def_fn_with_type_aliased_return_type() {
         // Type aliases disappear at the `rustc_middle::ty::Ty` level and therefore in
@@ -2436,26 +2491,26 @@
         // It seems desirable if the generated bindings conform to this aspect of the style guide,
         // because it makes things easier for *users* of these bindings.
         let testcases = [
-            // ( <Rust type>, (<expected C++ type>, <expected #include>) )
-            ("bool", ("bool", "")),
-            ("f32", ("float", "")),
-            ("f64", ("double", "")),
-            ("i8", ("std::int8_t", "cstdint")),
-            ("i16", ("std::int16_t", "cstdint")),
-            ("i32", ("std::int32_t", "cstdint")),
-            ("i64", ("std::int64_t", "cstdint")),
-            ("isize", ("std::intptr_t", "cstdint")),
-            ("u8", ("std::uint8_t", "cstdint")),
-            ("u16", ("std::uint16_t", "cstdint")),
-            ("u32", ("std::uint32_t", "cstdint")),
-            ("u64", ("std::uint64_t", "cstdint")),
-            ("usize", ("std::uintptr_t", "cstdint")),
-            ("char", ("std::uint32_t", "cstdint")),
-            ("SomeStruct", ("::rust_out::SomeStruct", "")),
-            ("SomeEnum", ("::rust_out::SomeEnum", "")),
-            ("SomeUnion", ("::rust_out::SomeUnion", "")),
+            // ( <Rust type>, (<expected C++ type>, <expected #include>, <expected prereq def>) )
+            ("bool", ("bool", "", "")),
+            ("f32", ("float", "", "")),
+            ("f64", ("double", "", "")),
+            ("i8", ("std::int8_t", "cstdint", "")),
+            ("i16", ("std::int16_t", "cstdint", "")),
+            ("i32", ("std::int32_t", "cstdint", "")),
+            ("i64", ("std::int64_t", "cstdint", "")),
+            ("isize", ("std::intptr_t", "cstdint", "")),
+            ("u8", ("std::uint8_t", "cstdint", "")),
+            ("u16", ("std::uint16_t", "cstdint", "")),
+            ("u32", ("std::uint32_t", "cstdint", "")),
+            ("u64", ("std::uint64_t", "cstdint", "")),
+            ("usize", ("std::uintptr_t", "cstdint", "")),
+            ("char", ("std::uint32_t", "cstdint", "")),
+            ("SomeStruct", ("::rust_out::SomeStruct", "", "SomeStruct")),
+            ("SomeEnum", ("::rust_out::SomeEnum", "", "SomeEnum")),
+            ("SomeUnion", ("::rust_out::SomeUnion", "", "SomeUnion")),
             // Extra parens/sugar are expected to be ignored:
-            ("(bool)", ("bool", "")),
+            ("(bool)", ("bool", "", "")),
         ];
         let preamble = quote! {
             #![allow(unused_parens)]
@@ -2473,14 +2528,17 @@
                 pub y: i32,
             }
         };
-        test_ty(&testcases, preamble, |desc, tcx, ty, (expected_snippet, expected_include)| {
-            let (actual_snippet, actual_includes) = {
-                let cc_snippet = format_ty_for_cc(tcx, ty).unwrap();
-                (cc_snippet.tokens.to_string(), cc_snippet.prereqs.includes)
+        test_ty(
+            &testcases,
+            preamble,
+            |desc, tcx, ty, (expected_tokens, expected_include, expected_prereq_def)| {
+                let (actual_tokens, actual_includes, actual_prereq_defs) = {
+                    let s = format_ty_for_cc(tcx, ty).unwrap();
+                    (s.tokens.to_string(), s.prereqs.includes, s.prereqs.defs)
             };
 
-            let expected_snippet = expected_snippet.parse::<TokenStream>().unwrap().to_string();
-            assert_eq!(actual_snippet, expected_snippet, "{desc}");
+                let expected_tokens = expected_tokens.parse::<TokenStream>().unwrap().to_string();
+                assert_eq!(actual_tokens, expected_tokens, "{desc}");
 
             if expected_include.is_empty() {
                 assert!(actual_includes.is_empty());
@@ -2491,7 +2549,16 @@
                     quote! { include <#expected_header> }
                 );
             }
-        });
+
+                if expected_prereq_def.is_empty() {
+                    assert!(actual_prereq_defs.is_empty());
+                } else {
+                    let expected_def_id = find_def_id_by_name(tcx, expected_prereq_def);
+                    assert_eq!(1, actual_prereq_defs.len());
+                    assert_eq!(expected_def_id, actual_prereq_defs.into_iter().next().unwrap());
+                }
+            },
+        );
     }
 
     #[test]
@@ -2672,10 +2739,10 @@
                 pub y: i32,
             }
         };
-        test_ty(&testcases, preamble, |desc, tcx, ty, expected_snippet| {
-            let actual_snippet = format_ty_for_rs(tcx, ty).unwrap().to_string();
-            let expected_snippet = expected_snippet.parse::<TokenStream>().unwrap().to_string();
-            assert_eq!(actual_snippet, expected_snippet, "{desc}");
+        test_ty(&testcases, preamble, |desc, tcx, ty, expected_tokens| {
+            let actual_tokens = format_ty_for_rs(tcx, ty).unwrap().to_string();
+            let expected_tokens = expected_tokens.parse::<TokenStream>().unwrap().to_string();
+            assert_eq!(actual_tokens, expected_tokens, "{desc}");
         });
     }
 
@@ -2743,14 +2810,14 @@
                 pub y: i32,
             }
         };
-        test_ty(&testcases, preamble, |desc, tcx, ty, (expected_snippet, expected_include)| {
-            let (actual_snippet, actual_includes) = {
+        test_ty(&testcases, preamble, |desc, tcx, ty, (expected_tokens, expected_include)| {
+            let (actual_tokens, actual_includes) = {
                 let cc_snippet = format_cc_thunk_arg(tcx, ty, quote! { value });
                 (cc_snippet.tokens.to_string(), cc_snippet.prereqs.includes)
             };
 
-            let expected_snippet = expected_snippet.parse::<TokenStream>().unwrap().to_string();
-            assert_eq!(actual_snippet, expected_snippet, "{desc}");
+            let expected_tokens = expected_tokens.parse::<TokenStream>().unwrap().to_string();
+            assert_eq!(actual_tokens, expected_tokens, "{desc}");
 
             if expected_include.is_empty() {
                 assert!(actual_includes.is_empty());