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