diff options
Diffstat (limited to 'src/librustdoc/test.rs')
| -rw-r--r-- | src/librustdoc/test.rs | 143 |
1 files changed, 91 insertions, 52 deletions
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 7fa1b38bdad..74a16cb867d 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -27,11 +27,10 @@ use rustc::hir::intravisit; use rustc::session::{self, CompileIncomplete, config}; use rustc::session::config::{OutputType, OutputTypes, Externs}; use rustc::session::search_paths::{SearchPaths, PathKind}; -use rustc_back::dynamic_lib::DynamicLibrary; -use rustc_back::tempdir::TempDir; +use rustc_metadata::dynamic_lib::DynamicLibrary; +use tempdir::TempDir; use rustc_driver::{self, driver, Compilation}; use rustc_driver::driver::phase_2_configure_and_expand; -use rustc_driver::pretty::ReplaceBodyWithLoop; use rustc_metadata::cstore::CStore; use rustc_resolve::MakeGlobMap; use rustc_trans; @@ -39,7 +38,6 @@ use rustc_trans::back::link; use syntax::ast; use syntax::codemap::CodeMap; use syntax::feature_gate::UnstableFeatures; -use syntax::fold::Folder; use syntax_pos::{BytePos, DUMMY_SP, Pos, Span}; use errors; use errors::emitter::ColorConfig; @@ -61,7 +59,8 @@ pub fn run(input: &str, crate_name: Option<String>, maybe_sysroot: Option<PathBuf>, render_type: RenderType, - display_warnings: bool) + display_warnings: bool, + linker: Option<String>) -> isize { let input_path = PathBuf::from(input); let input = config::Input::File(input_path.clone()); @@ -80,7 +79,9 @@ pub fn run(input: &str, let codemap = Rc::new(CodeMap::new(sessopts.file_path_mapping())); let handler = - errors::Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(codemap.clone())); + errors::Handler::with_tty_emitter(ColorConfig::Auto, + true, false, + Some(codemap.clone())); let cstore = Rc::new(CStore::new(box rustc_trans::LlvmMetadataLoader)); let mut sess = session::build_session_( @@ -94,7 +95,6 @@ pub fn run(input: &str, let krate = panictry!(driver::phase_1_parse_input(&driver::CompileController::basic(), &sess, &input)); - let krate = ReplaceBodyWithLoop::new().fold_crate(krate); let driver::ExpansionResult { defs, mut hir_forest, .. } = { phase_2_configure_and_expand( &sess, @@ -121,7 +121,8 @@ pub fn run(input: &str, maybe_sysroot, Some(codemap), None, - render_type); + render_type, + linker); { let map = hir::map::map_crate(&sess, &*cstore, &mut hir_forest, &defs); @@ -180,10 +181,13 @@ fn run_test(test: &str, cratename: &str, filename: &str, cfgs: Vec<String>, libs externs: Externs, should_panic: bool, no_run: bool, as_test_harness: bool, compile_fail: bool, mut error_codes: Vec<String>, opts: &TestOptions, - maybe_sysroot: Option<PathBuf>) { + maybe_sysroot: Option<PathBuf>, + linker: Option<String>) { // the test harness wants its own `main` & top level functions, so // never wrap the test in `fn main() { ... }` let test = make_test(test, Some(cratename), as_test_harness, opts); + // FIXME(#44940): if doctests ever support path remapping, then this filename + // needs to be the result of CodeMap::span_to_unmapped_path let input = config::Input::Str { name: filename.to_owned(), input: test.to_owned(), @@ -199,6 +203,7 @@ fn run_test(test: &str, cratename: &str, filename: &str, cfgs: Vec<String>, libs externs, cg: config::CodegenOptions { prefer_dynamic: true, + linker, .. config::basic_codegen_options() }, test: as_test_harness, @@ -232,7 +237,8 @@ fn run_test(test: &str, cratename: &str, filename: &str, cfgs: Vec<String>, libs let data = Arc::new(Mutex::new(Vec::new())); let codemap = Rc::new(CodeMap::new(sessopts.file_path_mapping())); let emitter = errors::emitter::EmitterWriter::new(box Sink(data.clone()), - Some(codemap.clone())); + Some(codemap.clone()), + false); let old = io::set_panic(Some(box Sink(data.clone()))); let _bomb = Bomb(data.clone(), old.unwrap_or(box io::stdout())); @@ -330,15 +336,23 @@ pub fn make_test(s: &str, let mut prog = String::new(); - // First push any outer attributes from the example, assuming they - // are intended to be crate attributes. - prog.push_str(&crate_attrs); + if opts.attrs.is_empty() { + // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some + // lints that are commonly triggered in doctests. The crate-level test attributes are + // commonly used to make tests fail in case they trigger warnings, so having this there in + // that case may cause some tests to pass when they shouldn't have. + prog.push_str("#![allow(unused)]\n"); + } - // Next, any attributes for other aspects such as lints. + // Next, any attributes that came from the crate root via #![doc(test(attr(...)))]. for attr in &opts.attrs { prog.push_str(&format!("#![{}]\n", attr)); } + // Now push any outer attributes from the example, assuming they + // are intended to be crate attributes. + prog.push_str(&crate_attrs); + // Don't inject `extern crate std` because it's already injected by the // compiler. if !s.contains("extern crate") && !opts.no_crate_inject && cratename != Some("std") { @@ -405,13 +419,33 @@ pub struct Collector { pub tests: Vec<testing::TestDescAndFn>, // to be removed when hoedown will be definitely gone pub old_tests: HashMap<String, Vec<String>>, + + // The name of the test displayed to the user, separated by `::`. + // + // In tests from Rust source, this is the path to the item + // e.g. `["std", "vec", "Vec", "push"]`. + // + // In tests from a markdown file, this is the titles of all headers (h1~h6) + // of the sections that contain the code block, e.g. if the markdown file is + // written as: + // + // ``````markdown + // # Title + // + // ## Subtitle + // + // ```rust + // assert!(true); + // ``` + // `````` + // + // the `names` vector of that test will be `["Title", "Subtitle"]`. names: Vec<String>, + cfgs: Vec<String>, libs: SearchPaths, externs: Externs, - cnt: usize, use_headers: bool, - current_header: Option<String>, cratename: String, opts: TestOptions, maybe_sysroot: Option<PathBuf>, @@ -420,13 +454,14 @@ pub struct Collector { filename: Option<String>, // to be removed when hoedown will be removed as well pub render_type: RenderType, + linker: Option<String>, } impl Collector { pub fn new(cratename: String, cfgs: Vec<String>, libs: SearchPaths, externs: Externs, use_headers: bool, opts: TestOptions, maybe_sysroot: Option<PathBuf>, codemap: Option<Rc<CodeMap>>, filename: Option<String>, - render_type: RenderType) -> Collector { + render_type: RenderType, linker: Option<String>) -> Collector { Collector { tests: Vec::new(), old_tests: HashMap::new(), @@ -434,9 +469,7 @@ impl Collector { cfgs, libs, externs, - cnt: 0, use_headers, - current_header: None, cratename, opts, maybe_sysroot, @@ -444,32 +477,17 @@ impl Collector { codemap, filename, render_type, + linker, } } fn generate_name(&self, line: usize, filename: &str) -> String { - if self.use_headers { - if let Some(ref header) = self.current_header { - format!("{} - {} (line {})", filename, header, line) - } else { - format!("{} - (line {})", filename, line) - } - } else { - format!("{} - {} (line {})", filename, self.names.join("::"), line) - } + format!("{} - {} (line {})", filename, self.names.join("::"), line) } // to be removed once hoedown is gone fn generate_name_beginning(&self, filename: &str) -> String { - if self.use_headers { - if let Some(ref header) = self.current_header { - format!("{} - {} (line", filename, header) - } else { - format!("{} - (line", filename) - } - } else { - format!("{} - {} (line", filename, self.names.join("::")) - } + format!("{} - {} (line", filename, self.names.join("::")) } pub fn add_old_test(&mut self, test: String, filename: String) { @@ -493,11 +511,10 @@ impl Collector { found = entry.remove_item(&test).is_some(); } if !found { - let _ = writeln!(&mut io::stderr(), - "WARNING: {} Code block is not currently run as a test, but will \ - in future versions of rustdoc. Please ensure this code block is \ - a runnable test, or use the `ignore` directive.", - name); + eprintln!("WARNING: {} Code block is not currently run as a test, but will \ + in future versions of rustdoc. Please ensure this code block is \ + a runnable test, or use the `ignore` directive.", + name); return } } @@ -507,6 +524,7 @@ impl Collector { let cratename = self.cratename.to_string(); let opts = self.opts.clone(); let maybe_sysroot = self.maybe_sysroot.clone(); + let linker = self.linker.clone(); debug!("Creating test {}: {}", name, test); self.tests.push(testing::TestDescAndFn { desc: testing::TestDesc { @@ -535,7 +553,8 @@ impl Collector { compile_fail, error_codes, &opts, - maybe_sysroot) + maybe_sysroot, + linker) }) } { Ok(()) => (), @@ -578,7 +597,7 @@ impl Collector { } pub fn register_header(&mut self, name: &str, level: u32) { - if self.use_headers && level == 1 { + if self.use_headers { // we use these headings as test names, so it's good if // they're valid identifiers. let name = name.chars().enumerate().map(|(i, c)| { @@ -590,9 +609,28 @@ impl Collector { } }).collect::<String>(); - // new header => reset count. - self.cnt = 0; - self.current_header = Some(name); + // Here we try to efficiently assemble the header titles into the + // test name in the form of `h1::h2::h3::h4::h5::h6`. + // + // Suppose originally `self.names` contains `[h1, h2, h3]`... + let level = level as usize; + if level <= self.names.len() { + // ... Consider `level == 2`. All headers in the lower levels + // are irrelevant in this new level. So we should reset + // `self.names` to contain headers until <h2>, and replace that + // slot with the new name: `[h1, name]`. + self.names.truncate(level); + self.names[level - 1] = name; + } else { + // ... On the other hand, consider `level == 5`. This means we + // need to extend `self.names` to contain five headers. We fill + // in the missing level (<h4>) with `_`. Thus `self.names` will + // become `[h1, h2, h3, "_", name]`. + if level - 1 > self.names.len() { + self.names.resize(level - 1, "_".to_owned()); + } + self.names.push(name); + } } } } @@ -622,15 +660,16 @@ impl<'a, 'hir> HirCollector<'a, 'hir> { attrs.collapse_doc_comments(); attrs.unindent_doc_comments(); - if let Some(doc) = attrs.doc_value() { - self.collector.cnt = 0; + // the collapse-docs pass won't combine sugared/raw doc attributes, or included files with + // anything else, this will combine them for us + if let Some(doc) = attrs.collapsed_doc_value() { if self.collector.render_type == RenderType::Pulldown { - markdown::old_find_testable_code(doc, self.collector, + markdown::old_find_testable_code(&doc, self.collector, attrs.span.unwrap_or(DUMMY_SP)); - markdown::find_testable_code(doc, self.collector, + markdown::find_testable_code(&doc, self.collector, attrs.span.unwrap_or(DUMMY_SP)); } else { - markdown::old_find_testable_code(doc, self.collector, + markdown::old_find_testable_code(&doc, self.collector, attrs.span.unwrap_or(DUMMY_SP)); } } |
