Make items that stem from decl context have a list of children item ids and generate code recursively

After this cl:
  * We use a new method `Importer::GetItemIdsInSourceOrder()` to obtain a list of their children items (including comments and unsupported items, excluding children `decl`s whose `ImportDecl()` returned `std::nullopt`, as they don't have a corresponding item.)
  * We generate source for items recursively, starting from `flat_ir.top_level_item_ids`.
    * This required some changes to `generate_record()`, as it used to not be responsible for generating (unsupported) items for its own children, and now it is.
  * Comments: We store all the comments, so we can find the relevant comments for each decl context.
    * We now generate free comments that appear within a `Record`; however, comments between fields now appear out of place, after the struct definition. I will address this in a followup cl.

PiperOrigin-RevId: 440906345
diff --git a/rs_bindings_from_cc/importer_test.cc b/rs_bindings_from_cc/importer_test.cc
index 9541618..808f513 100644
--- a/rs_bindings_from_cc/importer_test.cc
+++ b/rs_bindings_from_cc/importer_test.cc
@@ -24,12 +24,14 @@
 
 using ::testing::AllOf;
 using ::testing::AnyOf;
+using ::testing::Contains;
 using ::testing::Each;
 using ::testing::ElementsAre;
 using ::testing::IsEmpty;
 using ::testing::Not;
 using ::testing::Pointee;
 using ::testing::SizeIs;
+using ::testing::UnorderedElementsAre;
 using ::testing::VariantWith;
 using ::testing::status::StatusIs;
 
@@ -42,6 +44,23 @@
   return std::nullopt;
 }
 
+std::optional<IR::Item> FindItemById(const IR& ir, ItemId id) {
+  for (auto item : ir.items) {
+    if (auto* record = std::get_if<Record>(&item); record && record->id == id) {
+      return item;
+    } else if (auto* func = std::get_if<Func>(&item); func && func->id == id) {
+      return item;
+    } else if (auto* comment = std::get_if<Comment>(&item);
+               comment && comment->id == id) {
+      return item;
+    } else if (auto* unsupported = std::get_if<UnsupportedItem>(&item);
+               unsupported && unsupported->id == id) {
+      return item;
+    }
+  }
+  return std::nullopt;
+}
+
 template <typename T>
 UnqualifiedIdentifier GetName(const T& x) {
   return x.identifier;
@@ -111,6 +130,14 @@
   return false;
 }
 
+// Matches text for comments.
+MATCHER_P(TextIs, text, "") {
+  if (arg.text == text) return true;
+
+  *result_listener << "actual text: '" << arg.text << "'";
+  return false;
+}
+
 // Matches an RsType or CcType that has the given decl_id.
 MATCHER_P(DeclIdIs, decl_id, "") {
   if (arg.decl_id.hasValue() && *arg.decl_id == decl_id) return true;
@@ -301,7 +328,7 @@
 TEST(ImporterTest, FuncWithVoidReturnType) {
   ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc("void Foo();"));
   EXPECT_THAT(ItemsWithoutBuiltins(ir),
-              ElementsAre(VariantWith<Func>(
+              UnorderedElementsAre(VariantWith<Func>(
                   AllOf(IdentifierIs("Foo"), MangledNameIs("_Z3Foov"),
                         ReturnType(IsVoid()), ParamsAre()))));
 }
@@ -310,7 +337,7 @@
   ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc("void Foo(); void Bar();"));
   EXPECT_THAT(
       ItemsWithoutBuiltins(ir),
-      ElementsAre(
+      UnorderedElementsAre(
           VariantWith<Func>(AllOf(IdentifierIs("Foo"), MangledNameIs("_Z3Foov"),
                                   ReturnType(IsVoid()), ParamsAre())),
           VariantWith<Func>(AllOf(IdentifierIs("Bar"), MangledNameIs("_Z3Barv"),
@@ -331,35 +358,36 @@
                            BazelLabel{"//two_funcs:one_target"}},
                       }));
   EXPECT_THAT(ItemsWithoutBuiltins(ir),
-              ElementsAre(VariantWith<Func>(IdentifierIs("Foo")),
-                          VariantWith<Func>(IdentifierIs("Bar"))));
+              UnorderedElementsAre(VariantWith<Func>(IdentifierIs("Foo")),
+                                   VariantWith<Func>(IdentifierIs("Bar"))));
 }
 
 TEST(ImporterTest, NonInlineFunc) {
   ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc("void Foo() {}"));
   EXPECT_THAT(ItemsWithoutBuiltins(ir),
-              ElementsAre(VariantWith<Func>(
+              UnorderedElementsAre(VariantWith<Func>(
                   AllOf(IdentifierIs("Foo"), Not(IsInline())))));
 }
 
 TEST(ImporterTest, InlineFunc) {
   ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc("inline void Foo() {}"));
-  EXPECT_THAT(
-      ItemsWithoutBuiltins(ir),
-      ElementsAre(VariantWith<Func>(AllOf(IdentifierIs("Foo"), IsInline()))));
+  EXPECT_THAT(ItemsWithoutBuiltins(ir),
+              UnorderedElementsAre(
+                  VariantWith<Func>(AllOf(IdentifierIs("Foo"), IsInline()))));
 }
 
 TEST(ImporterTest, FuncJustOnce) {
   ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc("void Foo(); void Foo();"));
-  EXPECT_THAT(ItemsWithoutBuiltins(ir),
-              ElementsAre(VariantWith<Func>(AllOf(IdentifierIs("Foo")))));
+  EXPECT_THAT(
+      ItemsWithoutBuiltins(ir),
+      UnorderedElementsAre(VariantWith<Func>(AllOf(IdentifierIs("Foo")))));
 }
 
 TEST(ImporterTest, TestImportPointerFunc) {
   ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc("int* Foo(int* a);"));
 
   EXPECT_THAT(ItemsWithoutBuiltins(ir),
-              ElementsAre(VariantWith<Func>(AllOf(
+              UnorderedElementsAre(VariantWith<Func>(AllOf(
                   ReturnType(IsIntPtr()), ParamsAre(ParamType(IsIntPtr()))))));
 }
 
@@ -383,7 +411,7 @@
   ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc("int& Foo(int& a);"));
 
   EXPECT_THAT(ItemsWithoutBuiltins(ir),
-              ElementsAre(VariantWith<Func>(AllOf(
+              UnorderedElementsAre(VariantWith<Func>(AllOf(
                   ReturnType(IsIntRef()), ParamsAre(ParamType(IsIntRef()))))));
 }
 
@@ -776,5 +804,73 @@
   EXPECT_THAT(records, Each(Pointee(Not(IsTrivialAbi()))));
 }
 
+TEST(ImporterTest, TopLevelItemIds) {
+  absl::string_view file = R"cc(
+    struct TopLevelStruct {};
+    // Top level comment
+
+    // Function comment
+    void top_level_func();
+    namespace top_level_namespace {
+    struct Nested {};
+    // free nested comment
+
+    // nested_func comment
+    void nested_func();
+    }  // namespace top_level_namespace
+  )cc";
+  ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc(file));
+
+  std::vector<IR::Item> items;
+  for (const auto& id : ir.top_level_item_ids) {
+    auto item = FindItemById(ir, id);
+    ASSERT_TRUE(item.has_value());
+    items.push_back(*item);
+  }
+
+  EXPECT_THAT(ir.top_level_item_ids, SizeIs(5));
+  EXPECT_THAT(
+      items,
+      ElementsAre(
+          VariantWith<Record>(RsNameIs("TopLevelStruct")),
+          VariantWith<Comment>(TextIs("Top level comment")),
+          VariantWith<Func>(IdentifierIs("top_level_func")),
+          VariantWith<UnsupportedItem>(NameIs("top_level_namespace")),
+          VariantWith<Comment>(TextIs("namespace top_level_namespace"))));
+}
+
+TEST(ImporterTest, RecordItemIds) {
+  absl::string_view file = R"cc(
+    struct TopLevelStruct {
+      // A free comment
+
+      // foo comment
+      int foo;
+
+      int bar();
+      struct Nested {};
+      int baz();
+    };
+  )cc";
+  ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc(file));
+
+  std::vector<const Record*> records = ir.get_items_if<Record>();
+  EXPECT_THAT(records, SizeIs(1));
+
+  std::vector<IR::Item> items;
+  for (const auto& id : records[0]->child_item_ids) {
+    auto item = FindItemById(ir, id);
+    ASSERT_TRUE(item.has_value());
+    items.push_back(*item);
+  }
+
+  EXPECT_THAT(items,
+              AllOf(Contains(VariantWith<Comment>(TextIs("A free comment"))),
+                    Contains(VariantWith<Func>(IdentifierIs("bar"))),
+                    Contains(VariantWith<UnsupportedItem>(
+                        NameIs("TopLevelStruct::Nested"))),
+                    Contains(VariantWith<Func>(IdentifierIs("baz")))));
+}
+
 }  // namespace
 }  // namespace crubit