Skip to content

Commit e4a2dcf

Browse files
Changochencopybara-github
authored andcommitted
Support use statement for functions and records.
PiperOrigin-RevId: 653413189 Change-Id: I47dd78ebd9acaeada17af109f7485a011259a248
1 parent 425f441 commit e4a2dcf

File tree

6 files changed

+240
-13
lines changed

6 files changed

+240
-13
lines changed

cc_bindings_from_rs/bindings.rs

Lines changed: 128 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ use itertools::Itertools;
2323
use proc_macro2::{Ident, Literal, TokenStream};
2424
use quote::{format_ident, quote, ToTokens};
2525
use rustc_attr::find_deprecation;
26-
use rustc_hir::{AssocItemKind, Item, ItemKind, Node, Safety};
26+
use rustc_hir::def::{DefKind, Res};
27+
use rustc_hir::{AssocItemKind, Item, ItemKind, Node, Safety, UseKind, UsePath};
2728
use rustc_infer::infer::TyCtxtInferExt;
2829
use rustc_middle::dep_graph::DepContext;
2930
use rustc_middle::mir::Mutability;
@@ -1078,26 +1079,115 @@ fn format_deprecated_tag(tcx: TyCtxt, def_id: DefId) -> Option<TokenStream> {
10781079
None
10791080
}
10801081

1082+
fn format_use(
1083+
db: &dyn BindingsGenerator<'_>,
1084+
using_name: &str,
1085+
use_path: &UsePath,
1086+
use_kind: &UseKind,
1087+
) -> Result<ApiSnippets> {
1088+
let tcx = db.tcx();
1089+
1090+
// TODO(b/350772554): Support multiple items with the same name in `use`
1091+
// statements.`
1092+
if use_path.res.len() != 1 {
1093+
bail!(
1094+
"use statements which resolve to multiple items with the same name are not supported yet"
1095+
);
1096+
}
1097+
1098+
match use_kind {
1099+
UseKind::Single => {}
1100+
// TODO(b/350772554): Implement `pub use foo::{x,y}` and `pub use foo::*`
1101+
UseKind::Glob | UseKind::ListStem => {
1102+
bail!("Unsupported use kind: {use_kind:?}");
1103+
}
1104+
};
1105+
let (def_kind, def_id) = match use_path.res[0] {
1106+
// TODO(b/350772554): Support PrimTy.
1107+
Res::Def(def_kind, def_id) => (def_kind, def_id),
1108+
_ => {
1109+
bail!(
1110+
"Unsupported use statement that refers to this type of the entity: {:#?}",
1111+
use_path.res[0]
1112+
);
1113+
}
1114+
};
1115+
ensure!(
1116+
is_directly_public(tcx, def_id),
1117+
"Not directly public type (re-exports are not supported yet - b/262052635)"
1118+
);
1119+
1120+
match def_kind {
1121+
DefKind::Fn => {
1122+
let mut prereqs;
1123+
// TODO(b/350772554): Support exporting private functions.
1124+
if let Some(local_id) = def_id.as_local() {
1125+
if let Ok(snippet) = db.format_fn(local_id) {
1126+
prereqs = snippet.main_api.prereqs;
1127+
} else {
1128+
bail!("Ignoring the use because the bindings for the target is not generated");
1129+
}
1130+
} else {
1131+
bail!("Unsupported checking for external function");
1132+
}
1133+
let fully_qualified_fn_name = FullyQualifiedName::new(tcx, def_id);
1134+
let unqualified_rust_fn_name =
1135+
fully_qualified_fn_name.name.expect("Functions are assumed to always have a name");
1136+
let formatted_fully_qualified_fn_name = fully_qualified_fn_name.format_for_cc()?;
1137+
let cpp_name = crubit_attr::get(tcx, def_id).unwrap().cpp_name;
1138+
let main_api_fn_name =
1139+
format_cc_ident(cpp_name.unwrap_or(unqualified_rust_fn_name).as_str())
1140+
.context("Error formatting function name")?;
1141+
let using_name = format_cc_ident(using_name).context("Error formatting using name")?;
1142+
1143+
prereqs.defs.insert(def_id.expect_local());
1144+
let tokens = if format!("{}", using_name) == format!("{}", main_api_fn_name) {
1145+
quote! {using #formatted_fully_qualified_fn_name;}
1146+
} else {
1147+
// TODO(b/350772554): Support function alias.
1148+
bail!("Unsupported function alias");
1149+
};
1150+
Ok(ApiSnippets {
1151+
main_api: CcSnippet { prereqs, tokens },
1152+
cc_details: CcSnippet::default(),
1153+
rs_details: quote! {},
1154+
})
1155+
}
1156+
DefKind::Struct | DefKind::Enum => {
1157+
let use_type = tcx.type_of(def_id).instantiate_identity();
1158+
create_type_alias(db, using_name, use_type)
1159+
}
1160+
_ => bail!(
1161+
"Unsupported use statement that refers to this type of the entity: {:#?}",
1162+
use_path.res
1163+
),
1164+
}
1165+
}
1166+
10811167
fn format_type_alias(
10821168
db: &dyn BindingsGenerator<'_>,
10831169
local_def_id: LocalDefId,
10841170
) -> Result<ApiSnippets> {
10851171
let tcx = db.tcx();
10861172
let def_id: DefId = local_def_id.to_def_id();
10871173
let alias_type = tcx.type_of(def_id).instantiate_identity();
1088-
let alias_name = format_cc_ident(tcx.item_name(def_id).as_str())
1089-
.context("Error formatting type alias name")?;
1174+
create_type_alias(db, tcx.item_name(def_id).as_str(), alias_type)
1175+
}
1176+
1177+
fn create_type_alias<'tcx>(
1178+
db: &dyn BindingsGenerator<'tcx>,
1179+
alias_name: &str,
1180+
alias_type: Ty<'tcx>,
1181+
) -> Result<ApiSnippets> {
10901182
let cc_bindings = format_ty_for_cc(db, alias_type, TypeLocation::Other)?;
10911183
let mut main_api_prereqs = CcPrerequisites::default();
10921184
let actual_type_name = cc_bindings.into_tokens(&mut main_api_prereqs);
10931185

1186+
let alias_name = format_cc_ident(alias_name).context("Error formatting type alias name")?;
1187+
let tokens = quote! {using #alias_name = #actual_type_name;};
1188+
10941189
Ok(ApiSnippets {
1095-
main_api: CcSnippet {
1096-
prereqs: main_api_prereqs,
1097-
tokens: quote! {
1098-
using #alias_name = #actual_type_name;
1099-
},
1100-
},
1190+
main_api: CcSnippet { prereqs: main_api_prereqs, tokens },
11011191
cc_details: CcSnippet::default(),
11021192
rs_details: quote! {},
11031193
})
@@ -2526,6 +2616,9 @@ fn format_item(db: &dyn BindingsGenerator<'_>, def_id: LocalDefId) -> Result<Opt
25262616
db.format_adt_core(def_id.to_def_id())
25272617
.map(|core| Some(format_adt(db, core))),
25282618
Item { kind: ItemKind::TyAlias(..), ..} => format_type_alias(db, def_id).map(Some),
2619+
Item { ident, kind: ItemKind::Use(use_path, use_kind), ..} => {
2620+
format_use(db, ident.as_str(), use_path, use_kind).map(Some)
2621+
},
25292622
Item { kind: ItemKind::Impl(_), .. } | // Handled by `format_adt`
25302623
Item { kind: ItemKind::Mod(_), .. } => // Handled by `format_crate`
25312624
Ok(None),
@@ -3420,12 +3513,12 @@ pub mod tests {
34203513
test_generated_bindings(test_src, |bindings| {
34213514
let bindings = bindings.unwrap();
34223515

3423-
let failures = vec![(1, 15), (3, 21), (4, 24)];
3516+
let failures = vec![(1, 15), (3, 21)];
34243517
for (use_number, line_number) in failures.into_iter() {
34253518
let expected_comment_txt = format!(
34263519
"Error generating bindings for `{{use#{use_number}}}` defined at \
34273520
<crubit_unittests.rs>;l={line_number}: \
3428-
Unsupported rustc_hir::hir::ItemKind: `use` import"
3521+
Not directly public type (re-exports are not supported yet - b/262052635)"
34293522
);
34303523
assert_cc_matches!(
34313524
bindings.h_body,
@@ -6850,6 +6943,30 @@ pub mod tests {
68506943
});
68516944
}
68526945

6946+
#[test]
6947+
fn test_format_item_use_normal_type() {
6948+
let test_src = r#"
6949+
pub mod test_mod {
6950+
pub struct S{
6951+
pub field: i32
6952+
}
6953+
}
6954+
6955+
pub use test_mod::S as G;
6956+
"#;
6957+
test_format_item(test_src, "G", |result| {
6958+
let result = result.unwrap().unwrap();
6959+
let main_api = &result.main_api;
6960+
assert!(!main_api.prereqs.is_empty());
6961+
assert_cc_matches!(
6962+
main_api.tokens,
6963+
quote! {
6964+
using G = ::rust_out::test_mod::S;
6965+
}
6966+
);
6967+
});
6968+
}
6969+
68536970
#[test]
68546971
fn test_format_item_type_alias() {
68556972
let test_src = r#"

cc_bindings_from_rs/cc_bindings_from_rs.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,11 +336,12 @@ mod tests {
336336
assert!(error_report_out_path.exists());
337337
let error_report = std::fs::read_to_string(&error_report_out_path)?;
338338
let expected_error_report = r#"{
339-
"Unsupported rustc_hir::hir::ItemKind: {}": {
339+
"Unsupported use statement that refers to this type of the entity: {:#?}": {
340340
"count": 2,
341-
"sample_message": "Unsupported rustc_hir::hir::ItemKind: `use` import"
341+
"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]"
342342
}
343343
}"#;
344+
println!("error_report: {}", error_report);
344345
assert_eq!(expected_error_report, error_report);
345346
Ok(())
346347
}

cc_bindings_from_rs/test/uses/BUILD

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""End-to-end tests of `cc_bindings_from_rs`, focusing on use statements."""
2+
3+
load(
4+
"@rules_rust//rust:defs.bzl",
5+
"rust_library",
6+
)
7+
load(
8+
"//cc_bindings_from_rs/bazel_support:cc_bindings_from_rust_rule.bzl",
9+
"cc_bindings_from_rust",
10+
)
11+
load("//common:crubit_wrapper_macros_oss.bzl", "crubit_cc_test")
12+
13+
package(default_applicable_licenses = ["//:license"])
14+
15+
rust_library(
16+
name = "uses",
17+
testonly = 1,
18+
srcs = ["uses.rs"],
19+
)
20+
21+
cc_bindings_from_rust(
22+
name = "uses_cc_api",
23+
testonly = 1,
24+
crate = ":uses",
25+
)
26+
27+
crubit_cc_test(
28+
name = "uses_test",
29+
srcs = ["uses_test.cc"],
30+
deps = [
31+
":uses_cc_api",
32+
"@com_google_googletest//:gtest_main",
33+
],
34+
)

cc_bindings_from_rs/test/uses/uses.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Part of the Crubit project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
// Put before the real definition to make sure that the generarated C++ bindings
6+
// is not affected by the order of the imports.
7+
pub use test_mod::f;
8+
9+
pub mod test_mod {
10+
pub fn f() -> i32 {
11+
42
12+
}
13+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Part of the Crubit project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#include <cstdint>
6+
#include <type_traits>
7+
#include <utility>
8+
9+
#include "gtest/gtest.h"
10+
#include "cc_bindings_from_rs/test/uses/uses_cc_api.h"
11+
12+
namespace crubit {
13+
namespace {
14+
15+
TEST(UsesTest, UsesExportsAsUsing) { EXPECT_EQ(uses::f(), 42); }
16+
17+
} // namespace
18+
} // namespace crubit

docs/rust/use_statements.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# C++ bindings for `use` statement
2+
3+
Crubit supports `use` statement for functions and user-defined types. For the
4+
following Rust code:
5+
6+
```rust
7+
pub use pub_module::pub_function;
8+
pub use pub_module::pub_struct;
9+
pub use pub_module::pub_struct2 as pub_struct3;
10+
```
11+
12+
Crubit will generate the following bindings:
13+
14+
```cpp
15+
using ::pub_module::pub_function;
16+
using ::pub_module::pub_struct;
17+
using pub_struct3 = ::pub_module::pub_struct2;
18+
```
19+
20+
Currently, Crubit doesn't support the following cases:
21+
22+
### `use` with multiple items or glob
23+
24+
```rust
25+
pub use some_module::{a, b}; // Not supported yet.
26+
pub use some_module::*; // Not supported yet.`
27+
```
28+
29+
### `use` a module
30+
31+
```rust
32+
pub use some_module; // Not supported yet.
33+
```
34+
35+
### `use` a non-directly public item
36+
37+
```rust
38+
mod private_mod {
39+
pub fn func() {}
40+
}
41+
42+
pub use private_mod::func; // Not supported yet. `func` is not directly public
43+
// because `private_mod` is private.
44+
```

0 commit comments

Comments
 (0)