Support `use` glob.

PiperOrigin-RevId: 670742832
Change-Id: Idd540ae60707b88dd912c87e8000a4b97f2e6191
diff --git a/cc_bindings_from_rs/bindings.rs b/cc_bindings_from_rs/bindings.rs
index 6a38375..aecb12e 100644
--- a/cc_bindings_from_rs/bindings.rs
+++ b/cc_bindings_from_rs/bindings.rs
@@ -30,7 +30,7 @@
 use rustc_middle::mir::ConstValue;
 use rustc_middle::mir::Mutability;
 use rustc_middle::ty::{self, Ty, TyCtxt}; // See <internal link>/ty.html#import-conventions
-use rustc_span::def_id::{DefId, LocalDefId, LOCAL_CRATE};
+use rustc_span::def_id::{DefId, LocalDefId, LocalModDefId, LOCAL_CRATE};
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_target::abi::{
     Abi, AddressSpace, FieldsShape, Integer, Layout, Pointer, Primitive, Scalar,
@@ -1336,44 +1336,13 @@
     None
 }
 
-fn format_use(
+fn generate_using_statement(
     db: &dyn BindingsGenerator<'_>,
     using_name: &str,
-    use_path: &UsePath,
-    use_kind: &UseKind,
+    def_id: DefId,
+    def_kind: DefKind,
 ) -> Result<ApiSnippets> {
     let tcx = db.tcx();
-
-    // TODO(b/350772554): Support multiple items with the same name in `use`
-    // statements.`
-    if use_path.res.len() != 1 {
-        bail!(
-            "use statements which resolve to multiple items with the same name are not supported yet"
-        );
-    }
-
-    match use_kind {
-        UseKind::Single | UseKind::ListStem => {}
-        // TODO(b/350772554): Implement `pub use foo::*`
-        UseKind::Glob => {
-            bail!("Unsupported use kind: {use_kind:?}");
-        }
-    };
-    let (def_kind, def_id) = match use_path.res[0] {
-        // TODO(b/350772554): Support PrimTy.
-        Res::Def(def_kind, def_id) => (def_kind, def_id),
-        _ => {
-            bail!(
-                "Unsupported use statement that refers to this type of the entity: {:#?}",
-                use_path.res[0]
-            );
-        }
-    };
-    ensure!(
-        is_directly_public(tcx, def_id),
-        "Not directly public type (re-exports are not supported yet - b/262052635)"
-    );
-
     match def_kind {
         DefKind::Fn => {
             let mut prereqs;
@@ -1416,13 +1385,96 @@
             let use_type = SugaredTy::new(tcx.type_of(def_id).instantiate_identity(), None);
             create_type_alias(db, def_id, using_name, use_type)
         }
-        _ => bail!(
-            "Unsupported use statement that refers to this type of the entity: {:#?}",
-            use_path.res
-        ),
+        _ => {
+            bail!("Unsupported use statement that refers to this type of the entity: {:#?}", def_id)
+        }
     }
 }
 
+fn public_free_items_in_mod(
+    db: &dyn BindingsGenerator<'_>,
+    def_id: DefId,
+) -> Vec<(DefId, DefKind)> {
+    let mut items = vec![];
+    if def_id.as_local().is_none() {
+        return items;
+    }
+    let tcx = db.tcx();
+    let local_mod_def_id = LocalModDefId::new_unchecked(def_id.as_local().unwrap());
+    for item_id in tcx.hir_module_items(local_mod_def_id).free_items() {
+        let item_local_def_id: LocalDefId = item_id.owner_id.def_id;
+        let item_def_id = item_local_def_id.to_def_id();
+        let item_def_kind = tcx.def_kind(item_def_id);
+
+        if !is_directly_public(tcx, item_def_id) {
+            continue;
+        }
+        match item_def_kind {
+            DefKind::Fn | DefKind::Struct | DefKind::Enum => {
+                items.push((item_def_id, item_def_kind));
+            }
+            _ => {}
+        }
+    }
+    items
+}
+
+fn format_use(
+    db: &dyn BindingsGenerator<'_>,
+    using_name: &str,
+    use_path: &UsePath,
+    use_kind: &UseKind,
+) -> Result<ApiSnippets> {
+    let tcx = db.tcx();
+
+    // TODO(b/350772554): Support multiple items with the same name in `use`
+    // statements.`
+    if use_path.res.len() != 1 {
+        bail!(
+            "use statements which resolve to multiple items with the same name are not supported yet"
+        );
+    }
+
+    let (def_kind, def_id) = match use_path.res[0] {
+        // TODO(b/350772554): Support PrimTy.
+        // TODO(b/350772554): Support `use some_module`.
+        Res::Def(def_kind, def_id) if def_kind != DefKind::Mod || use_kind == &UseKind::Glob => {
+            (def_kind, def_id)
+        }
+        _ => {
+            bail!(
+                "Unsupported use statement that refers to this type of the entity: {:#?}",
+                use_path.res[0]
+            );
+        }
+    };
+    ensure!(
+        is_directly_public(tcx, def_id),
+        "Not directly public type (re-exports are not supported yet - b/262052635)"
+    );
+
+    let mut aliases = vec![];
+    if def_kind == DefKind::Mod {
+        for (item_def_id, item_def_kind) in public_free_items_in_mod(db, def_id) {
+            let item_using_name = format_cc_ident(tcx.item_name(item_def_id).as_str())
+                .context("Error formatting using name")?;
+            aliases.push((item_using_name.to_string(), item_def_id, item_def_kind));
+        }
+    } else {
+        aliases.push((using_name.to_string(), def_id, def_kind));
+    }
+    // TODO(b/350772554): Expose the errors. If any of the types in the `use`
+    // statement is not supported, we currently ignore it and discard the
+    // errors.
+    Ok(aliases
+        .into_iter()
+        .map(|(using_name, def_id, def_kind)| {
+            generate_using_statement(db, &using_name, def_id, def_kind)
+        })
+        .filter_map(Result::ok)
+        .collect())
+}
+
 fn format_const(db: &dyn BindingsGenerator<'_>, local_def_id: LocalDefId) -> Result<ApiSnippets> {
     let tcx = db.tcx();
     let def_id: DefId = local_def_id.to_def_id();
@@ -7567,6 +7619,33 @@
     }
 
     #[test]
+    fn test_generate_bindings_use_glob() {
+        let test_src = r#"
+            pub mod test_mod {
+                pub struct X{
+                    pub field: i32
+                }
+                pub struct Y{
+                    pub field: i32
+                }
+            }
+
+            pub use test_mod::*;
+            "#;
+
+        test_generated_bindings(test_src, |bindings| {
+            let bindings = bindings.unwrap();
+            assert_cc_matches!(
+                bindings.h_body,
+                quote! {
+                    using X = ::rust_out::test_mod::X;
+                    using Y = ::rust_out::test_mod::Y;
+                }
+            );
+        });
+    }
+
+    #[test]
     fn test_format_item_type_alias() {
         let test_src = r#"
                 pub type TypeAlias = i32;
diff --git a/cc_bindings_from_rs/cc_bindings_from_rs.rs b/cc_bindings_from_rs/cc_bindings_from_rs.rs
index 82598d6..dc228d0 100644
--- a/cc_bindings_from_rs/cc_bindings_from_rs.rs
+++ b/cc_bindings_from_rs/cc_bindings_from_rs.rs
@@ -347,7 +347,7 @@
         let expected_error_report = r#"{
   "Unsupported use statement that refers to this type of the entity: {:#?}": {
     "count": 2,
-    "sample_message": "Unsupported use statement that refers to this type of the entity: [\n    Def(\n        Mod,\n        DefId(std[46ff]::collections),\n    ),\n]"
+    "sample_message": "Unsupported use statement that refers to this type of the entity: Def(\n    Mod,\n    DefId(std[46ff]::collections),\n)"
   }
 }"#;
         assert_eq!(expected_error_report, error_report);
diff --git a/cc_bindings_from_rs/test/golden/uses.rs b/cc_bindings_from_rs/test/golden/uses.rs
new file mode 100644
index 0000000..2920f66
--- /dev/null
+++ b/cc_bindings_from_rs/test/golden/uses.rs
@@ -0,0 +1,29 @@
+// 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
+
+#![allow(dead_code)]
+
+pub mod test_use_glob {
+    pub fn f1() -> i32 {
+        42
+    }
+
+    pub fn f2() -> i32 {
+        43
+    }
+
+    fn f3() -> i32 {
+        44
+    }
+
+    pub struct X1 {
+        x: i32,
+    }
+
+    struct X2 {
+        x: i32,
+    }
+}
+
+pub use test_use_glob::*;
diff --git a/cc_bindings_from_rs/test/golden/uses_cc_api.h b/cc_bindings_from_rs/test/golden/uses_cc_api.h
new file mode 100644
index 0000000..450e4b5
--- /dev/null
+++ b/cc_bindings_from_rs/test/golden/uses_cc_api.h
@@ -0,0 +1,88 @@
+// 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
+
+// Automatically @generated C++ bindings for the following Rust crate:
+// uses_rust
+
+#pragma once
+
+#include "support/internal/attribute_macros.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <type_traits>
+
+namespace uses_rust {
+
+namespace test_use_glob {
+
+// Generated from:
+// cc_bindings_from_rs/test/golden/uses.rs;l=8
+std::int32_t f1();
+
+// Generated from:
+// cc_bindings_from_rs/test/golden/uses.rs;l=12
+std::int32_t f2();
+
+// Generated from:
+// cc_bindings_from_rs/test/golden/uses.rs;l=20
+struct CRUBIT_INTERNAL_RUST_TYPE(":: uses_rust :: test_use_glob :: X1") alignas(
+    4) [[clang::trivial_abi]] X1 final {
+ public:
+  // `test_use_glob::X1` doesn't implement the `Default` trait
+  X1() = delete;
+
+  // No custom `Drop` impl and no custom \"drop glue\" required
+  ~X1() = default;
+  X1(X1&&) = default;
+  X1& operator=(X1&&) = default;
+
+  // `test_use_glob::X1` doesn't implement the `Clone` trait
+  X1(const X1&) = delete;
+  X1& operator=(const X1&) = delete;
+
+ private:
+  union {
+    // Generated from:
+    // cc_bindings_from_rs/test/golden/uses.rs;l=21
+    std::int32_t x;
+  };
+
+ private:
+  static void __crubit_field_offset_assertions();
+};
+
+}  // namespace test_use_glob
+
+using ::uses_rust::test_use_glob::f1;
+using ::uses_rust::test_use_glob::f2;
+using X1 = ::uses_rust::test_use_glob::X1;
+
+namespace test_use_glob {
+
+namespace __crubit_internal {
+extern "C" std::int32_t __crubit_thunk_f1();
+}
+inline std::int32_t f1() { return __crubit_internal::__crubit_thunk_f1(); }
+
+namespace __crubit_internal {
+extern "C" std::int32_t __crubit_thunk_f2();
+}
+inline std::int32_t f2() { return __crubit_internal::__crubit_thunk_f2(); }
+
+static_assert(
+    sizeof(X1) == 4,
+    "Verify that ADT layout didn't change since this header got generated");
+static_assert(
+    alignof(X1) == 4,
+    "Verify that ADT layout didn't change since this header got generated");
+static_assert(std::is_trivially_destructible_v<X1>);
+static_assert(std::is_trivially_move_constructible_v<X1>);
+static_assert(std::is_trivially_move_assignable_v<X1>);
+inline void X1::__crubit_field_offset_assertions() {
+  static_assert(0 == offsetof(X1, x));
+}
+}  // namespace test_use_glob
+
+}  // namespace uses_rust
diff --git a/cc_bindings_from_rs/test/golden/uses_cc_api_impl.rs b/cc_bindings_from_rs/test/golden/uses_cc_api_impl.rs
new file mode 100644
index 0000000..21f4845
--- /dev/null
+++ b/cc_bindings_from_rs/test/golden/uses_cc_api_impl.rs
@@ -0,0 +1,19 @@
+// 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
+
+// Automatically @generated C++ bindings for the following Rust crate:
+// uses_rust
+
+#![allow(improper_ctypes_definitions)]
+
+#[no_mangle]
+extern "C" fn __crubit_thunk_f1() -> i32 {
+    ::uses_rust::test_use_glob::f1()
+}
+#[no_mangle]
+extern "C" fn __crubit_thunk_f2() -> i32 {
+    ::uses_rust::test_use_glob::f2()
+}
+const _: () = assert!(::std::mem::size_of::<::uses_rust::test_use_glob::X1>() == 4);
+const _: () = assert!(::std::mem::align_of::<::uses_rust::test_use_glob::X1>() == 4);