Generate unsupported-item comments in `h_body`.
PiperOrigin-RevId: 477772319
diff --git a/cc_bindings_from_rs/README.md b/cc_bindings_from_rs/README.md
index 0a24446..ee30e5f 100644
--- a/cc_bindings_from_rs/README.md
+++ b/cc_bindings_from_rs/README.md
@@ -30,8 +30,7 @@
// Automatically @generated C++ bindings for the following Rust crate:
// test
-// List of public functions:
-// public_function
+// Error while generating bindings for `public_function` defined at [...]/scratch/test.rs:1:1: 1:25: Nothing works yet!
```
## Contributing
diff --git a/cc_bindings_from_rs/cc_bindings_from_rs.rs b/cc_bindings_from_rs/cc_bindings_from_rs.rs
index 080b75d..f503d63 100644
--- a/cc_bindings_from_rs/cc_bindings_from_rs.rs
+++ b/cc_bindings_from_rs/cc_bindings_from_rs.rs
@@ -196,6 +196,14 @@
tempdir: TempDir,
}
+ /// Result of `TestArgs::run` that helps tests access test outputs (e.g. the
+ /// internally generated `h_path` and/or `rs_input_path`).
+ #[derive(Debug)]
+ struct TestResult {
+ h_path: PathBuf,
+ rs_input_path: PathBuf,
+ }
+
impl TestArgs {
fn default_args() -> anyhow::Result<Self> {
Ok(Self {
@@ -241,9 +249,9 @@
///
/// Returns the path to the `h_out` file. The file's lifetime is the
/// same as `&self`.
- fn run(&self) -> anyhow::Result<PathBuf> {
+ fn run(&self) -> anyhow::Result<TestResult> {
let h_path = match self.h_path.as_ref() {
- None => self.tempdir.path().join("test_crate.rs"),
+ None => self.tempdir.path().join("test_crate_cc_api.h"),
Some(s) => PathBuf::from(s),
};
@@ -273,24 +281,28 @@
run_with_cmdline_args(&args)?;
- Ok(h_path)
+ Ok(TestResult { h_path, rs_input_path })
}
}
#[test]
fn test_happy_path() -> anyhow::Result<()> {
let test_args = TestArgs::default_args()?;
- let h_path = test_args.run().expect("Default args should succeed");
+ let test_result = test_args.run().expect("Default args should succeed");
- assert!(h_path.exists());
- let h_body = std::fs::read_to_string(&h_path)?;
+ assert!(test_result.h_path.exists());
+ let h_body = std::fs::read_to_string(&test_result.h_path)?;
+ let rs_input_path = test_result.rs_input_path.display().to_string();
assert_eq!(
h_body,
- "// Automatically @generated C++ bindings for the following Rust crate:\n\
- // test_crate\n\
- \n\
- // List of public functions:\n\
- // public_function\n"
+ format!(
+ "// Automatically @generated C++ bindings for the following Rust crate:\n\
+ // test_crate\n\
+ \n\
+ // Error while generating bindings for `public_function` \
+ defined at {rs_input_path}:1:2: 1:26: \
+ Nothing works yet!\n"
+ )
);
Ok(())
}
diff --git a/cc_bindings_from_rs/lib.rs b/cc_bindings_from_rs/lib.rs
index 482c60f..309ee36 100644
--- a/cc_bindings_from_rs/lib.rs
+++ b/cc_bindings_from_rs/lib.rs
@@ -2,32 +2,15 @@
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-use itertools::Itertools;
+use anyhow::{bail, Result};
use proc_macro2::TokenStream;
use quote::quote;
-use rustc_hir::{Item, ItemKind, Node};
use rustc_interface::Queries;
+use rustc_middle::dep_graph::DepContext;
use rustc_middle::middle::exported_symbols::ExportedSymbol;
use rustc_middle::ty::TyCtxt;
-use rustc_span::def_id::LOCAL_CRATE;
-
-// TODO(lukasza): Replace `get_names_of_exported_fns` with something that can
-// generate C++ bindings.
-fn get_names_of_exported_fns(tcx: TyCtxt) -> impl Iterator<Item = String> + '_ {
- tcx.exported_symbols(LOCAL_CRATE).iter().filter_map(move |(symbol, _)| match symbol {
- ExportedSymbol::NonGeneric(def_id) => {
- match tcx.hir().get_by_def_id(def_id.expect_local()) {
- Node::Item(Item { kind: ItemKind::Fn { .. }, .. }) => {
- Some(tcx.def_path_str(*def_id))
- }
- _ => None,
- }
- }
- ExportedSymbol::Generic(..) | ExportedSymbol::DropGlue(_) | ExportedSymbol::NoDefId(_) => {
- None
- }
- })
-}
+use rustc_span::def_id::{LocalDefId, LOCAL_CRATE};
+use std::fmt::Display;
pub struct GeneratedBindings {
pub h_body: TokenStream,
@@ -45,22 +28,16 @@
};
let h_body = {
- let comment_with_names_of_public_functions = format!(
- "List of public functions:\n{}",
- get_names_of_exported_fns(tcx).collect_vec().join(",\n")
- );
- quote! {
- #top_comment
- __COMMENT__ #comment_with_names_of_public_functions
- }
+ let main_content = format_crate(tcx);
+ quote! { #top_comment #main_content }
};
Self { h_body }
}
}
-/// Helper (used by `main` and `test::run_compiler`) for invoking functions
-/// operating on `TyCtxt`.
+/// Helper (used by `bindings_driver` and `test::run_compiler`) for invoking
+/// functions operating on `TyCtxt`.
pub fn enter_tcx<'tcx, F, T>(
queries: &'tcx Queries<'tcx>,
f: F,
@@ -73,12 +50,45 @@
Ok(query_context.peek_mut().enter(f))
}
+fn format_def(_tcx: TyCtxt, _def_id: LocalDefId) -> Result<TokenStream> {
+ bail!("Nothing works yet!")
+}
+
+fn format_unsupported_def(
+ tcx: TyCtxt,
+ local_def_id: LocalDefId,
+ err_msg: impl Display,
+) -> TokenStream {
+ let span = tcx.sess().source_map().span_to_embeddable_string(tcx.def_span(local_def_id));
+ let name = tcx.def_path_str(local_def_id.to_def_id());
+ let msg = format!("Error while generating bindings for `{name}` defined at {span}: {err_msg}");
+ quote! { __COMMENT__ #msg }
+}
+
+fn format_crate(tcx: TyCtxt) -> TokenStream {
+ let snippets =
+ tcx.exported_symbols(LOCAL_CRATE).iter().filter_map(move |(symbol, _)| match symbol {
+ ExportedSymbol::NonGeneric(def_id) => {
+ let def_id = def_id.expect_local(); // Exports are always from the local crate.
+ match format_def(tcx, def_id) {
+ Ok(snippet) => Some(snippet),
+ Err(err) => Some(format_unsupported_def(tcx, def_id, err)),
+ }
+ }
+ ExportedSymbol::Generic(def_id, ..) => {
+ let def_id = def_id.expect_local(); // Exports are always from the local crate.
+ Some(format_unsupported_def(tcx, def_id, "Generics are not supported yet."))
+ }
+ ExportedSymbol::DropGlue(_) | ExportedSymbol::NoDefId(_) => None,
+ });
+ quote! { #( #snippets )* }
+}
+
#[cfg(test)]
pub mod tests {
use super::GeneratedBindings;
use anyhow::Result;
- use itertools::Itertools;
use std::path::PathBuf;
use token_stream_printer::tokens_to_string;
@@ -93,23 +103,9 @@
}
#[test]
- fn test_get_names_of_exported_fns_public_vs_private() {
- let test_src = r#"
- pub fn public_function() {
- private_function()
- }
-
- fn private_function() {}
- "#;
- let exported_functions = get_names_of_exported_fns(test_src);
- assert_eq!(1, exported_functions.len());
- assert_eq!("public_function", exported_functions[0]);
- }
-
- #[test]
#[should_panic]
fn test_panic_when_syntax_errors_in_test_inputs() {
- get_names_of_exported_fns("syntax error here");
+ run_compiler("syntax error here", |_tcx| ())
}
#[test]
@@ -130,17 +126,14 @@
"// Automatically @generated C++ bindings for the following Rust crate:\n\
// rust_out\n\
\n\
- // List of public functions:\n\
- // public_function\n"
+ // Error while generating bindings for `public_function` \
+ defined at <crubit_unittests.rs>:2:17: 2:41: \
+ Nothing works yet!\n"
);
Ok(())
})
}
- fn get_names_of_exported_fns(source: &str) -> Vec<String> {
- run_compiler(source, |tcx| super::get_names_of_exported_fns(tcx).collect_vec())
- }
-
fn test_generated_bindings<F, T>(source: &str, f: F) -> T
where
F: FnOnce(GeneratedBindings) -> T + Send,