Cover `enclosing_namespace_id` in `IncompleteRecord::ToJson`.

PiperOrigin-RevId: 474810361
diff --git a/rs_bindings_from_cc/ir.cc b/rs_bindings_from_cc/ir.cc
index 90641f5..3083d12 100644
--- a/rs_bindings_from_cc/ir.cc
+++ b/rs_bindings_from_cc/ir.cc
@@ -382,6 +382,7 @@
       {"id", id},
       {"owning_target", owning_target},
       {"record_type", RecordTypeToString(record_type)},
+      {"enclosing_namespace_id", enclosing_namespace_id},
   };
 
   return llvm::json::Object{
diff --git a/rs_bindings_from_cc/ir_from_cc_test.rs b/rs_bindings_from_cc/ir_from_cc_test.rs
index 96775ec..b05e88e 100644
--- a/rs_bindings_from_cc/ir_from_cc_test.rs
+++ b/rs_bindings_from_cc/ir_from_cc_test.rs
@@ -2371,6 +2371,46 @@
 }
 
 #[test]
+fn test_struct_forward_declaration_in_namespace() -> Result<()> {
+    let ir = ir_from_cc(
+        r#"
+        namespace MyNamespace {
+        struct FwdDeclared;
+        }
+        "#,
+    )?;
+
+    assert_eq!(1, ir.namespaces().count());
+    let ns = ir.namespaces().next().unwrap();
+    assert_eq!("MyNamespace", ns.name.identifier);
+    assert_eq!(1, ns.child_item_ids.len());
+
+    let ns_id = ns.id;
+    let child_id = ns.child_item_ids[0];
+    assert_ir_matches!(
+        ir,
+        quote! {
+            Namespace(Namespace {
+                name: "MyNamespace" ...
+                id: ItemId(#ns_id) ...
+                child_item_ids: [ItemId(#child_id)] ...
+                enclosing_record_id: None ...
+                enclosing_namespace_id: None ...
+            }),
+            IncompleteRecord(IncompleteRecord {
+                cc_name: "FwdDeclared" ...
+                rs_name: "FwdDeclared" ...
+                id: ItemId(#child_id) ...
+                ...
+                enclosing_namespace_id: Some(ItemId(#ns_id)) ...
+            }),
+        }
+    );
+
+    Ok(())
+}
+
+#[test]
 fn test_union() {
     let ir = ir_from_cc("union SomeUnion { int first_field; int second_field; };").unwrap();
     assert_ir_matches!(
diff --git a/rs_bindings_from_cc/test/struct/forward_declarations/BUILD b/rs_bindings_from_cc/test/struct/forward_declarations/BUILD
index 10baa72..cf3527e 100644
--- a/rs_bindings_from_cc/test/struct/forward_declarations/BUILD
+++ b/rs_bindings_from_cc/test/struct/forward_declarations/BUILD
@@ -26,6 +26,11 @@
     ],
 )
 
+cc_library(
+    name = "no_definition_in_headers",
+    hdrs = ["no_definition_in_headers.h"],
+)
+
 rust_test(
     name = "forward_declarations_test",
     srcs = ["forward_declarations_test.rs"],
@@ -33,6 +38,7 @@
         ":declaration_1",
         ":declaration_2",
         ":definition",
+        ":no_definition_in_headers",
     ],
     deps = [
         "//rs_bindings_from_cc/support:ctor",
diff --git a/rs_bindings_from_cc/test/struct/forward_declarations/forward_declarations_test.rs b/rs_bindings_from_cc/test/struct/forward_declarations/forward_declarations_test.rs
index 5187393..adef747 100644
--- a/rs_bindings_from_cc/test/struct/forward_declarations/forward_declarations_test.rs
+++ b/rs_bindings_from_cc/test/struct/forward_declarations/forward_declarations_test.rs
@@ -190,3 +190,11 @@
     let nonunpin_ref = &*nonunpin;
     assert_eq!(123, declaration_1::InlineFunctionTakingNonunpinStruct(nonunpin_ref.cc_cast()));
 }
+
+#[test]
+fn test_forward_declared_used_as_field_type() {
+    // This is a regression test for b/246962427.  This mostly verifies that the
+    // generated bindings compile (and are usable at a very basic level).
+    use no_definition_in_headers::no_definition_in_headers::*;
+    let _s = Defined { field: std::ptr::null_mut() };
+}
diff --git a/rs_bindings_from_cc/test/struct/forward_declarations/no_definition_in_headers.h b/rs_bindings_from_cc/test/struct/forward_declarations/no_definition_in_headers.h
new file mode 100644
index 0000000..dfc4a7d
--- /dev/null
+++ b/rs_bindings_from_cc/test/struct/forward_declarations/no_definition_in_headers.h
@@ -0,0 +1,28 @@
+// Part of the Crubit project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#ifndef THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_STRUCT_FORWARD_DECLARATIONS_NO_DEFINITION_IN_HEADERS_H_
+#define THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_STRUCT_FORWARD_DECLARATIONS_NO_DEFINITION_IN_HEADERS_H_
+
+#pragma clang lifetime_elision
+
+// This is a regression test for b/246962427.
+//
+// This test mimics `absl::SynchLocksHeld` which is forward-declared in
+// `absl/base/internal/thread_identity.h` and doesn't have a definition in any
+// headers (only in `absl/synchronization/mutex.cc`).
+//
+// OTOH, note that the no-definition-in-headers wasn't the root cause of
+// b/246962427.  Instead, there was a minor problem in the integration between
+// A) `namespace` support and B) forward-declarations support.
+namespace no_definition_in_headers {
+
+struct FwdDeclared;
+
+struct Defined final {
+  FwdDeclared* field;
+};
+
+}  // namespace no_definition_in_headers
+
+#endif  // THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_STRUCT_FORWARD_DECLARATIONS_NO_DEFINITION_IN_HEADERS_H_