|
| 1 | +//! Test that users are able to inspec the MIR body of functions and types |
| 2 | +
|
| 3 | +#![feature(rustc_private)] |
| 4 | +#![feature(assert_matches)] |
| 5 | +#![feature(result_option_inspect)] |
| 6 | + |
| 7 | +mod sanity_checks; |
| 8 | + |
| 9 | +extern crate rustc_middle; |
| 10 | +extern crate rustc_smir; |
| 11 | + |
| 12 | +use rustc_middle::ty::TyCtxt; |
| 13 | +use rustc_smir::{rustc_internal, stable_mir}; |
| 14 | +use std::panic::{catch_unwind, AssertUnwindSafe}; |
| 15 | +use std::process::ExitCode; |
| 16 | + |
| 17 | +const CHECK_ARG: &str = "--check-smir"; |
| 18 | + |
| 19 | +type TestResult = Result<(), String>; |
| 20 | + |
| 21 | +/// This is a wrapper that can be used to replace rustc. |
| 22 | +/// |
| 23 | +/// Besides all supported rustc arguments, use `--check-smir` to run all the stable-mir checks. |
| 24 | +/// This allows us to use this tool in cargo projects to analyze the target crate only by running |
| 25 | +/// `cargo rustc --check-smir`. |
| 26 | +fn main() -> ExitCode { |
| 27 | + let mut check_smir = false; |
| 28 | + let args: Vec<_> = std::env::args() |
| 29 | + .filter(|arg| { |
| 30 | + let is_check_arg = arg == CHECK_ARG; |
| 31 | + check_smir |= is_check_arg; |
| 32 | + !is_check_arg |
| 33 | + }) |
| 34 | + .collect(); |
| 35 | + |
| 36 | + |
| 37 | + let callback = if check_smir { test_stable_mir } else { |_: TyCtxt| ExitCode::SUCCESS }; |
| 38 | + let result = rustc_internal::StableMir::new(args, callback).continue_compilation().run(); |
| 39 | + if let Ok(test_result) = result { |
| 40 | + test_result |
| 41 | + } else { |
| 42 | + ExitCode::FAILURE |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +macro_rules! run_tests { |
| 47 | + ($( $test:path ),+) => { |
| 48 | + [$({ |
| 49 | + run_test(stringify!($test), || { $test() }) |
| 50 | + },)+] |
| 51 | + }; |
| 52 | +} |
| 53 | + |
| 54 | +/// This function invoke other tests and process their results. |
| 55 | +/// Tests should avoid panic, |
| 56 | +fn test_stable_mir(tcx: TyCtxt<'_>) -> ExitCode { |
| 57 | + let results = run_tests![ |
| 58 | + sanity_checks::test_entry_fn, |
| 59 | + sanity_checks::test_all_fns, |
| 60 | + sanity_checks::test_traits, |
| 61 | + sanity_checks::test_crates |
| 62 | + ]; |
| 63 | + let (success, failure): (Vec<_>, Vec<_>) = results.iter().partition(|r| r.is_ok()); |
| 64 | + println!( |
| 65 | + "Ran {} tests. {} succeeded. {} failed", |
| 66 | + results.len(), |
| 67 | + success.len(), |
| 68 | + failure.len() |
| 69 | + ); |
| 70 | + if failure.is_empty() { |
| 71 | + ExitCode::SUCCESS |
| 72 | + } else { |
| 73 | + ExitCode::FAILURE |
| 74 | + } |
| 75 | +} |
| 76 | + |
| 77 | +fn run_test<F: FnOnce() -> TestResult>(name: &str, f: F) -> TestResult { |
| 78 | + let result = match catch_unwind(AssertUnwindSafe(f)) { |
| 79 | + Err(_) => Err("Panic: {}".to_string()), |
| 80 | + Ok(result) => result, |
| 81 | + }; |
| 82 | + println!( |
| 83 | + "Test {}: {}", |
| 84 | + name, |
| 85 | + result.as_ref().err().unwrap_or(&"Ok".to_string()) |
| 86 | + ); |
| 87 | + result |
| 88 | +} |
0 commit comments