Support use statement for functions and records.

PiperOrigin-RevId: 653413189
Change-Id: I47dd78ebd9acaeada17af109f7485a011259a248
diff --git a/cc_bindings_from_rs/bindings.rs b/cc_bindings_from_rs/bindings.rs
index 2a9d7fa..b4fe3b0 100644
--- a/cc_bindings_from_rs/bindings.rs
+++ b/cc_bindings_from_rs/bindings.rs
@@ -23,7 +23,8 @@
 use proc_macro2::{Ident, Literal, TokenStream};
 use quote::{format_ident, quote, ToTokens};
 use rustc_attr::find_deprecation;
-use rustc_hir::{AssocItemKind, Item, ItemKind, Node, Safety};
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{AssocItemKind, Item, ItemKind, Node, Safety, UseKind, UsePath};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::dep_graph::DepContext;
 use rustc_middle::mir::Mutability;
@@ -1078,6 +1079,91 @@
     None
 }
 
+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"
+        );
+    }
+
+    match use_kind {
+        UseKind::Single => {}
+        // TODO(b/350772554): Implement `pub use foo::{x,y}` and `pub use foo::*`
+        UseKind::Glob | UseKind::ListStem => {
+            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;
+            // TODO(b/350772554): Support exporting private functions.
+            if let Some(local_id) = def_id.as_local() {
+                if let Ok(snippet) = db.format_fn(local_id) {
+                    prereqs = snippet.main_api.prereqs;
+                } else {
+                    bail!("Ignoring the use because the bindings for the target is not generated");
+                }
+            } else {
+                bail!("Unsupported checking for external function");
+            }
+            let fully_qualified_fn_name = FullyQualifiedName::new(tcx, def_id);
+            let unqualified_rust_fn_name =
+                fully_qualified_fn_name.name.expect("Functions are assumed to always have a name");
+            let formatted_fully_qualified_fn_name = fully_qualified_fn_name.format_for_cc()?;
+            let cpp_name = crubit_attr::get(tcx, def_id).unwrap().cpp_name;
+            let main_api_fn_name =
+                format_cc_ident(cpp_name.unwrap_or(unqualified_rust_fn_name).as_str())
+                    .context("Error formatting function name")?;
+            let using_name = format_cc_ident(using_name).context("Error formatting using name")?;
+
+            prereqs.defs.insert(def_id.expect_local());
+            let tokens = if format!("{}", using_name) == format!("{}", main_api_fn_name) {
+                quote! {using #formatted_fully_qualified_fn_name;}
+            } else {
+                // TODO(b/350772554): Support function alias.
+                bail!("Unsupported function alias");
+            };
+            Ok(ApiSnippets {
+                main_api: CcSnippet { prereqs, tokens },
+                cc_details: CcSnippet::default(),
+                rs_details: quote! {},
+            })
+        }
+        DefKind::Struct | DefKind::Enum => {
+            let use_type = tcx.type_of(def_id).instantiate_identity();
+            create_type_alias(db, using_name, use_type)
+        }
+        _ => bail!(
+            "Unsupported use statement that refers to this type of the entity: {:#?}",
+            use_path.res
+        ),
+    }
+}
+
 fn format_type_alias(
     db: &dyn BindingsGenerator<'_>,
     local_def_id: LocalDefId,
@@ -1085,19 +1171,23 @@
     let tcx = db.tcx();
     let def_id: DefId = local_def_id.to_def_id();
     let alias_type = tcx.type_of(def_id).instantiate_identity();
-    let alias_name = format_cc_ident(tcx.item_name(def_id).as_str())
-        .context("Error formatting type alias name")?;
+    create_type_alias(db, tcx.item_name(def_id).as_str(), alias_type)
+}
+
+fn create_type_alias<'tcx>(
+    db: &dyn BindingsGenerator<'tcx>,
+    alias_name: &str,
+    alias_type: Ty<'tcx>,
+) -> Result<ApiSnippets> {
     let cc_bindings = format_ty_for_cc(db, alias_type, TypeLocation::Other)?;
     let mut main_api_prereqs = CcPrerequisites::default();
     let actual_type_name = cc_bindings.into_tokens(&mut main_api_prereqs);
 
+    let alias_name = format_cc_ident(alias_name).context("Error formatting type alias name")?;
+    let tokens = quote! {using #alias_name = #actual_type_name;};
+
     Ok(ApiSnippets {
-        main_api: CcSnippet {
-            prereqs: main_api_prereqs,
-            tokens: quote! {
-                using #alias_name = #actual_type_name;
-            },
-        },
+        main_api: CcSnippet { prereqs: main_api_prereqs, tokens },
         cc_details: CcSnippet::default(),
         rs_details: quote! {},
     })
@@ -2526,6 +2616,9 @@
             db.format_adt_core(def_id.to_def_id())
                 .map(|core| Some(format_adt(db, core))),
         Item { kind: ItemKind::TyAlias(..), ..} => format_type_alias(db, def_id).map(Some),
+        Item { ident, kind: ItemKind::Use(use_path, use_kind), ..} => {
+            format_use(db, ident.as_str(), use_path, use_kind).map(Some)
+        },
         Item { kind: ItemKind::Impl(_), .. } |  // Handled by `format_adt`
         Item { kind: ItemKind::Mod(_), .. } =>  // Handled by `format_crate`
             Ok(None),
@@ -3420,12 +3513,12 @@
         test_generated_bindings(test_src, |bindings| {
             let bindings = bindings.unwrap();
 
-            let failures = vec![(1, 15), (3, 21), (4, 24)];
+            let failures = vec![(1, 15), (3, 21)];
             for (use_number, line_number) in failures.into_iter() {
                 let expected_comment_txt = format!(
                     "Error generating bindings for `{{use#{use_number}}}` defined at \
                      <crubit_unittests.rs>;l={line_number}: \
-                     Unsupported rustc_hir::hir::ItemKind: `use` import"
+                     Not directly public type (re-exports are not supported yet - b/262052635)"
                 );
                 assert_cc_matches!(
                     bindings.h_body,
@@ -6851,6 +6944,30 @@
     }
 
     #[test]
+    fn test_format_item_use_normal_type() {
+        let test_src = r#"
+            pub mod test_mod {
+                pub struct S{
+                    pub field: i32
+                }
+            }
+
+            pub use test_mod::S as G;
+            "#;
+        test_format_item(test_src, "G", |result| {
+            let result = result.unwrap().unwrap();
+            let main_api = &result.main_api;
+            assert!(!main_api.prereqs.is_empty());
+            assert_cc_matches!(
+                main_api.tokens,
+                quote! {
+                    using G = ::rust_out::test_mod::S;
+                }
+            );
+        });
+    }
+
+    #[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 b95f326..967197c 100644
--- a/cc_bindings_from_rs/cc_bindings_from_rs.rs
+++ b/cc_bindings_from_rs/cc_bindings_from_rs.rs
@@ -336,11 +336,12 @@
         assert!(error_report_out_path.exists());
         let error_report = std::fs::read_to_string(&error_report_out_path)?;
         let expected_error_report = r#"{
-  "Unsupported rustc_hir::hir::ItemKind: {}": {
+  "Unsupported use statement that refers to this type of the entity: {:#?}": {
     "count": 2,
-    "sample_message": "Unsupported rustc_hir::hir::ItemKind: `use` import"
+    "sample_message": "Unsupported use statement that refers to this type of the entity: [\n    Def(\n        Mod,\n        DefId(1:728 ~ std[56d5]::collections),\n    ),\n]"
   }
 }"#;
+        println!("error_report: {}", error_report);
         assert_eq!(expected_error_report, error_report);
         Ok(())
     }
diff --git a/cc_bindings_from_rs/test/uses/BUILD b/cc_bindings_from_rs/test/uses/BUILD
new file mode 100644
index 0000000..6841315
--- /dev/null
+++ b/cc_bindings_from_rs/test/uses/BUILD
@@ -0,0 +1,34 @@
+"""End-to-end tests of `cc_bindings_from_rs`, focusing on use statements."""
+
+load(
+    "@rules_rust//rust:defs.bzl",
+    "rust_library",
+)
+load(
+    "//cc_bindings_from_rs/bazel_support:cc_bindings_from_rust_rule.bzl",
+    "cc_bindings_from_rust",
+)
+load("//common:crubit_wrapper_macros_oss.bzl", "crubit_cc_test")
+
+package(default_applicable_licenses = ["//:license"])
+
+rust_library(
+    name = "uses",
+    testonly = 1,
+    srcs = ["uses.rs"],
+)
+
+cc_bindings_from_rust(
+    name = "uses_cc_api",
+    testonly = 1,
+    crate = ":uses",
+)
+
+crubit_cc_test(
+    name = "uses_test",
+    srcs = ["uses_test.cc"],
+    deps = [
+        ":uses_cc_api",
+        "@com_google_googletest//:gtest_main",
+    ],
+)
diff --git a/cc_bindings_from_rs/test/uses/uses.rs b/cc_bindings_from_rs/test/uses/uses.rs
new file mode 100644
index 0000000..e22c827
--- /dev/null
+++ b/cc_bindings_from_rs/test/uses/uses.rs
@@ -0,0 +1,13 @@
+// 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
+
+// Put before the real definition to make sure that the generarated C++ bindings
+// is not affected by the order of the imports.
+pub use test_mod::f;
+
+pub mod test_mod {
+    pub fn f() -> i32 {
+        42
+    }
+}
diff --git a/cc_bindings_from_rs/test/uses/uses_test.cc b/cc_bindings_from_rs/test/uses/uses_test.cc
new file mode 100644
index 0000000..f00f9ca
--- /dev/null
+++ b/cc_bindings_from_rs/test/uses/uses_test.cc
@@ -0,0 +1,18 @@
+// 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
+
+#include <cstdint>
+#include <type_traits>
+#include <utility>
+
+#include "gtest/gtest.h"
+#include "cc_bindings_from_rs/test/uses/uses_cc_api.h"
+
+namespace crubit {
+namespace {
+
+TEST(UsesTest, UsesExportsAsUsing) { EXPECT_EQ(uses::f(), 42); }
+
+}  // namespace
+}  // namespace crubit
\ No newline at end of file
diff --git a/docs/rust/use_statements.md b/docs/rust/use_statements.md
new file mode 100644
index 0000000..61ba14e
--- /dev/null
+++ b/docs/rust/use_statements.md
@@ -0,0 +1,44 @@
+# C++ bindings for `use` statement
+
+Crubit supports `use` statement for functions and user-defined types. For the
+following Rust code:
+
+```rust
+pub use pub_module::pub_function;
+pub use pub_module::pub_struct;
+pub use pub_module::pub_struct2 as pub_struct3;
+```
+
+Crubit will generate the following bindings:
+
+```cpp
+using ::pub_module::pub_function;
+using ::pub_module::pub_struct;
+using pub_struct3 = ::pub_module::pub_struct2;
+```
+
+Currently, Crubit doesn't support the following cases:
+
+### `use` with multiple items or glob
+
+```rust
+pub use some_module::{a, b}; // Not supported yet.
+pub use some_module::*; // Not supported yet.`
+```
+
+### `use` a module
+
+```rust
+pub use some_module; // Not supported yet.
+```
+
+### `use` a non-directly public item
+
+```rust
+mod private_mod {
+  pub fn func() {}
+}
+
+pub use private_mod::func; // Not supported yet. `func` is not directly public
+                           // because `private_mod` is private.
+```