]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/test.rs
Auto merge of #58180 - davidtwco:issue-58053, r=estebank
[rust.git] / src / librustdoc / test.rs
1 use errors::{self, FatalError};
2 use errors::emitter::ColorConfig;
3 use rustc_data_structures::sync::Lrc;
4 use rustc_lint;
5 use rustc_driver::{self, driver, target_features, Compilation};
6 use rustc_driver::driver::phase_2_configure_and_expand;
7 use rustc_metadata::cstore::CStore;
8 use rustc_metadata::dynamic_lib::DynamicLibrary;
9 use rustc::hir;
10 use rustc::hir::intravisit;
11 use rustc::session::{self, CompileIncomplete, config};
12 use rustc::session::config::{OutputType, OutputTypes, Externs, CodegenOptions};
13 use rustc::session::search_paths::{SearchPath, PathKind};
14 use syntax::ast;
15 use syntax::source_map::SourceMap;
16 use syntax::edition::Edition;
17 use syntax::feature_gate::UnstableFeatures;
18 use syntax::with_globals;
19 use syntax_pos::{BytePos, DUMMY_SP, Pos, Span, FileName};
20 use tempfile::Builder as TempFileBuilder;
21 use testing;
22
23 use std::env;
24 use std::ffi::OsString;
25 use std::io::prelude::*;
26 use std::io;
27 use std::path::PathBuf;
28 use std::panic::{self, AssertUnwindSafe};
29 use std::process::Command;
30 use std::str;
31 use std::sync::{Arc, Mutex};
32
33 use clean::Attributes;
34 use config::Options;
35 use html::markdown::{self, ErrorCodes, LangString};
36
37 #[derive(Clone, Default)]
38 pub struct TestOptions {
39     /// Whether to disable the default `extern crate my_crate;` when creating doctests.
40     pub no_crate_inject: bool,
41     /// Whether to emit compilation warnings when compiling doctests. Setting this will suppress
42     /// the default `#![allow(unused)]`.
43     pub display_warnings: bool,
44     /// Additional crate-level attributes to add to doctests.
45     pub attrs: Vec<String>,
46 }
47
48 pub fn run(mut options: Options) -> isize {
49     let input = config::Input::File(options.input.clone());
50
51     let sessopts = config::Options {
52         maybe_sysroot: options.maybe_sysroot.clone().or_else(
53             || Some(env::current_exe().unwrap().parent().unwrap().parent().unwrap().to_path_buf())),
54         search_paths: options.libs.clone(),
55         crate_types: vec![config::CrateType::Dylib],
56         cg: options.codegen_options.clone(),
57         externs: options.externs.clone(),
58         unstable_features: UnstableFeatures::from_environment(),
59         lint_cap: Some(::rustc::lint::Level::Allow),
60         actually_rustdoc: true,
61         debugging_opts: config::DebuggingOptions {
62             ..config::basic_debugging_options()
63         },
64         edition: options.edition,
65         ..config::Options::default()
66     };
67     driver::spawn_thread_pool(sessopts, |sessopts| {
68         let source_map = Lrc::new(SourceMap::new(sessopts.file_path_mapping()));
69         let handler =
70             errors::Handler::with_tty_emitter(ColorConfig::Auto,
71                                             true, false,
72                                             Some(source_map.clone()));
73
74         let mut sess = session::build_session_(
75             sessopts, Some(options.input), handler, source_map.clone(),
76         );
77         let codegen_backend = rustc_driver::get_codegen_backend(&sess);
78         let cstore = CStore::new(codegen_backend.metadata_loader());
79         rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
80
81         let mut cfg = config::build_configuration(&sess,
82                                                   config::parse_cfgspecs(options.cfgs.clone()));
83         target_features::add_configuration(&mut cfg, &sess, &*codegen_backend);
84         sess.parse_sess.config = cfg;
85
86         let krate =
87             match driver::phase_1_parse_input(&driver::CompileController::basic(), &sess, &input) {
88                 Ok(krate) => krate,
89                 Err(mut e) => {
90                     e.emit();
91                     FatalError.raise();
92                 }
93             };
94         let driver::ExpansionResult { defs, mut hir_forest, .. } = {
95             phase_2_configure_and_expand(
96                 &sess,
97                 &cstore,
98                 krate,
99                 None,
100                 "rustdoc-test",
101                 None,
102                 |_| Ok(()),
103             ).expect("phase_2_configure_and_expand aborted in rustdoc!")
104         };
105
106         let crate_name = options.crate_name.unwrap_or_else(|| {
107             ::rustc_codegen_utils::link::find_crate_name(None, &hir_forest.krate().attrs, &input)
108         });
109         let mut opts = scrape_test_config(hir_forest.krate());
110         opts.display_warnings |= options.display_warnings;
111         let mut collector = Collector::new(
112             crate_name,
113             options.cfgs,
114             options.libs,
115             options.codegen_options,
116             options.externs,
117             false,
118             opts,
119             options.maybe_sysroot,
120             Some(source_map),
121             None,
122             options.linker,
123             options.edition,
124             options.persist_doctests,
125         );
126
127         {
128             let map = hir::map::map_crate(&sess, &cstore, &mut hir_forest, &defs);
129             let krate = map.krate();
130             let mut hir_collector = HirCollector {
131                 sess: &sess,
132                 collector: &mut collector,
133                 map: &map,
134                 codes: ErrorCodes::from(sess.opts.unstable_features.is_nightly_build()),
135             };
136             hir_collector.visit_testable("".to_string(), &krate.attrs, |this| {
137                 intravisit::walk_crate(this, krate);
138             });
139         }
140
141         options.test_args.insert(0, "rustdoctest".to_string());
142
143         testing::test_main(&options.test_args,
144                         collector.tests.into_iter().collect(),
145                         testing::Options::new().display_output(options.display_warnings));
146         0
147     })
148 }
149
150 // Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade.
151 fn scrape_test_config(krate: &::rustc::hir::Crate) -> TestOptions {
152     use syntax::print::pprust;
153
154     let mut opts = TestOptions {
155         no_crate_inject: false,
156         display_warnings: false,
157         attrs: Vec::new(),
158     };
159
160     let test_attrs: Vec<_> = krate.attrs.iter()
161         .filter(|a| a.check_name("doc"))
162         .flat_map(|a| a.meta_item_list().unwrap_or_else(Vec::new))
163         .filter(|a| a.check_name("test"))
164         .collect();
165     let attrs = test_attrs.iter().flat_map(|a| a.meta_item_list().unwrap_or(&[]));
166
167     for attr in attrs {
168         if attr.check_name("no_crate_inject") {
169             opts.no_crate_inject = true;
170         }
171         if attr.check_name("attr") {
172             if let Some(l) = attr.meta_item_list() {
173                 for item in l {
174                     opts.attrs.push(pprust::meta_list_item_to_string(item));
175                 }
176             }
177         }
178     }
179
180     opts
181 }
182
183 fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize,
184             cfgs: Vec<String>, libs: Vec<SearchPath>,
185             cg: CodegenOptions, externs: Externs,
186             should_panic: bool, no_run: bool, as_test_harness: bool,
187             compile_fail: bool, mut error_codes: Vec<String>, opts: &TestOptions,
188             maybe_sysroot: Option<PathBuf>, linker: Option<PathBuf>, edition: Edition,
189             persist_doctests: Option<PathBuf>) {
190     // The test harness wants its own `main` and top-level functions, so
191     // never wrap the test in `fn main() { ... }`.
192     let (test, line_offset) = make_test(test, Some(cratename), as_test_harness, opts);
193     // FIXME(#44940): if doctests ever support path remapping, then this filename
194     // needs to be the result of `SourceMap::span_to_unmapped_path`.
195     let path = match filename {
196         FileName::Real(path) => path.clone(),
197         _ => PathBuf::from(r"doctest.rs"),
198     };
199
200     let input = config::Input::Str {
201         name: FileName::DocTest(path, line as isize - line_offset as isize),
202         input: test,
203     };
204     let outputs = OutputTypes::new(&[(OutputType::Exe, None)]);
205
206     let sessopts = config::Options {
207         maybe_sysroot: maybe_sysroot.or_else(
208             || Some(env::current_exe().unwrap().parent().unwrap().parent().unwrap().to_path_buf())),
209         search_paths: libs,
210         crate_types: vec![config::CrateType::Executable],
211         output_types: outputs,
212         externs,
213         cg: config::CodegenOptions {
214             linker,
215             ..cg
216         },
217         test: as_test_harness,
218         unstable_features: UnstableFeatures::from_environment(),
219         debugging_opts: config::DebuggingOptions {
220             ..config::basic_debugging_options()
221         },
222         edition,
223         ..config::Options::default()
224     };
225
226     // Shuffle around a few input and output handles here. We're going to pass
227     // an explicit handle into rustc to collect output messages, but we also
228     // want to catch the error message that rustc prints when it fails.
229     //
230     // We take our thread-local stderr (likely set by the test runner) and replace
231     // it with a sink that is also passed to rustc itself. When this function
232     // returns the output of the sink is copied onto the output of our own thread.
233     //
234     // The basic idea is to not use a default Handler for rustc, and then also
235     // not print things by default to the actual stderr.
236     struct Sink(Arc<Mutex<Vec<u8>>>);
237     impl Write for Sink {
238         fn write(&mut self, data: &[u8]) -> io::Result<usize> {
239             Write::write(&mut *self.0.lock().unwrap(), data)
240         }
241         fn flush(&mut self) -> io::Result<()> { Ok(()) }
242     }
243     struct Bomb(Arc<Mutex<Vec<u8>>>, Box<dyn Write+Send>);
244     impl Drop for Bomb {
245         fn drop(&mut self) {
246             let _ = self.1.write_all(&self.0.lock().unwrap());
247         }
248     }
249     let data = Arc::new(Mutex::new(Vec::new()));
250
251     let old = io::set_panic(Some(box Sink(data.clone())));
252     let _bomb = Bomb(data.clone(), old.unwrap_or(box io::stdout()));
253
254     enum DirState {
255         Temp(tempfile::TempDir),
256         Perm(PathBuf),
257     }
258
259     impl DirState {
260         fn path(&self) -> &std::path::Path {
261             match self {
262                 DirState::Temp(t) => t.path(),
263                 DirState::Perm(p) => p.as_path(),
264             }
265         }
266     }
267
268     let (libdir, outdir, compile_result) = driver::spawn_thread_pool(sessopts, |sessopts| {
269         let source_map = Lrc::new(SourceMap::new(sessopts.file_path_mapping()));
270         let emitter = errors::emitter::EmitterWriter::new(box Sink(data.clone()),
271                                                         Some(source_map.clone()),
272                                                         false,
273                                                         false);
274
275         // Compile the code
276         let diagnostic_handler = errors::Handler::with_emitter(true, false, box emitter);
277
278         let mut sess = session::build_session_(
279             sessopts, None, diagnostic_handler, source_map,
280         );
281         let codegen_backend = rustc_driver::get_codegen_backend(&sess);
282         let cstore = CStore::new(codegen_backend.metadata_loader());
283         rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
284
285         let outdir = Mutex::new(
286             if let Some(mut path) = persist_doctests {
287                 path.push(format!("{}_{}",
288                     filename
289                         .to_string()
290                         .rsplit('/')
291                         .next()
292                         .unwrap()
293                         .replace(".", "_"),
294                         line)
295                 );
296                 std::fs::create_dir_all(&path)
297                     .expect("Couldn't create directory for doctest executables");
298
299                 DirState::Perm(path)
300             } else {
301                 DirState::Temp(TempFileBuilder::new()
302                                 .prefix("rustdoctest")
303                                 .tempdir()
304                                 .expect("rustdoc needs a tempdir"))
305             }
306         );
307         let libdir = sess.target_filesearch(PathKind::All).get_lib_path();
308         let mut control = driver::CompileController::basic();
309
310         let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs.clone()));
311         target_features::add_configuration(&mut cfg, &sess, &*codegen_backend);
312         sess.parse_sess.config = cfg;
313
314         let out = Some(outdir.lock().unwrap().path().join("rust_out"));
315
316         if no_run {
317             control.after_analysis.stop = Compilation::Stop;
318         }
319
320         let res = panic::catch_unwind(AssertUnwindSafe(|| {
321             driver::compile_input(
322                 codegen_backend,
323                 &sess,
324                 &cstore,
325                 &None,
326                 &input,
327                 &None,
328                 &out,
329                 None,
330                 &control
331             )
332         }));
333
334         let compile_result = match res {
335             Ok(Ok(())) | Ok(Err(CompileIncomplete::Stopped)) => Ok(()),
336             Err(_) | Ok(Err(CompileIncomplete::Errored(_))) => Err(())
337         };
338
339         (libdir, outdir, compile_result)
340     });
341
342     match (compile_result, compile_fail) {
343         (Ok(()), true) => {
344             panic!("test compiled while it wasn't supposed to")
345         }
346         (Ok(()), false) => {}
347         (Err(()), true) => {
348             if error_codes.len() > 0 {
349                 let out = String::from_utf8(data.lock().unwrap().to_vec()).unwrap();
350                 error_codes.retain(|err| !out.contains(err));
351             }
352         }
353         (Err(()), false) => {
354             panic!("couldn't compile the test")
355         }
356     }
357
358     if error_codes.len() > 0 {
359         panic!("Some expected error codes were not found: {:?}", error_codes);
360     }
361
362     if no_run { return }
363
364     // Run the code!
365     //
366     // We're careful to prepend the *target* dylib search path to the child's
367     // environment to ensure that the target loads the right libraries at
368     // runtime. It would be a sad day if the *host* libraries were loaded as a
369     // mistake.
370     let mut cmd = Command::new(&outdir.lock().unwrap().path().join("rust_out"));
371     let var = DynamicLibrary::envvar();
372     let newpath = {
373         let path = env::var_os(var).unwrap_or(OsString::new());
374         let mut path = env::split_paths(&path).collect::<Vec<_>>();
375         path.insert(0, libdir);
376         env::join_paths(path).unwrap()
377     };
378     cmd.env(var, &newpath);
379
380     match cmd.output() {
381         Err(e) => panic!("couldn't run the test: {}{}", e,
382                         if e.kind() == io::ErrorKind::PermissionDenied {
383                             " - maybe your tempdir is mounted with noexec?"
384                         } else { "" }),
385         Ok(out) => {
386             if should_panic && out.status.success() {
387                 panic!("test executable succeeded when it should have failed");
388             } else if !should_panic && !out.status.success() {
389                 panic!("test executable failed:\n{}\n{}\n",
390                        str::from_utf8(&out.stdout).unwrap_or(""),
391                        str::from_utf8(&out.stderr).unwrap_or(""));
392             }
393         }
394     }
395 }
396
397 /// Makes the test file. Also returns the number of lines before the code begins
398 pub fn make_test(s: &str,
399                  cratename: Option<&str>,
400                  dont_insert_main: bool,
401                  opts: &TestOptions)
402                  -> (String, usize) {
403     let (crate_attrs, everything_else, crates) = partition_source(s);
404     let everything_else = everything_else.trim();
405     let mut line_offset = 0;
406     let mut prog = String::new();
407
408     if opts.attrs.is_empty() && !opts.display_warnings {
409         // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some
410         // lints that are commonly triggered in doctests. The crate-level test attributes are
411         // commonly used to make tests fail in case they trigger warnings, so having this there in
412         // that case may cause some tests to pass when they shouldn't have.
413         prog.push_str("#![allow(unused)]\n");
414         line_offset += 1;
415     }
416
417     // Next, any attributes that came from the crate root via #![doc(test(attr(...)))].
418     for attr in &opts.attrs {
419         prog.push_str(&format!("#![{}]\n", attr));
420         line_offset += 1;
421     }
422
423     // Now push any outer attributes from the example, assuming they
424     // are intended to be crate attributes.
425     prog.push_str(&crate_attrs);
426     prog.push_str(&crates);
427
428     // Uses libsyntax to parse the doctest and find if there's a main fn and the extern
429     // crate already is included.
430     let (already_has_main, already_has_extern_crate, found_macro) = crate::syntax::with_globals(|| {
431         use crate::syntax::{ast, parse::{self, ParseSess}, source_map::FilePathMapping};
432         use crate::syntax_pos::FileName;
433         use errors::emitter::EmitterWriter;
434         use errors::Handler;
435
436         let filename = FileName::anon_source_code(s);
437         let source = crates + &everything_else;
438
439         // Any errors in parsing should also appear when the doctest is compiled for real, so just
440         // send all the errors that libsyntax emits directly into a `Sink` instead of stderr.
441         let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
442         let emitter = EmitterWriter::new(box io::sink(), None, false, false);
443         let handler = Handler::with_emitter(false, false, box emitter);
444         let sess = ParseSess::with_span_handler(handler, cm);
445
446         let mut found_main = false;
447         let mut found_extern_crate = cratename.is_none();
448         let mut found_macro = false;
449
450         let mut parser = match parse::maybe_new_parser_from_source_str(&sess, filename, source) {
451             Ok(p) => p,
452             Err(errs) => {
453                 for mut err in errs {
454                     err.cancel();
455                 }
456
457                 return (found_main, found_extern_crate, found_macro);
458             }
459         };
460
461         loop {
462             match parser.parse_item() {
463                 Ok(Some(item)) => {
464                     if !found_main {
465                         if let ast::ItemKind::Fn(..) = item.node {
466                             if item.ident.as_str() == "main" {
467                                 found_main = true;
468                             }
469                         }
470                     }
471
472                     if !found_extern_crate {
473                         if let ast::ItemKind::ExternCrate(original) = item.node {
474                             // This code will never be reached if `cratename` is none because
475                             // `found_extern_crate` is initialized to `true` if it is none.
476                             let cratename = cratename.unwrap();
477
478                             match original {
479                                 Some(name) => found_extern_crate = name.as_str() == cratename,
480                                 None => found_extern_crate = item.ident.as_str() == cratename,
481                             }
482                         }
483                     }
484
485                     if !found_macro {
486                         if let ast::ItemKind::Mac(..) = item.node {
487                             found_macro = true;
488                         }
489                     }
490
491                     if found_main && found_extern_crate {
492                         break;
493                     }
494                 }
495                 Ok(None) => break,
496                 Err(mut e) => {
497                     e.cancel();
498                     break;
499                 }
500             }
501         }
502
503         (found_main, found_extern_crate, found_macro)
504     });
505
506     // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't
507     // see it. In that case, run the old text-based scan to see if they at least have a main
508     // function written inside a macro invocation. See
509     // https://github.com/rust-lang/rust/issues/56898
510     let already_has_main = if found_macro && !already_has_main {
511         s.lines()
512             .map(|line| {
513                 let comment = line.find("//");
514                 if let Some(comment_begins) = comment {
515                     &line[0..comment_begins]
516                 } else {
517                     line
518                 }
519             })
520             .any(|code| code.contains("fn main"))
521     } else {
522         already_has_main
523     };
524
525     // Don't inject `extern crate std` because it's already injected by the
526     // compiler.
527     if !already_has_extern_crate && !opts.no_crate_inject && cratename != Some("std") {
528         if let Some(cratename) = cratename {
529             // Make sure its actually used if not included.
530             if s.contains(cratename) {
531                 prog.push_str(&format!("extern crate {};\n", cratename));
532                 line_offset += 1;
533             }
534         }
535     }
536
537     if dont_insert_main || already_has_main {
538         prog.push_str(everything_else);
539     } else {
540         prog.push_str("fn main() {\n");
541         line_offset += 1;
542         prog.push_str(everything_else);
543         prog.push_str("\n}");
544     }
545
546     debug!("final doctest:\n{}", prog);
547
548     (prog, line_offset)
549 }
550
551 // FIXME(aburka): use a real parser to deal with multiline attributes
552 fn partition_source(s: &str) -> (String, String, String) {
553     #[derive(Copy, Clone, PartialEq)]
554     enum PartitionState {
555         Attrs,
556         Crates,
557         Other,
558     }
559     let mut state = PartitionState::Attrs;
560     let mut before = String::new();
561     let mut crates = String::new();
562     let mut after = String::new();
563
564     for line in s.lines() {
565         let trimline = line.trim();
566
567         // FIXME(misdreavus): if a doc comment is placed on an extern crate statement, it will be
568         // shunted into "everything else"
569         match state {
570             PartitionState::Attrs => {
571                 state = if trimline.starts_with("#![") ||
572                     trimline.chars().all(|c| c.is_whitespace()) ||
573                     (trimline.starts_with("//") && !trimline.starts_with("///"))
574                 {
575                     PartitionState::Attrs
576                 } else if trimline.starts_with("extern crate") ||
577                     trimline.starts_with("#[macro_use] extern crate")
578                 {
579                     PartitionState::Crates
580                 } else {
581                     PartitionState::Other
582                 };
583             }
584             PartitionState::Crates => {
585                 state = if trimline.starts_with("extern crate") ||
586                     trimline.starts_with("#[macro_use] extern crate") ||
587                     trimline.chars().all(|c| c.is_whitespace()) ||
588                     (trimline.starts_with("//") && !trimline.starts_with("///"))
589                 {
590                     PartitionState::Crates
591                 } else {
592                     PartitionState::Other
593                 };
594             }
595             PartitionState::Other => {}
596         }
597
598         match state {
599             PartitionState::Attrs => {
600                 before.push_str(line);
601                 before.push_str("\n");
602             }
603             PartitionState::Crates => {
604                 crates.push_str(line);
605                 crates.push_str("\n");
606             }
607             PartitionState::Other => {
608                 after.push_str(line);
609                 after.push_str("\n");
610             }
611         }
612     }
613
614     debug!("before:\n{}", before);
615     debug!("crates:\n{}", crates);
616     debug!("after:\n{}", after);
617
618     (before, after, crates)
619 }
620
621 pub trait Tester {
622     fn add_test(&mut self, test: String, config: LangString, line: usize);
623     fn get_line(&self) -> usize {
624         0
625     }
626     fn register_header(&mut self, _name: &str, _level: u32) {}
627 }
628
629 pub struct Collector {
630     pub tests: Vec<testing::TestDescAndFn>,
631
632     // The name of the test displayed to the user, separated by `::`.
633     //
634     // In tests from Rust source, this is the path to the item
635     // e.g., `["std", "vec", "Vec", "push"]`.
636     //
637     // In tests from a markdown file, this is the titles of all headers (h1~h6)
638     // of the sections that contain the code block, e.g., if the markdown file is
639     // written as:
640     //
641     // ``````markdown
642     // # Title
643     //
644     // ## Subtitle
645     //
646     // ```rust
647     // assert!(true);
648     // ```
649     // ``````
650     //
651     // the `names` vector of that test will be `["Title", "Subtitle"]`.
652     names: Vec<String>,
653
654     cfgs: Vec<String>,
655     libs: Vec<SearchPath>,
656     cg: CodegenOptions,
657     externs: Externs,
658     use_headers: bool,
659     cratename: String,
660     opts: TestOptions,
661     maybe_sysroot: Option<PathBuf>,
662     position: Span,
663     source_map: Option<Lrc<SourceMap>>,
664     filename: Option<PathBuf>,
665     linker: Option<PathBuf>,
666     edition: Edition,
667     persist_doctests: Option<PathBuf>,
668 }
669
670 impl Collector {
671     pub fn new(cratename: String, cfgs: Vec<String>, libs: Vec<SearchPath>, cg: CodegenOptions,
672                externs: Externs, use_headers: bool, opts: TestOptions,
673                maybe_sysroot: Option<PathBuf>, source_map: Option<Lrc<SourceMap>>,
674                filename: Option<PathBuf>, linker: Option<PathBuf>, edition: Edition,
675                persist_doctests: Option<PathBuf>) -> Collector {
676         Collector {
677             tests: Vec::new(),
678             names: Vec::new(),
679             cfgs,
680             libs,
681             cg,
682             externs,
683             use_headers,
684             cratename,
685             opts,
686             maybe_sysroot,
687             position: DUMMY_SP,
688             source_map,
689             filename,
690             linker,
691             edition,
692             persist_doctests,
693         }
694     }
695
696     fn generate_name(&self, line: usize, filename: &FileName) -> String {
697         format!("{} - {} (line {})", filename, self.names.join("::"), line)
698     }
699
700     pub fn set_position(&mut self, position: Span) {
701         self.position = position;
702     }
703
704     fn get_filename(&self) -> FileName {
705         if let Some(ref source_map) = self.source_map {
706             let filename = source_map.span_to_filename(self.position);
707             if let FileName::Real(ref filename) = filename {
708                 if let Ok(cur_dir) = env::current_dir() {
709                     if let Ok(path) = filename.strip_prefix(&cur_dir) {
710                         return path.to_owned().into();
711                     }
712                 }
713             }
714             filename
715         } else if let Some(ref filename) = self.filename {
716             filename.clone().into()
717         } else {
718             FileName::Custom("input".to_owned())
719         }
720     }
721 }
722
723 impl Tester for Collector {
724     fn add_test(&mut self, test: String, config: LangString, line: usize) {
725         let filename = self.get_filename();
726         let name = self.generate_name(line, &filename);
727         let cfgs = self.cfgs.clone();
728         let libs = self.libs.clone();
729         let cg = self.cg.clone();
730         let externs = self.externs.clone();
731         let cratename = self.cratename.to_string();
732         let opts = self.opts.clone();
733         let maybe_sysroot = self.maybe_sysroot.clone();
734         let linker = self.linker.clone();
735         let edition = config.edition.unwrap_or(self.edition);
736         let persist_doctests = self.persist_doctests.clone();
737
738         debug!("Creating test {}: {}", name, test);
739         self.tests.push(testing::TestDescAndFn {
740             desc: testing::TestDesc {
741                 name: testing::DynTestName(name.clone()),
742                 ignore: config.ignore,
743                 // compiler failures are test failures
744                 should_panic: testing::ShouldPanic::No,
745                 allow_fail: config.allow_fail,
746             },
747             testfn: testing::DynTestFn(box move || {
748                 let panic = io::set_panic(None);
749                 let print = io::set_print(None);
750                 match {
751                     rustc_driver::in_named_rustc_thread(name, move || with_globals(move || {
752                         io::set_panic(panic);
753                         io::set_print(print);
754                         run_test(&test,
755                                  &cratename,
756                                  &filename,
757                                  line,
758                                  cfgs,
759                                  libs,
760                                  cg,
761                                  externs,
762                                  config.should_panic,
763                                  config.no_run,
764                                  config.test_harness,
765                                  config.compile_fail,
766                                  config.error_codes,
767                                  &opts,
768                                  maybe_sysroot,
769                                  linker,
770                                  edition,
771                                  persist_doctests)
772                     }))
773                 } {
774                     Ok(()) => (),
775                     Err(err) => panic::resume_unwind(err),
776                 }
777             }),
778         });
779     }
780
781     fn get_line(&self) -> usize {
782         if let Some(ref source_map) = self.source_map {
783             let line = self.position.lo().to_usize();
784             let line = source_map.lookup_char_pos(BytePos(line as u32)).line;
785             if line > 0 { line - 1 } else { line }
786         } else {
787             0
788         }
789     }
790
791     fn register_header(&mut self, name: &str, level: u32) {
792         if self.use_headers {
793             // We use these headings as test names, so it's good if
794             // they're valid identifiers.
795             let name = name.chars().enumerate().map(|(i, c)| {
796                     if (i == 0 && c.is_xid_start()) ||
797                         (i != 0 && c.is_xid_continue()) {
798                         c
799                     } else {
800                         '_'
801                     }
802                 }).collect::<String>();
803
804             // Here we try to efficiently assemble the header titles into the
805             // test name in the form of `h1::h2::h3::h4::h5::h6`.
806             //
807             // Suppose that originally `self.names` contains `[h1, h2, h3]`...
808             let level = level as usize;
809             if level <= self.names.len() {
810                 // ... Consider `level == 2`. All headers in the lower levels
811                 // are irrelevant in this new level. So we should reset
812                 // `self.names` to contain headers until <h2>, and replace that
813                 // slot with the new name: `[h1, name]`.
814                 self.names.truncate(level);
815                 self.names[level - 1] = name;
816             } else {
817                 // ... On the other hand, consider `level == 5`. This means we
818                 // need to extend `self.names` to contain five headers. We fill
819                 // in the missing level (<h4>) with `_`. Thus `self.names` will
820                 // become `[h1, h2, h3, "_", name]`.
821                 if level - 1 > self.names.len() {
822                     self.names.resize(level - 1, "_".to_owned());
823                 }
824                 self.names.push(name);
825             }
826         }
827     }
828 }
829
830 struct HirCollector<'a, 'hir: 'a> {
831     sess: &'a session::Session,
832     collector: &'a mut Collector,
833     map: &'a hir::map::Map<'hir>,
834     codes: ErrorCodes,
835 }
836
837 impl<'a, 'hir> HirCollector<'a, 'hir> {
838     fn visit_testable<F: FnOnce(&mut Self)>(&mut self,
839                                             name: String,
840                                             attrs: &[ast::Attribute],
841                                             nested: F) {
842         let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs);
843         if let Some(ref cfg) = attrs.cfg {
844             if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) {
845                 return;
846             }
847         }
848
849         let has_name = !name.is_empty();
850         if has_name {
851             self.collector.names.push(name);
852         }
853
854         attrs.collapse_doc_comments();
855         attrs.unindent_doc_comments();
856         // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with
857         // anything else, this will combine them for us.
858         if let Some(doc) = attrs.collapsed_doc_value() {
859             self.collector.set_position(attrs.span.unwrap_or(DUMMY_SP));
860             let res = markdown::find_testable_code(&doc, self.collector, self.codes);
861             if let Err(err) = res {
862                 self.sess.diagnostic().span_warn(attrs.span.unwrap_or(DUMMY_SP),
863                     &err.to_string());
864             }
865         }
866
867         nested(self);
868
869         if has_name {
870             self.collector.names.pop();
871         }
872     }
873 }
874
875 impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> {
876     fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'hir> {
877         intravisit::NestedVisitorMap::All(&self.map)
878     }
879
880     fn visit_item(&mut self, item: &'hir hir::Item) {
881         let name = if let hir::ItemKind::Impl(.., ref ty, _) = item.node {
882             self.map.node_to_pretty_string(ty.id)
883         } else {
884             item.ident.to_string()
885         };
886
887         self.visit_testable(name, &item.attrs, |this| {
888             intravisit::walk_item(this, item);
889         });
890     }
891
892     fn visit_trait_item(&mut self, item: &'hir hir::TraitItem) {
893         self.visit_testable(item.ident.to_string(), &item.attrs, |this| {
894             intravisit::walk_trait_item(this, item);
895         });
896     }
897
898     fn visit_impl_item(&mut self, item: &'hir hir::ImplItem) {
899         self.visit_testable(item.ident.to_string(), &item.attrs, |this| {
900             intravisit::walk_impl_item(this, item);
901         });
902     }
903
904     fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem) {
905         self.visit_testable(item.ident.to_string(), &item.attrs, |this| {
906             intravisit::walk_foreign_item(this, item);
907         });
908     }
909
910     fn visit_variant(&mut self,
911                      v: &'hir hir::Variant,
912                      g: &'hir hir::Generics,
913                      item_id: ast::NodeId) {
914         self.visit_testable(v.node.ident.to_string(), &v.node.attrs, |this| {
915             intravisit::walk_variant(this, v, g, item_id);
916         });
917     }
918
919     fn visit_struct_field(&mut self, f: &'hir hir::StructField) {
920         self.visit_testable(f.ident.to_string(), &f.attrs, |this| {
921             intravisit::walk_struct_field(this, f);
922         });
923     }
924
925     fn visit_macro_def(&mut self, macro_def: &'hir hir::MacroDef) {
926         self.visit_testable(macro_def.name.to_string(), &macro_def.attrs, |_| ());
927     }
928 }
929
930 #[cfg(test)]
931 mod tests {
932     use super::{TestOptions, make_test};
933
934     #[test]
935     fn make_test_basic() {
936         //basic use: wraps with `fn main`, adds `#![allow(unused)]`
937         let opts = TestOptions::default();
938         let input =
939 "assert_eq!(2+2, 4);";
940         let expected =
941 "#![allow(unused)]
942 fn main() {
943 assert_eq!(2+2, 4);
944 }".to_string();
945         let output = make_test(input, None, false, &opts);
946         assert_eq!(output, (expected, 2));
947     }
948
949     #[test]
950     fn make_test_crate_name_no_use() {
951         // If you give a crate name but *don't* use it within the test, it won't bother inserting
952         // the `extern crate` statement.
953         let opts = TestOptions::default();
954         let input =
955 "assert_eq!(2+2, 4);";
956         let expected =
957 "#![allow(unused)]
958 fn main() {
959 assert_eq!(2+2, 4);
960 }".to_string();
961         let output = make_test(input, Some("asdf"), false, &opts);
962         assert_eq!(output, (expected, 2));
963     }
964
965     #[test]
966     fn make_test_crate_name() {
967         // If you give a crate name and use it within the test, it will insert an `extern crate`
968         // statement before `fn main`.
969         let opts = TestOptions::default();
970         let input =
971 "use asdf::qwop;
972 assert_eq!(2+2, 4);";
973         let expected =
974 "#![allow(unused)]
975 extern crate asdf;
976 fn main() {
977 use asdf::qwop;
978 assert_eq!(2+2, 4);
979 }".to_string();
980         let output = make_test(input, Some("asdf"), false, &opts);
981         assert_eq!(output, (expected, 3));
982     }
983
984     #[test]
985     fn make_test_no_crate_inject() {
986         // Even if you do use the crate within the test, setting `opts.no_crate_inject` will skip
987         // adding it anyway.
988         let opts = TestOptions {
989             no_crate_inject: true,
990             display_warnings: false,
991             attrs: vec![],
992         };
993         let input =
994 "use asdf::qwop;
995 assert_eq!(2+2, 4);";
996         let expected =
997 "#![allow(unused)]
998 fn main() {
999 use asdf::qwop;
1000 assert_eq!(2+2, 4);
1001 }".to_string();
1002         let output = make_test(input, Some("asdf"), false, &opts);
1003         assert_eq!(output, (expected, 2));
1004     }
1005
1006     #[test]
1007     fn make_test_ignore_std() {
1008         // Even if you include a crate name, and use it in the doctest, we still won't include an
1009         // `extern crate` statement if the crate is "std" -- that's included already by the
1010         // compiler!
1011         let opts = TestOptions::default();
1012         let input =
1013 "use std::*;
1014 assert_eq!(2+2, 4);";
1015         let expected =
1016 "#![allow(unused)]
1017 fn main() {
1018 use std::*;
1019 assert_eq!(2+2, 4);
1020 }".to_string();
1021         let output = make_test(input, Some("std"), false, &opts);
1022         assert_eq!(output, (expected, 2));
1023     }
1024
1025     #[test]
1026     fn make_test_manual_extern_crate() {
1027         // When you manually include an `extern crate` statement in your doctest, `make_test`
1028         // assumes you've included one for your own crate too.
1029         let opts = TestOptions::default();
1030         let input =
1031 "extern crate asdf;
1032 use asdf::qwop;
1033 assert_eq!(2+2, 4);";
1034         let expected =
1035 "#![allow(unused)]
1036 extern crate asdf;
1037 fn main() {
1038 use asdf::qwop;
1039 assert_eq!(2+2, 4);
1040 }".to_string();
1041         let output = make_test(input, Some("asdf"), false, &opts);
1042         assert_eq!(output, (expected, 2));
1043     }
1044
1045     #[test]
1046     fn make_test_manual_extern_crate_with_macro_use() {
1047         let opts = TestOptions::default();
1048         let input =
1049 "#[macro_use] extern crate asdf;
1050 use asdf::qwop;
1051 assert_eq!(2+2, 4);";
1052         let expected =
1053 "#![allow(unused)]
1054 #[macro_use] extern crate asdf;
1055 fn main() {
1056 use asdf::qwop;
1057 assert_eq!(2+2, 4);
1058 }".to_string();
1059         let output = make_test(input, Some("asdf"), false, &opts);
1060         assert_eq!(output, (expected, 2));
1061     }
1062
1063     #[test]
1064     fn make_test_opts_attrs() {
1065         // If you supplied some doctest attributes with `#![doc(test(attr(...)))]`, it will use
1066         // those instead of the stock `#![allow(unused)]`.
1067         let mut opts = TestOptions::default();
1068         opts.attrs.push("feature(sick_rad)".to_string());
1069         let input =
1070 "use asdf::qwop;
1071 assert_eq!(2+2, 4);";
1072         let expected =
1073 "#![feature(sick_rad)]
1074 extern crate asdf;
1075 fn main() {
1076 use asdf::qwop;
1077 assert_eq!(2+2, 4);
1078 }".to_string();
1079         let output = make_test(input, Some("asdf"), false, &opts);
1080         assert_eq!(output, (expected, 3));
1081
1082         // Adding more will also bump the returned line offset.
1083         opts.attrs.push("feature(hella_dope)".to_string());
1084         let expected =
1085 "#![feature(sick_rad)]
1086 #![feature(hella_dope)]
1087 extern crate asdf;
1088 fn main() {
1089 use asdf::qwop;
1090 assert_eq!(2+2, 4);
1091 }".to_string();
1092         let output = make_test(input, Some("asdf"), false, &opts);
1093         assert_eq!(output, (expected, 4));
1094     }
1095
1096     #[test]
1097     fn make_test_crate_attrs() {
1098         // Including inner attributes in your doctest will apply them to the whole "crate", pasting
1099         // them outside the generated main function.
1100         let opts = TestOptions::default();
1101         let input =
1102 "#![feature(sick_rad)]
1103 assert_eq!(2+2, 4);";
1104         let expected =
1105 "#![allow(unused)]
1106 #![feature(sick_rad)]
1107 fn main() {
1108 assert_eq!(2+2, 4);
1109 }".to_string();
1110         let output = make_test(input, None, false, &opts);
1111         assert_eq!(output, (expected, 2));
1112     }
1113
1114     #[test]
1115     fn make_test_with_main() {
1116         // Including your own `fn main` wrapper lets the test use it verbatim.
1117         let opts = TestOptions::default();
1118         let input =
1119 "fn main() {
1120     assert_eq!(2+2, 4);
1121 }";
1122         let expected =
1123 "#![allow(unused)]
1124 fn main() {
1125     assert_eq!(2+2, 4);
1126 }".to_string();
1127         let output = make_test(input, None, false, &opts);
1128         assert_eq!(output, (expected, 1));
1129     }
1130
1131     #[test]
1132     fn make_test_fake_main() {
1133         // ... but putting it in a comment will still provide a wrapper.
1134         let opts = TestOptions::default();
1135         let input =
1136 "//Ceci n'est pas une `fn main`
1137 assert_eq!(2+2, 4);";
1138         let expected =
1139 "#![allow(unused)]
1140 //Ceci n'est pas une `fn main`
1141 fn main() {
1142 assert_eq!(2+2, 4);
1143 }".to_string();
1144         let output = make_test(input, None, false, &opts);
1145         assert_eq!(output, (expected, 2));
1146     }
1147
1148     #[test]
1149     fn make_test_dont_insert_main() {
1150         // Even with that, if you set `dont_insert_main`, it won't create the `fn main` wrapper.
1151         let opts = TestOptions::default();
1152         let input =
1153 "//Ceci n'est pas une `fn main`
1154 assert_eq!(2+2, 4);";
1155         let expected =
1156 "#![allow(unused)]
1157 //Ceci n'est pas une `fn main`
1158 assert_eq!(2+2, 4);".to_string();
1159         let output = make_test(input, None, true, &opts);
1160         assert_eq!(output, (expected, 1));
1161     }
1162
1163     #[test]
1164     fn make_test_display_warnings() {
1165         // If the user is asking to display doctest warnings, suppress the default `allow(unused)`.
1166         let mut opts = TestOptions::default();
1167         opts.display_warnings = true;
1168         let input =
1169 "assert_eq!(2+2, 4);";
1170         let expected =
1171 "fn main() {
1172 assert_eq!(2+2, 4);
1173 }".to_string();
1174         let output = make_test(input, None, false, &opts);
1175         assert_eq!(output, (expected, 1));
1176     }
1177
1178     #[test]
1179     fn make_test_issues_21299_33731() {
1180         let opts = TestOptions::default();
1181
1182         let input =
1183 "// fn main
1184 assert_eq!(2+2, 4);";
1185
1186         let expected =
1187 "#![allow(unused)]
1188 // fn main
1189 fn main() {
1190 assert_eq!(2+2, 4);
1191 }".to_string();
1192
1193         let output = make_test(input, None, false, &opts);
1194         assert_eq!(output, (expected, 2));
1195
1196         let input =
1197 "extern crate hella_qwop;
1198 assert_eq!(asdf::foo, 4);";
1199
1200         let expected =
1201 "#![allow(unused)]
1202 extern crate hella_qwop;
1203 extern crate asdf;
1204 fn main() {
1205 assert_eq!(asdf::foo, 4);
1206 }".to_string();
1207
1208         let output = make_test(input, Some("asdf"), false, &opts);
1209         assert_eq!(output, (expected, 3));
1210     }
1211
1212     #[test]
1213     fn make_test_main_in_macro() {
1214         let opts = TestOptions::default();
1215         let input =
1216 "#[macro_use] extern crate my_crate;
1217 test_wrapper! {
1218     fn main() {}
1219 }";
1220         let expected =
1221 "#![allow(unused)]
1222 #[macro_use] extern crate my_crate;
1223 test_wrapper! {
1224     fn main() {}
1225 }".to_string();
1226
1227         let output = make_test(input, Some("my_crate"), false, &opts);
1228         assert_eq!(output, (expected, 1));
1229     }
1230 }