Refactor `run_compiler` into a method of `CompilerCallbacks`. This CL also renames `callbacks::CompilerCallbacks` into `bindings_driver::RunCompiler` which better reflects what this type does (and the naming and API shape nicely parallels that of `rustc_driver::RunCompiler`). PiperOrigin-RevId: 476371475
diff --git a/cc_bindings_from_rs/main.rs b/cc_bindings_from_rs/main.rs index d297f12..cc2d36e 100644 --- a/cc_bindings_from_rs/main.rs +++ b/cc_bindings_from_rs/main.rs
@@ -24,7 +24,7 @@ use cmdline::Cmdline; use itertools::Itertools; -mod bindings_generation { +mod bindings_main { use anyhow::Context; use rustc_middle::ty::TyCtxt; @@ -53,8 +53,9 @@ /// Glue that enables the top-level `main() -> anyhow::Result<()>` to call into /// `fn main(cmdline: &Cmdline, tcx: TyCtxt) -> anyhow::Result<()>` in the -/// `bindings_generation` module. -mod callbacks { +/// `bindings_main` module. This mostly wraps and simplifies a subset of APIs +/// from the `rustc_driver` module. +mod bindings_driver { use rustc_interface::interface::Compiler; use rustc_interface::Queries; @@ -62,38 +63,74 @@ use crate::cmdline::Cmdline; use crate::lib::enter_tcx; - /// When passed to `rustc_driver::RunCompiler::run`, the `CompilerCallbacks` - /// below will wait until Rust compiler parsing and analysis are done, - /// and then will invoke `bindings_generation::main` and stash/expose - /// its result via `CompilerCallbacks::into_result`. - pub struct CompilerCallbacks<'a> { + /// Wrapper around `rustc_driver::RunCompiler` that exposes a simplified API + /// (e.g. doesn't take arbitrary `Callbacks` but always calls into + /// `bindings_main::main`). + pub struct RunCompiler<'a> { cmdline: &'a Cmdline, - result: Option<anyhow::Result<()>>, + bindings_main_result: Option<anyhow::Result<()>>, } - impl<'a> CompilerCallbacks<'a> { + impl<'a> RunCompiler<'a> { + /// Creates new Rust compiler runner that will + /// - pass `cmdline.rustc_args()` to the Rust compiler + /// - pass `cmdline` to `bindings_main::main` (in addition to passing + /// `TyCtxt` - see the doc comment of `RunCompiler::run` below). pub fn new(cmdline: &'a Cmdline) -> Self { - Self { cmdline, result: None } + Self { cmdline, bindings_main_result: None } } - pub fn into_result(self) -> anyhow::Result<()> { - assert!( - self.result.is_some(), - "CompilerCallbacks::run_main should have been called by now" - ); - self.result.unwrap() + /// Runs Rust compiler and then passes the `TyCtxt` of the + /// parsed+analyzed Rust crate into `bindings_main::main`. + /// Returns the combined results from Rust compiler *and* + /// `bindings_main::main`. + pub fn run(mut self) -> anyhow::Result<()> { + // Rust compiler unwinds with a special sentinel value to abort compilation on + // fatal errors. We use `catch_fatal_errors` to 1) catch such panics and + // translate them into a Result, and 2) resume and propagate other panics. + let rustc_result = rustc_driver::catch_fatal_errors(|| { + rustc_driver::RunCompiler::new(self.cmdline.rustc_args(), &mut self).run() + }); + + // Flatten `Result<Result<T, ...>>` into `Result<T, ...>` (i.e. get the Result + // from `RunCompiler::run` rather than the Result from + // `catch_fatal_errors`). + let rustc_result = rustc_result.and_then(|result| result); + + // Translate `rustc_interface::interface::Result` into `anyhow::Result`. (Can't + // use `?` because the trait `std::error::Error` is not implemented for + // `ErrorGuaranteed` which is required by the impl of + // `From<ErrorGuaranteed>` for `anyhow::Error`.) + let rustc_result = rustc_result.map_err(|_err| { + // We can ignore `_err` because it has no payload / because this type has only + // one valid/possible value. + anyhow::format_err!("Errors reported by Rust compiler.") + }); + + // Return either `rustc_result` or `self.bindings_main_result`. + rustc_result.and_then(|()| { + assert!( + self.bindings_main_result.is_some(), + "RunCompiler::run_main should have been called by now" + ); + self.bindings_main_result.unwrap() + }) } } - impl rustc_driver::Callbacks for CompilerCallbacks<'_> { + impl rustc_driver::Callbacks for RunCompiler<'_> { fn after_analysis<'tcx>( &mut self, _compiler: &Compiler, queries: &'tcx Queries<'tcx>, ) -> rustc_driver::Compilation { let rustc_result = enter_tcx(queries, |tcx| { - assert!(self.result.is_none(), "after_analysis should only run once"); - self.result = Some(crate::bindings_generation::main(self.cmdline, tcx)); + assert!( + self.bindings_main_result.is_none(), + "after_analysis should only run once" + ); + self.bindings_main_result = + Some(crate::bindings_main::main(self.cmdline, tcx)) }); // `expect`ing no errors in `rustc_result`, because `after_analysis` is only @@ -107,38 +144,9 @@ } } -/// Wrapper around `rustc_driver::RunCompiler::run` that returns -/// `anyhow::Result<()>` instead of either returning -/// `rustc_interface::interface::Result<()>` or panicking with a special -/// sentinel value. -fn run_compiler<T>(rustc_args: &[String], callbacks: &mut T) -> anyhow::Result<()> -where - T: rustc_driver::Callbacks + Send, -{ - // Rust compiler unwinds with a special sentinel value to abort compilation on - // fatal errors. We use `catch_fatal_errors` to 1) catch such panics and - // translate them into a Result, and 2) resume and propagate other panics. - let result = rustc_driver::catch_fatal_errors(|| { - rustc_driver::RunCompiler::new(rustc_args, callbacks).run() - }); - - // Flatten `Result<Result<T, ...>>` into `Result<T, ...>`. - let result = result.and_then(|result| result); - - // Translate `rustc_interface::interface::Result` into `anyhow::Result`. (Can't - // use `?` because the trait `std::error::Error` is not implemented for - // `ErrorGuaranteed` which is required by the impl of - // `From<ErrorGuaranteed>` for `anyhow::Error`.) - result.map_err(|_err| { - // We can ignore `_err` because it has no payload / because this type has only - // one valid/possible value. - anyhow::format_err!("Errors reported by Rust compiler.") - }) -} - // TODO(lukasza): Add end-to-end tests that verify that the exit code is // non-zero when: -// * input contains syntax errors - test coverage for `run_compiler`. +// * input contains syntax errors - test coverage for `RunCompiler::run`. // * mandatory parameters (e.g. `--h_out`) are missing - test coverage for how // `main` calls `Cmdline::new`. // * `--h_out` cannot be written to (in this case, the error message should @@ -160,8 +168,7 @@ Cmdline::new(&args)? }; - // Invoke the Rust compiler with Crubit-specific `callbacks`. - let mut callbacks = callbacks::CompilerCallbacks::new(&cmdline); - run_compiler(cmdline.rustc_args(), &mut callbacks)?; - callbacks.into_result() + // Invoke the Rust compiler and call `bindings_main::main` after parsing and + // analysis are done. + bindings_driver::RunCompiler::new(&cmdline).run() }