]> git.lizzy.rs Git - rust.git/blob - src/bin/miri-rustc-tests.rs
Auto merge of #1293 - Firstyear:1289-miri-readme, r=RalfJung
[rust.git] / src / bin / miri-rustc-tests.rs
1 #![feature(rustc_private)]
2
3 extern crate rustc_middle;
4 extern crate rustc_driver;
5 extern crate rustc_hir;
6 extern crate rustc_interface;
7 extern crate rustc_span;
8
9 use std::io;
10 use std::io::Write;
11 use std::path::Path;
12 use std::sync::{Arc, Mutex};
13
14 use rustc_middle::ty::TyCtxt;
15 use rustc_driver::Compilation;
16 use rustc_hir as hir;
17 use rustc_hir::def_id::LOCAL_CRATE;
18 use rustc_hir::itemlikevisit;
19 use rustc_interface::{interface, Queries};
20
21 struct MiriCompilerCalls {
22     /// whether we are building for the host
23     host_target: bool,
24 }
25
26 impl rustc_driver::Callbacks for MiriCompilerCalls {
27     fn after_analysis<'tcx>(
28         &mut self,
29         compiler: &interface::Compiler,
30         queries: &'tcx Queries<'tcx>,
31     ) -> Compilation {
32         compiler.session().abort_if_errors();
33         queries.global_ctxt().unwrap().peek_mut().enter(|tcx| {
34             if std::env::args().any(|arg| arg == "--test") {
35                 struct Visitor<'tcx>(TyCtxt<'tcx>);
36                 impl<'tcx, 'hir> itemlikevisit::ItemLikeVisitor<'hir> for Visitor<'tcx> {
37                     fn visit_item(&mut self, i: &'hir hir::Item) {
38                         if let hir::ItemKind::Fn(.., body_id) = i.kind {
39                             if i.attrs
40                                 .iter()
41                                 .any(|attr| attr.check_name(rustc_span::symbol::sym::test))
42                             {
43                                 let config = miri::MiriConfig::default();
44                                 let did = self.0.hir().body_owner_def_id(body_id);
45                                 println!("running test: {}", self.0.def_path_debug_str(did));
46                                 miri::eval_main(self.0, did, config);
47                                 self.0.sess.abort_if_errors();
48                             }
49                         }
50                     }
51                     fn visit_trait_item(&mut self, _trait_item: &'hir hir::TraitItem) {}
52                     fn visit_impl_item(&mut self, _impl_item: &'hir hir::ImplItem) {}
53                 }
54                 tcx.hir().krate().visit_all_item_likes(&mut Visitor(tcx));
55             } else if let Some((entry_def_id, _)) = tcx.entry_fn(LOCAL_CRATE) {
56                 let config = miri::MiriConfig::default();
57                 miri::eval_main(tcx, entry_def_id, config);
58
59                 compiler.session().abort_if_errors();
60             } else {
61                 println!("no main function found, assuming auxiliary build");
62             }
63         });
64
65         // Continue execution on host target
66         if self.host_target { Compilation::Continue } else { Compilation::Stop }
67     }
68 }
69
70 fn main() {
71     let path = option_env!("MIRI_RUSTC_TEST").map(String::from).unwrap_or_else(|| {
72         std::env::var("MIRI_RUSTC_TEST")
73             .expect("need to set MIRI_RUSTC_TEST to path of rustc tests")
74     });
75
76     let mut mir_not_found = Vec::new();
77     let mut crate_not_found = Vec::new();
78     let mut success = 0;
79     let mut failed = Vec::new();
80     let mut c_abi_fns = Vec::new();
81     let mut abi = Vec::new();
82     let mut unsupported = Vec::new();
83     let mut unimplemented_intrinsic = Vec::new();
84     let mut limits = Vec::new();
85     let mut files: Vec<_> = std::fs::read_dir(path).unwrap().collect();
86     while let Some(file) = files.pop() {
87         let file = file.unwrap();
88         let path = file.path();
89         if file.metadata().unwrap().is_dir() {
90             if !path.to_str().unwrap().ends_with("auxiliary") {
91                 // add subdirs recursively
92                 files.extend(std::fs::read_dir(path).unwrap());
93             }
94             continue;
95         }
96         if !file.metadata().unwrap().is_file() || !path.to_str().unwrap().ends_with(".rs") {
97             continue;
98         }
99         let stderr = std::io::stderr();
100         write!(stderr.lock(), "test [miri-pass] {} ... ", path.display()).unwrap();
101         let mut host_target = false;
102         let mut args: Vec<String> = std::env::args()
103             .filter(|arg| {
104                 if arg == "--miri_host_target" {
105                     host_target = true;
106                     false // remove the flag, rustc doesn't know it
107                 } else {
108                     true
109                 }
110             })
111             .collect();
112         args.splice(1..1, miri::miri_default_args().iter().map(ToString::to_string));
113         // file to process
114         args.push(path.display().to_string());
115
116         let sysroot_flag = String::from("--sysroot");
117         if !args.contains(&sysroot_flag) {
118             args.push(sysroot_flag);
119             args.push(
120                 Path::new(&std::env::var("HOME").unwrap())
121                     .join(".xargo")
122                     .join("HOST")
123                     .display()
124                     .to_string(),
125             );
126         }
127
128         // A threadsafe buffer for writing.
129         #[derive(Default, Clone)]
130         struct BufWriter(Arc<Mutex<Vec<u8>>>);
131
132         impl Write for BufWriter {
133             fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
134                 self.0.lock().unwrap().write(buf)
135             }
136             fn flush(&mut self) -> io::Result<()> {
137                 self.0.lock().unwrap().flush()
138             }
139         }
140         let buf = BufWriter::default();
141         let output = buf.clone();
142         let result = std::panic::catch_unwind(|| {
143             let _ = rustc_driver::run_compiler(
144                 &args,
145                 &mut MiriCompilerCalls { host_target },
146                 None,
147                 Some(Box::new(buf)),
148             );
149         });
150
151         match result {
152             Ok(()) => {
153                 success += 1;
154                 writeln!(stderr.lock(), "ok").unwrap()
155             }
156             Err(_) => {
157                 let output = output.0.lock().unwrap();
158                 let output_err = std::str::from_utf8(&output).unwrap();
159                 if let Some(text) = output_err.splitn(2, "no mir for `").nth(1) {
160                     let end = text.find('`').unwrap();
161                     mir_not_found.push(text[..end].to_string());
162                     writeln!(stderr.lock(), "NO MIR FOR `{}`", &text[..end]).unwrap();
163                 } else if let Some(text) = output_err.splitn(2, "can't find crate for `").nth(1) {
164                     let end = text.find('`').unwrap();
165                     crate_not_found.push(text[..end].to_string());
166                     writeln!(stderr.lock(), "CAN'T FIND CRATE FOR `{}`", &text[..end]).unwrap();
167                 } else {
168                     for text in output_err.split("error: ").skip(1) {
169                         let end = text.find('\n').unwrap_or(text.len());
170                         let c_abi = "can't call C ABI function: ";
171                         let unimplemented_intrinsic_s = "unimplemented intrinsic: ";
172                         let unsupported_s = "miri does not support ";
173                         let abi_s = "can't handle function with ";
174                         let limit_s = "reached the configured maximum ";
175                         if text.starts_with(c_abi) {
176                             c_abi_fns.push(text[c_abi.len()..end].to_string());
177                         } else if text.starts_with(unimplemented_intrinsic_s) {
178                             unimplemented_intrinsic
179                                 .push(text[unimplemented_intrinsic_s.len()..end].to_string());
180                         } else if text.starts_with(unsupported_s) {
181                             unsupported.push(text[unsupported_s.len()..end].to_string());
182                         } else if text.starts_with(abi_s) {
183                             abi.push(text[abi_s.len()..end].to_string());
184                         } else if text.starts_with(limit_s) {
185                             limits.push(text[limit_s.len()..end].to_string());
186                         } else if text.find("aborting").is_none() {
187                             failed.push(text[..end].to_string());
188                         }
189                     }
190                     writeln!(stderr.lock(), "stderr: \n {}", output_err).unwrap();
191                 }
192             }
193         }
194     }
195     let stderr = std::io::stderr();
196     let mut stderr = stderr.lock();
197     writeln!(
198         stderr,
199         "{} success, {} no mir, {} crate not found, {} failed, {} C fn, {} ABI, {} unsupported, {} intrinsic",
200         success,
201         mir_not_found.len(),
202         crate_not_found.len(),
203         failed.len(),
204         c_abi_fns.len(),
205         abi.len(),
206         unsupported.len(),
207         unimplemented_intrinsic.len()
208     )
209     .unwrap();
210     writeln!(stderr, "# The \"other reasons\" errors").unwrap();
211     writeln!(stderr, "(sorted, deduplicated)").unwrap();
212     print_vec(&mut stderr, failed);
213
214     writeln!(stderr, "# can't call C ABI function").unwrap();
215     print_vec(&mut stderr, c_abi_fns);
216
217     writeln!(stderr, "# unsupported ABI").unwrap();
218     print_vec(&mut stderr, abi);
219
220     writeln!(stderr, "# unsupported").unwrap();
221     print_vec(&mut stderr, unsupported);
222
223     writeln!(stderr, "# unimplemented intrinsics").unwrap();
224     print_vec(&mut stderr, unimplemented_intrinsic);
225
226     writeln!(stderr, "# mir not found").unwrap();
227     print_vec(&mut stderr, mir_not_found);
228
229     writeln!(stderr, "# crate not found").unwrap();
230     print_vec(&mut stderr, crate_not_found);
231 }
232
233 fn print_vec<W: std::io::Write>(stderr: &mut W, v: Vec<String>) {
234     writeln!(stderr, "```").unwrap();
235     for (n, s) in vec_to_hist(v).into_iter().rev() {
236         writeln!(stderr, "{:4} {}", n, s).unwrap();
237     }
238     writeln!(stderr, "```").unwrap();
239 }
240
241 fn vec_to_hist<T: PartialEq + Ord>(mut v: Vec<T>) -> Vec<(usize, T)> {
242     v.sort();
243     let mut v = v.into_iter();
244     let mut result = Vec::new();
245     let mut current = v.next();
246     'outer: while let Some(current_val) = current {
247         let mut n = 1;
248         for next in &mut v {
249             if next == current_val {
250                 n += 1;
251             } else {
252                 result.push((n, current_val));
253                 current = Some(next);
254                 continue 'outer;
255             }
256         }
257         result.push((n, current_val));
258         break;
259     }
260     result.sort();
261     result
262 }