Import decls in LinkageSpecDecl

We don't need an explicit Item for this decl, so in this CL we're changing
importer.cc to iterate decls inside LinkageSpecDecl right away.

PiperOrigin-RevId: 450869812
diff --git a/rs_bindings_from_cc/BUILD b/rs_bindings_from_cc/BUILD
index 2c373f0..0e1be69 100644
--- a/rs_bindings_from_cc/BUILD
+++ b/rs_bindings_from_cc/BUILD
@@ -335,6 +335,7 @@
         "//common:rust_allocator_shims",
         "@crate_index//:anyhow",
         "@crate_index//:itertools",
+        "@crate_index//:proc-macro2",
         "@crate_index//:quote",
     ],
 )
diff --git a/rs_bindings_from_cc/importer.cc b/rs_bindings_from_cc/importer.cc
index 4fdd514..b785291 100644
--- a/rs_bindings_from_cc/importer.cc
+++ b/rs_bindings_from_cc/importer.cc
@@ -298,10 +298,21 @@
   }
 
   absl::flat_hash_set<ItemId> visited_item_ids;
-  for (auto child : decl_context->decls()) {
+  std::vector<clang::Decl*> decls_to_visit;
+  llvm::copy(decl_context->decls(), std::back_inserter(decls_to_visit));
+
+  while (!decls_to_visit.empty()) {
+    clang::Decl* child = decls_to_visit.back();
+    decls_to_visit.pop_back();
     auto decl = child->getCanonicalDecl();
     if (!IsFromCurrentTarget(decl)) continue;
 
+    if (const auto* linkage_spec_decl =
+            llvm::dyn_cast<clang::LinkageSpecDecl>(decl)) {
+      absl::c_copy(linkage_spec_decl->decls(),
+                   std::back_inserter(decls_to_visit));
+    }
+
     // We remove comments attached to a child decl or that are within a child
     // decl.
     if (auto raw_comment = ctx_.getRawCommentForDeclNoCache(decl)) {
@@ -413,9 +424,14 @@
 void Importer::ImportDeclsFromDeclContext(
     const clang::DeclContext* decl_context) {
   for (auto decl : decl_context->decls()) {
-    // TODO(rosica): We don't always want the canonical decl here (especially
-    // not in namespaces).
-    GetDeclItem(decl->getCanonicalDecl());
+    if (const auto* linkage_spec_decl =
+            llvm::dyn_cast<clang::LinkageSpecDecl>(decl)) {
+      ImportDeclsFromDeclContext(linkage_spec_decl);
+    } else {
+      // TODO(rosica): We don't always want the canonical decl here (especially
+      // not in namespaces).
+      GetDeclItem(decl->getCanonicalDecl());
+    }
   }
 }
 
diff --git a/rs_bindings_from_cc/ir_from_cc_test.rs b/rs_bindings_from_cc/ir_from_cc_test.rs
index 375e2f5..46eafca 100644
--- a/rs_bindings_from_cc/ir_from_cc_test.rs
+++ b/rs_bindings_from_cc/ir_from_cc_test.rs
@@ -2856,3 +2856,44 @@
     // assert_eq!(namespaces[0].canonical_namespace_id,
     // namespaces[1].canonical_namespace_id);
 }
+
+#[test]
+fn test_items_inside_linkage_spec_decl_are_imported() {
+    let ir = ir_from_cc(
+        r#"
+          extern "C" {
+            struct MyStruct {};
+          }
+      "#,
+    )
+    .unwrap();
+    assert_ir_matches!(ir, quote! { Record { ... cc_name: "MyStruct" ... } })
+}
+
+#[test]
+fn test_items_inside_linkage_spec_decl_are_considered_toplevel() {
+    // The test below assumes the first top_level_item_ids element is the one added
+    // by the the source code under test. Let's double check that assumption here.
+    assert!(ir_from_cc("").unwrap().top_level_item_ids().next().is_none());
+
+    let ir = ir_from_cc(
+        r#"
+    extern "C" {
+      struct MyStruct {};
+    }"#,
+    )
+    .unwrap();
+    let item_id = proc_macro2::Literal::usize_unsuffixed(ir.top_level_item_ids().next().unwrap().0);
+
+    assert_ir_matches!(
+        ir,
+        quote! {
+          ...
+          Record {
+            ... cc_name: "MyStruct" ...
+            ... id: ItemId(#item_id) ...
+          }
+          ...
+        }
+    );
+}