blob: 6226426e48f4da56e1541fbbb78d70fb06510b31 [file] [log] [blame]
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -07001// 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
Lukasz Anforowicz2293ef12022-09-29 13:37:05 -07005#![feature(never_type)]
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -07006#![feature(rustc_private)]
7#![deny(rustc::internal)]
8
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -07009extern crate rustc_driver;
10extern crate rustc_error_codes;
11extern crate rustc_errors;
Lukasz Anforowicz5bddf182022-09-30 16:06:59 -070012extern crate rustc_feature;
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -070013extern crate rustc_hir;
14extern crate rustc_interface;
Lukasz Anforowicz88bde9b2022-10-14 14:48:07 -070015extern crate rustc_lint_defs;
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -070016extern crate rustc_middle;
17extern crate rustc_session;
18extern crate rustc_span;
Lukasz Anforowicze4333062022-10-17 14:47:53 -070019extern crate rustc_target;
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -070020
Lukasz Anforowicz13794df2022-10-21 07:56:34 -070021// TODO(b/254679226): `bindings` and `cmdline` should be separate crates.
Lukasz Anforowicze7874b52022-09-30 16:52:28 -070022mod bindings;
Lukasz Anforowiczbf5a4ee2022-09-22 07:38:33 -070023mod cmdline;
Lukasz Anforowiczbf5a4ee2022-09-22 07:38:33 -070024
Lukasz Anforowicz1093f782022-09-28 12:45:10 -070025use anyhow::Context;
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -070026use itertools::Itertools;
Lukasz Anforowicz1093f782022-09-28 12:45:10 -070027use rustc_middle::ty::TyCtxt;
28use std::path::Path;
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -070029
Lukasz Anforowicze7874b52022-09-30 16:52:28 -070030use bindings::GeneratedBindings;
Lukasz Anforowicz1093f782022-09-28 12:45:10 -070031use cmdline::Cmdline;
Lukasz Anforowicz5c081b22022-11-11 08:40:17 -080032use token_stream_printer::{
33 cc_tokens_to_formatted_string, rs_tokens_to_formatted_string, RustfmtConfig,
34};
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -070035
Lukasz Anforowicz1093f782022-09-28 12:45:10 -070036/// This mostly wraps and simplifies a subset of APIs from the `rustc_driver`
37/// module.
Lukasz Anforowiczc3452842022-09-23 08:02:58 -070038mod bindings_driver {
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -070039
Lukasz Anforowicz76e26232022-10-18 17:26:28 -070040 use anyhow::anyhow;
Lukasz Anforowicz31ca0492022-09-28 15:55:23 -070041 use either::Either;
Lukasz Anforowiczbf5a4ee2022-09-22 07:38:33 -070042 use rustc_interface::interface::Compiler;
43 use rustc_interface::Queries;
Lukasz Anforowicz1093f782022-09-28 12:45:10 -070044 use rustc_middle::ty::TyCtxt;
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -070045
Lukasz Anforowicze7874b52022-09-30 16:52:28 -070046 use crate::bindings::enter_tcx;
Lukasz Anforowiczbf5a4ee2022-09-22 07:38:33 -070047
Lukasz Anforowicz1093f782022-09-28 12:45:10 -070048 /// Wrapper around `rustc_driver::RunCompiler::run` that exposes a
49 /// simplified API:
50 /// - Takes a `callback` that will be invoked from within Rust compiler,
51 /// after parsing and analysis are done,
Lukasz Anforowiczca87e472022-10-14 14:58:30 -070052 /// - Compilation will stop after parsing, analysis, and the `callback` are
Lukasz Anforowicz1093f782022-09-28 12:45:10 -070053 /// done,
54 /// - Returns the combined results from the Rust compiler *and* the
55 /// `callback`.
56 pub fn run_after_analysis_and_stop<F, R>(
57 rustc_args: &[String],
58 callback: F,
59 ) -> anyhow::Result<R>
60 where
61 F: FnOnce(TyCtxt) -> anyhow::Result<R> + Send,
62 R: Send,
63 {
64 AfterAnalysisCallback::new(rustc_args, callback).run()
65 }
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -070066
Lukasz Anforowicz1093f782022-09-28 12:45:10 -070067 struct AfterAnalysisCallback<'a, F, R>
68 where
69 F: FnOnce(TyCtxt) -> anyhow::Result<R> + Send,
70 R: Send,
71 {
72 args: &'a [String],
Lukasz Anforowicz31ca0492022-09-28 15:55:23 -070073 callback_or_result: Either<F, anyhow::Result<R>>,
Lukasz Anforowicz1093f782022-09-28 12:45:10 -070074 }
75
76 impl<'a, F, R> AfterAnalysisCallback<'a, F, R>
77 where
78 F: FnOnce(TyCtxt) -> anyhow::Result<R> + Send,
79 R: Send,
80 {
81 fn new(args: &'a [String], callback: F) -> Self {
Lukasz Anforowicz31ca0492022-09-28 15:55:23 -070082 Self { args, callback_or_result: Either::Left(callback) }
Lukasz Anforowiczbf5a4ee2022-09-22 07:38:33 -070083 }
84
Lukasz Anforowiczca87e472022-10-14 14:58:30 -070085 /// Runs Rust compiler, and then invokes the stored callback (with
86 /// `TyCtxt` of the parsed+analyzed Rust crate as the callback's
87 /// argument), and then finally returns the combined results
88 /// from Rust compiler *and* the callback.
Lukasz Anforowicz1093f782022-09-28 12:45:10 -070089 fn run(mut self) -> anyhow::Result<R> {
Lukasz Anforowiczc3452842022-09-23 08:02:58 -070090 // Rust compiler unwinds with a special sentinel value to abort compilation on
91 // fatal errors. We use `catch_fatal_errors` to 1) catch such panics and
92 // translate them into a Result, and 2) resume and propagate other panics.
Lukasz Anforowiczca87e472022-10-14 14:58:30 -070093 use rustc_interface::interface::Result;
94 let rustc_result: Result<Result<()>> = rustc_driver::catch_fatal_errors(|| {
Lukasz Anforowicz1093f782022-09-28 12:45:10 -070095 rustc_driver::RunCompiler::new(self.args, &mut self).run()
Lukasz Anforowiczc3452842022-09-23 08:02:58 -070096 });
97
Lukasz Anforowiczca87e472022-10-14 14:58:30 -070098 // Flatten `Result<Result<T, ...>>` into `Result<T, ...>` (i.e. combine the
99 // result from `RunCompiler::run` and `catch_fatal_errors`).
100 //
101 // TODO(lukasza): Use `Result::flatten` API when it gets stabilized. See also
102 // https://github.com/rust-lang/rust/issues/70142
103 let rustc_result: Result<()> = rustc_result.and_then(|result| result);
Lukasz Anforowiczc3452842022-09-23 08:02:58 -0700104
105 // Translate `rustc_interface::interface::Result` into `anyhow::Result`. (Can't
106 // use `?` because the trait `std::error::Error` is not implemented for
107 // `ErrorGuaranteed` which is required by the impl of
108 // `From<ErrorGuaranteed>` for `anyhow::Error`.)
Lukasz Anforowiczca87e472022-10-14 14:58:30 -0700109 let rustc_result: anyhow::Result<()> = rustc_result.map_err(|_err| {
Lukasz Anforowiczc3452842022-09-23 08:02:58 -0700110 // We can ignore `_err` because it has no payload / because this type has only
111 // one valid/possible value.
112 anyhow::format_err!("Errors reported by Rust compiler.")
113 });
114
Lukasz Anforowicz76e26232022-10-18 17:26:28 -0700115 // Return either `rustc_result` or `self.callback_result` or a new error.
Lukasz Anforowiczc3452842022-09-23 08:02:58 -0700116 rustc_result.and_then(|()| {
Lukasz Anforowicz76e26232022-10-18 17:26:28 -0700117 self.callback_or_result.right_or_else(|_left| {
118 // When rustc cmdline arguments (i.e. `self.args`) are empty (or contain
119 // `--help`) then the `after_analysis` callback won't be invoked. Handle
120 // this case by emitting an explicit error at the Crubit level.
121 Err(anyhow!("The Rust compiler had no crate to compile and analyze"))
122 })
Lukasz Anforowiczc3452842022-09-23 08:02:58 -0700123 })
Lukasz Anforowiczbf5a4ee2022-09-22 07:38:33 -0700124 }
125 }
126
Lukasz Anforowiczd0f0a842022-11-03 12:40:13 -0700127 impl<'a, F, R> rustc_driver::Callbacks for AfterAnalysisCallback<'a, F, R>
Lukasz Anforowicz1093f782022-09-28 12:45:10 -0700128 where
129 F: FnOnce(TyCtxt) -> anyhow::Result<R> + Send,
130 R: Send,
131 {
Lukasz Anforowiczbf5a4ee2022-09-22 07:38:33 -0700132 fn after_analysis<'tcx>(
133 &mut self,
134 _compiler: &Compiler,
135 queries: &'tcx Queries<'tcx>,
136 ) -> rustc_driver::Compilation {
Lukasz Anforowiczb8279db2022-09-22 07:40:55 -0700137 let rustc_result = enter_tcx(queries, |tcx| {
Lukasz Anforowicz31ca0492022-09-28 15:55:23 -0700138 let callback = {
139 let temporary_placeholder = Either::Right(Err(anyhow::anyhow!("unused")));
140 std::mem::replace(&mut self.callback_or_result, temporary_placeholder)
141 .left_or_else(|_| panic!("`after_analysis` should only run once"))
142 };
143 self.callback_or_result = Either::Right(callback(tcx));
Lukasz Anforowiczb8279db2022-09-22 07:40:55 -0700144 });
Lukasz Anforowiczbf5a4ee2022-09-22 07:38:33 -0700145
146 // `expect`ing no errors in `rustc_result`, because `after_analysis` is only
147 // called by `rustc_driver` if earlier compiler analysis was successful
148 // (which as the *last* compilation phase presumably covers *all*
149 // errors).
Lukasz Anforowiczb8279db2022-09-22 07:40:55 -0700150 rustc_result.expect("Expecting no compile errors inside `after_analysis` callback.");
Lukasz Anforowiczbf5a4ee2022-09-22 07:38:33 -0700151
152 rustc_driver::Compilation::Stop
153 }
154 }
Lukasz Anforowicz581fd752022-09-21 11:30:15 -0700155}
156
Lukasz Anforowicz1093f782022-09-28 12:45:10 -0700157fn write_file(path: &Path, content: &str) -> anyhow::Result<()> {
158 std::fs::write(path, content)
159 .with_context(|| format!("Error when writing to {}", path.display()))
160}
161
162fn run_with_tcx(cmdline: &Cmdline, tcx: TyCtxt) -> anyhow::Result<()> {
Lukasz Anforowicz24160d52022-10-19 06:45:45 -0700163 let bindings = GeneratedBindings::generate(tcx)?;
Lukasz Anforowicze7a25002022-11-10 06:21:42 -0800164
Lukasz Anforowicz5c081b22022-11-11 08:40:17 -0800165 {
166 let h_body = cc_tokens_to_formatted_string(bindings.h_body)?;
167 write_file(&cmdline.h_out, &h_body)?;
168 }
169
170 {
171 let rustfmt_config =
172 RustfmtConfig::new(&cmdline.rustfmt_exe_path, cmdline.rustfmt_config_path.as_deref());
173 let rs_body = rs_tokens_to_formatted_string(bindings.rs_body, &rustfmt_config)?;
174 write_file(&cmdline.rs_out, &rs_body)?;
175 }
Lukasz Anforowicze7a25002022-11-10 06:21:42 -0800176
177 Ok(())
Lukasz Anforowicz1093f782022-09-28 12:45:10 -0700178}
179
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700180/// Main entrypoint that (unlike `main`) doesn't do any intitializations that
181/// should only happen once for the binary (e.g. it doesn't call
182/// `install_ice_hook`) and therefore can be used from the tests module below.
183fn run_with_cmdline_args(args: &[String]) -> anyhow::Result<()> {
184 let cmdline = Cmdline::new(args)?;
Lukasz Anforowicza18aab82022-09-29 11:18:06 -0700185 bindings_driver::run_after_analysis_and_stop(&cmdline.rustc_args, |tcx| {
Lukasz Anforowicz1093f782022-09-28 12:45:10 -0700186 run_with_tcx(&cmdline, tcx)
187 })
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700188}
189
Lukasz Anforowicz581fd752022-09-21 11:30:15 -0700190fn main() -> anyhow::Result<()> {
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -0700191 rustc_driver::init_env_logger("CRUBIT_LOG");
192
193 // TODO: Investigate if we should install a signal handler here. See also how
194 // compiler/rustc_driver/src/lib.rs calls `signal_handler::install()`.
195
Lukasz Anforowicz13794df2022-10-21 07:56:34 -0700196 // TODO(b/254689400): Provide Crubit-specific panic hook message (we shouldn't use
Lukasz Anforowiczca87e472022-10-14 14:58:30 -0700197 // `rustc_driver::install_ice_hook` because it's message asks to file bugs at
198 // https://github.com/rust-lang/rust/issues/new.
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -0700199
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700200 // `std::env::args()` will panic if any of the cmdline arguments are not valid
201 // Unicode. This seems okay.
202 let args = std::env::args().collect_vec();
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -0700203
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700204 run_with_cmdline_args(&args)
Lukasz Anforowiczd1a00dc2022-09-29 13:21:51 -0700205 .map_err(|anyhow_err| match anyhow_err.downcast::<clap::Error>() {
206 // Explicitly call `clap::Error::exit`, because 1) it results in *colored* output and
207 // 2) it uses a zero exit code for specific "errors" (e.g. for `--help` output).
Lukasz Anforowiczd1a00dc2022-09-29 13:21:51 -0700208 Ok(clap_err) => {
Lukasz Anforowicz2293ef12022-09-29 13:37:05 -0700209 let _ : ! = clap_err.exit();
Lukasz Anforowiczd1a00dc2022-09-29 13:21:51 -0700210 },
211
212 // Return `other_err` from `main`. This will print the error message (no color codes
213 // though) and terminate the process with a non-zero exit code.
214 Err(other_err) => other_err,
215 })
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700216}
217
218#[cfg(test)]
219mod tests {
220 use super::run_with_cmdline_args;
221
Lukasz Anforowicze7874b52022-09-30 16:52:28 -0700222 use crate::bindings::tests::get_sysroot_for_testing;
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700223 use itertools::Itertools;
224 use std::path::PathBuf;
225 use tempfile::{tempdir, TempDir};
Lukasz Anforowicz5c081b22022-11-11 08:40:17 -0800226 use token_stream_printer::RUSTFMT_EXE_PATH_FOR_TESTING;
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700227
228 /// Test data builder (see also
229 /// https://testing.googleblog.com/2018/02/testing-on-toilet-cleanly-create-test.html).
230 struct TestArgs {
231 h_path: Option<String>,
232 extra_crubit_args: Vec<String>,
Lukasz Anforowicz24160d52022-10-19 06:45:45 -0700233
234 /// Arg for the following `rustc` flag: `--codegen=panic=<arg>`.
235 panic_mechanism: String,
236
237 /// Other `rustc` flags.
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700238 extra_rustc_args: Vec<String>,
Lukasz Anforowicz24160d52022-10-19 06:45:45 -0700239
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700240 tempdir: TempDir,
241 }
242
Lukasz Anforowiczed1c4f02022-09-29 11:11:20 -0700243 /// Result of `TestArgs::run` that helps tests access test outputs (e.g. the
244 /// internally generated `h_path` and/or `rs_input_path`).
245 #[derive(Debug)]
246 struct TestResult {
247 h_path: PathBuf,
Lukasz Anforowicze7a25002022-11-10 06:21:42 -0800248 rs_path: PathBuf,
Lukasz Anforowiczed1c4f02022-09-29 11:11:20 -0700249 }
250
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700251 impl TestArgs {
252 fn default_args() -> anyhow::Result<Self> {
253 Ok(Self {
254 h_path: None,
255 extra_crubit_args: vec![],
Lukasz Anforowicz24160d52022-10-19 06:45:45 -0700256 panic_mechanism: "abort".to_string(),
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700257 extra_rustc_args: vec![],
258 tempdir: tempdir()?,
259 })
260 }
261
262 /// Use the specified `h_path` rather than auto-generating one in
263 /// `self`-managed temporary directory.
264 fn with_h_path(mut self, h_path: &str) -> Self {
265 self.h_path = Some(h_path.to_string());
266 self
267 }
268
Lukasz Anforowicz24160d52022-10-19 06:45:45 -0700269 /// Replaces the default `--codegen=panic=abort` with the specified
270 /// `panic_mechanism`.
271 fn with_panic_mechanism(mut self, panic_mechanism: &str) -> Self {
272 self.panic_mechanism = panic_mechanism.to_string();
273 self
274 }
275
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700276 /// Appends `extra_rustc_args` at the end of the cmdline (i.e. as
277 /// additional rustc args, in addition to `--sysroot`,
278 /// `--crate-type=...`, etc.).
279 fn with_extra_rustc_args<T>(mut self, extra_rustc_args: T) -> Self
280 where
281 T: IntoIterator,
282 T::Item: Into<String>,
283 {
284 self.extra_rustc_args = extra_rustc_args.into_iter().map(|t| t.into()).collect_vec();
285 self
286 }
287
288 /// Appends `extra_crubit_args` before the first `--`.
289 fn with_extra_crubit_args<T>(mut self, extra_crubit_args: T) -> Self
290 where
291 T: IntoIterator,
292 T::Item: Into<String>,
293 {
294 self.extra_crubit_args = extra_crubit_args.into_iter().map(|t| t.into()).collect_vec();
295 self
296 }
297
298 /// Invokes `super::run_with_cmdline_args` with default `test_crate.rs`
299 /// input (and with other default args + args gathered by
300 /// `self`).
301 ///
302 /// Returns the path to the `h_out` file. The file's lifetime is the
303 /// same as `&self`.
Lukasz Anforowiczed1c4f02022-09-29 11:11:20 -0700304 fn run(&self) -> anyhow::Result<TestResult> {
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700305 let h_path = match self.h_path.as_ref() {
Lukasz Anforowiczed1c4f02022-09-29 11:11:20 -0700306 None => self.tempdir.path().join("test_crate_cc_api.h"),
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700307 Some(s) => PathBuf::from(s),
308 };
Lukasz Anforowicze7a25002022-11-10 06:21:42 -0800309 let rs_path = self.tempdir.path().join("test_crate_cc_api_impl.rs");
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700310
311 let rs_input_path = self.tempdir.path().join("test_crate.rs");
312 std::fs::write(
313 &rs_input_path,
Lukasz Anforowicz7123f212022-10-20 08:51:04 -0700314 r#" #[no_mangle]
315 pub extern "C" fn public_function() {
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700316 private_function()
317 }
318
319 fn private_function() {}
320 "#,
321 )?;
322
323 let mut args = vec![
324 "cc_bindings_from_rs_unittest_executable".to_string(),
Lukasz Anforowicza18aab82022-09-29 11:18:06 -0700325 format!("--h-out={}", h_path.display()),
Lukasz Anforowicze7a25002022-11-10 06:21:42 -0800326 format!("--rs-out={}", rs_path.display()),
Lukasz Anforowicz5c081b22022-11-11 08:40:17 -0800327 format!("--rustfmt-exe-path={RUSTFMT_EXE_PATH_FOR_TESTING}"),
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700328 ];
329 args.extend(self.extra_crubit_args.iter().cloned());
330 args.extend([
331 "--".to_string(),
Lukasz Anforowicz24160d52022-10-19 06:45:45 -0700332 format!("--codegen=panic={}", &self.panic_mechanism),
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700333 "--crate-type=lib".to_string(),
334 format!("--sysroot={}", get_sysroot_for_testing().display()),
335 rs_input_path.display().to_string(),
336 ]);
337 args.extend(self.extra_rustc_args.iter().cloned());
338
339 run_with_cmdline_args(&args)?;
340
Lukasz Anforowicze7a25002022-11-10 06:21:42 -0800341 Ok(TestResult { h_path, rs_path })
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700342 }
343 }
344
345 #[test]
346 fn test_happy_path() -> anyhow::Result<()> {
347 let test_args = TestArgs::default_args()?;
Lukasz Anforowiczed1c4f02022-09-29 11:11:20 -0700348 let test_result = test_args.run().expect("Default args should succeed");
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700349
Lukasz Anforowiczed1c4f02022-09-29 11:11:20 -0700350 assert!(test_result.h_path.exists());
351 let h_body = std::fs::read_to_string(&test_result.h_path)?;
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700352 assert_eq!(
353 h_body,
Lukasz Anforowicz4aa1ff92022-10-10 11:22:22 -0700354r#"// Automatically @generated C++ bindings for the following Rust crate:
355// test_crate
356
Lukasz Anforowicz31b29cd2022-10-10 11:33:41 -0700357#pragma once
358
Lukasz Anforowicze4333062022-10-17 14:47:53 -0700359namespace test_crate {
360extern "C" void public_function();
361}"#
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700362 );
Lukasz Anforowicze7a25002022-11-10 06:21:42 -0800363
364 assert!(test_result.rs_path.exists());
365 let rs_body = std::fs::read_to_string(&test_result.rs_path)?;
366 assert_eq!(
367 rs_body,
368 r#"// Automatically @generated C++ bindings for the following Rust crate:
369// test_crate
370"#
371 );
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700372 Ok(())
373 }
374
375 #[test]
376 fn test_cmdline_error_propagation() -> anyhow::Result<()> {
377 // Tests that errors from `Cmdline::new` get propagated. Broader coverage of
378 // various error types can be found in tests in `cmdline.rs`.
379 let err = TestArgs::default_args()?
380 .with_extra_crubit_args(["--unrecognized-crubit-flag"])
381 .run()
382 .expect_err("--unrecognized_crubit_flag should trigger an error");
383
Lukasz Anforowiczca87e472022-10-14 14:58:30 -0700384 let msg = format!("{err:#}");
Lukasz Anforowicza18aab82022-09-29 11:18:06 -0700385 assert!(
386 msg.contains("Found argument '--unrecognized-crubit-flag' which wasn't expected"),
387 "msg = {}",
388 msg,
389 );
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700390 Ok(())
391 }
392
393 #[test]
394 fn test_rustc_error_propagation() -> anyhow::Result<()> {
395 // Tests that `rustc` errors are propagated.
396 let err = TestArgs::default_args()?
397 .with_extra_rustc_args(["--unrecognized-rustc-flag"])
398 .run()
399 .expect_err("--unrecognized-rustc-flag should trigger an error");
400
Lukasz Anforowiczca87e472022-10-14 14:58:30 -0700401 let msg = format!("{err:#}");
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700402 assert_eq!("Errors reported by Rust compiler.", msg);
403 Ok(())
404 }
405
406 #[test]
Lukasz Anforowicz76e26232022-10-18 17:26:28 -0700407 fn test_rustc_help() -> anyhow::Result<()> {
408 // Tests that we gracefully handle scenarios where `rustc` doesn't compile
409 // anything (e.g. when there are no rustc cmdline arguments, or when
410 // `--help` is present).
411 let err = TestArgs::default_args()?
412 .with_extra_rustc_args(["--help"])
413 .run()
414 .expect_err("--help passed to rustc should trigger Crubit-level error");
415
416 let msg = format!("{err:#}");
417 assert_eq!("The Rust compiler had no crate to compile and analyze", msg);
418 Ok(())
419 }
420
421 #[test]
Lukasz Anforowicz24160d52022-10-19 06:45:45 -0700422 fn test_rustc_unsupported_panic_mechanism() -> anyhow::Result<()> {
423 // Tests that `panic=unwind` results in an error.
424 let err = TestArgs::default_args()?
425 .with_panic_mechanism("unwind")
426 .run()
427 .expect_err("panic=unwind should trigger an error");
428
429 let msg = format!("{err:#}");
430 assert_eq!("No support for panic=unwind strategy (b/254049425)", msg);
431 Ok(())
432 }
433
434 #[test]
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700435 fn test_invalid_h_out_path() -> anyhow::Result<()> {
Lukasz Anforowicza18aab82022-09-29 11:18:06 -0700436 // Tests not only the specific problem of an invalid `--h-out` argument, but
Lukasz Anforowiczca87e472022-10-14 14:58:30 -0700437 // also tests that errors from `run_with_tcx` are propagated.
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700438 let err = TestArgs::default_args()?
439 .with_h_path("../..")
440 .run()
Lukasz Anforowicza18aab82022-09-29 11:18:06 -0700441 .expect_err("Unwriteable --h-out should trigger an error");
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700442
Lukasz Anforowiczca87e472022-10-14 14:58:30 -0700443 let msg = format!("{err:#}");
444 assert_eq!("Error when writing to ../..: Is a directory (os error 21)", msg);
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -0700445 Ok(())
446 }
447
448 #[test]
449 fn test_no_output_file() -> anyhow::Result<()> {
450 // Tests that we stop the compilation midway.
451 let tmpdir = tempdir()?;
452 let out_path = tmpdir.path().join("unexpected_output.o");
453 TestArgs::default_args()?
454 .with_extra_rustc_args(vec!["-o", &out_path.display().to_string()])
455 .run()
456 .expect("No rustc or Crubit errors are expected in this test");
457
458 assert!(!out_path.exists());
459 Ok(())
460 }
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -0700461}