about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAndreas Jonson <andjo403@users.noreply.github.com>2019-08-29 23:15:31 +0200
committerAndreas Jonson <andjo403@users.noreply.github.com>2019-08-29 23:15:31 +0200
commitb304cd02c033945144404d859b8d031a46e9c8ca (patch)
treeafb236ad3628889b781af6c389e744441c21c88c
parent56ab485fbe61add87ac8febea421e2a576edca26 (diff)
downloadrust-b304cd02c033945144404d859b8d031a46e9c8ca.tar.gz
rust-b304cd02c033945144404d859b8d031a46e9c8ca.zip
Run doctests via out-of-process rustc
-rw-r--r--src/librustdoc/config.rs12
-rw-r--r--src/librustdoc/markdown.rs7
-rw-r--r--src/librustdoc/test.rs214
3 files changed, 82 insertions, 151 deletions
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index cefae2e105e..30b1706f294 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -39,12 +39,18 @@ pub struct Options {
     pub error_format: ErrorOutputType,
     /// Library search paths to hand to the compiler.
     pub libs: Vec<SearchPath>,
+    /// Library search paths strings to hand to the compiler.
+    pub lib_strs: Vec<String>,
     /// The list of external crates to link against.
     pub externs: Externs,
+    /// The list of external crates strings to link against.
+    pub extern_strs: Vec<String>,
     /// List of `cfg` flags to hand to the compiler. Always includes `rustdoc`.
     pub cfgs: Vec<String>,
     /// Codegen options to hand to the compiler.
     pub codegen_options: CodegenOptions,
+    /// Codegen options strings to hand to the compiler.
+    pub codegen_options_strs: Vec<String>,
     /// Debugging (`-Z`) options to pass to the compiler.
     pub debugging_options: DebuggingOptions,
     /// The target used to compile the crate against.
@@ -461,6 +467,9 @@ impl Options {
         let generate_search_filter = !matches.opt_present("disable-per-crate-search");
         let persist_doctests = matches.opt_str("persist-doctests").map(PathBuf::from);
         let generate_redirect_pages = matches.opt_present("generate-redirect-pages");
+        let codegen_options_strs = matches.opt_strs("C");
+        let lib_strs = matches.opt_strs("L");
+        let extern_strs = matches.opt_strs("extern");
 
         let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
 
@@ -470,9 +479,12 @@ impl Options {
             proc_macro_crate,
             error_format,
             libs,
+            lib_strs,
             externs,
+            extern_strs,
             cfgs,
             codegen_options,
+            codegen_options_strs,
             debugging_options,
             target,
             edition,
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index e735a9779c9..a30fc05f36a 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -142,11 +142,8 @@ pub fn test(mut options: Options, diag: &errors::Handler) -> i32 {
     let mut opts = TestOptions::default();
     opts.no_crate_inject = true;
     opts.display_warnings = options.display_warnings;
-    let mut collector = Collector::new(options.input.display().to_string(), options.cfgs,
-                                       options.libs, options.codegen_options, options.externs,
-                                       true, opts, options.maybe_sysroot, None,
-                                       Some(options.input),
-                                       options.linker, options.edition, options.persist_doctests);
+    let mut collector = Collector::new(options.input.display().to_string(), options.clone(),
+                                       true, opts, None, Some(options.input));
     collector.set_position(DUMMY_SP);
     let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
 
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index cb6ae1c2bd2..70511131e53 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -2,10 +2,7 @@ use rustc_data_structures::sync::Lrc;
 use rustc_interface::interface;
 use rustc::hir;
 use rustc::hir::intravisit;
-use rustc::hir::def_id::LOCAL_CRATE;
 use rustc::session::{self, config, DiagnosticOutput};
-use rustc::session::config::{OutputType, OutputTypes, Externs, CodegenOptions};
-use rustc::session::search_paths::SearchPath;
 use rustc::util::common::ErrorReported;
 use syntax::ast;
 use syntax::with_globals;
@@ -13,13 +10,11 @@ use syntax::source_map::SourceMap;
 use syntax::edition::Edition;
 use syntax::feature_gate::UnstableFeatures;
 use std::env;
-use std::io::prelude::*;
-use std::io;
-use std::panic::{self, AssertUnwindSafe};
+use std::io::{self, Write};
+use std::panic;
 use std::path::PathBuf;
-use std::process::{self, Command};
+use std::process::{self, Command, Stdio};
 use std::str;
-use std::sync::{Arc, Mutex};
 use syntax::symbol::sym;
 use syntax_pos::{BytePos, DUMMY_SP, Pos, Span, FileName};
 use tempfile::Builder as TempFileBuilder;
@@ -89,18 +84,11 @@ pub fn run(options: Options) -> i32 {
         opts.display_warnings |= options.display_warnings;
         let mut collector = Collector::new(
             compiler.crate_name()?.peek().to_string(),
-            options.cfgs,
-            options.libs,
-            options.codegen_options,
-            options.externs,
+            options,
             false,
             opts,
-            options.maybe_sysroot,
             Some(compiler.source_map().clone()),
             None,
-            options.linker,
-            options.edition,
-            options.persist_doctests,
         );
 
         let mut global_ctxt = compiler.global_ctxt()?.take();
@@ -189,20 +177,14 @@ fn run_test(
     cratename: &str,
     filename: &FileName,
     line: usize,
-    cfgs: Vec<String>,
-    libs: Vec<SearchPath>,
-    cg: CodegenOptions,
-    externs: Externs,
+    options: Options,
     should_panic: bool,
     no_run: bool,
     as_test_harness: bool,
     compile_fail: bool,
     mut error_codes: Vec<String>,
     opts: &TestOptions,
-    maybe_sysroot: Option<PathBuf>,
-    linker: Option<PathBuf>,
     edition: Edition,
-    persist_doctests: Option<PathBuf>,
 ) -> Result<(), TestFailure> {
     let (test, line_offset) = match panic::catch_unwind(|| {
         make_test(test, Some(cratename), as_test_harness, opts, edition)
@@ -223,61 +205,6 @@ fn run_test(
         _ => PathBuf::from(r"doctest.rs"),
     };
 
-    let input = config::Input::Str {
-        name: FileName::DocTest(path, line as isize - line_offset as isize),
-        input: test,
-    };
-    let outputs = OutputTypes::new(&[(OutputType::Exe, None)]);
-
-    let sessopts = config::Options {
-        maybe_sysroot,
-        search_paths: libs,
-        crate_types: vec![config::CrateType::Executable],
-        output_types: outputs,
-        externs,
-        cg: config::CodegenOptions {
-            linker,
-            ..cg
-        },
-        test: as_test_harness,
-        unstable_features: UnstableFeatures::from_environment(),
-        debugging_opts: config::DebuggingOptions {
-            ..config::basic_debugging_options()
-        },
-        edition,
-        ..config::Options::default()
-    };
-
-    // Shuffle around a few input and output handles here. We're going to pass
-    // an explicit handle into rustc to collect output messages, but we also
-    // want to catch the error message that rustc prints when it fails.
-    //
-    // We take our thread-local stderr (likely set by the test runner) and replace
-    // it with a sink that is also passed to rustc itself. When this function
-    // returns the output of the sink is copied onto the output of our own thread.
-    //
-    // The basic idea is to not use a default Handler for rustc, and then also
-    // not print things by default to the actual stderr.
-    struct Sink(Arc<Mutex<Vec<u8>>>);
-    impl Write for Sink {
-        fn write(&mut self, data: &[u8]) -> io::Result<usize> {
-            Write::write(&mut *self.0.lock().unwrap(), data)
-        }
-        fn flush(&mut self) -> io::Result<()> { Ok(()) }
-    }
-    struct Bomb(Arc<Mutex<Vec<u8>>>, Option<Box<dyn Write+Send>>);
-    impl Drop for Bomb {
-        fn drop(&mut self) {
-            let mut old = self.1.take().unwrap();
-            let _ = old.write_all(&self.0.lock().unwrap());
-            io::set_panic(Some(old));
-        }
-    }
-    let data = Arc::new(Mutex::new(Vec::new()));
-
-    let old = io::set_panic(Some(box Sink(data.clone())));
-    let _bomb = Bomb(data.clone(), Some(old.unwrap_or(box io::stdout())));
-
     enum DirState {
         Temp(tempfile::TempDir),
         Perm(PathBuf),
@@ -292,7 +219,7 @@ fn run_test(
         }
     }
 
-    let outdir = if let Some(mut path) = persist_doctests {
+    let outdir = if let Some(mut path) = options.persist_doctests {
         path.push(format!("{}_{}",
             filename
                 .to_string()
@@ -314,41 +241,65 @@ fn run_test(
     };
     let output_file = outdir.path().join("rust_out");
 
-    let config = interface::Config {
-        opts: sessopts,
-        crate_cfg: config::parse_cfgspecs(cfgs),
-        input,
-        input_path: None,
-        output_file: Some(output_file.clone()),
-        output_dir: None,
-        file_loader: None,
-        diagnostic_output: DiagnosticOutput::Raw(box Sink(data.clone())),
-        stderr: Some(data.clone()),
-        crate_name: None,
-        lint_caps: Default::default(),
-    };
+    let mut compiler = Command::new(std::env::current_exe().unwrap().with_file_name("rustc"));
+    compiler.arg("--crate-type").arg("bin");
+    for cfg in &options.cfgs {
+        compiler.arg("--cfg").arg(&cfg);
+    }
+    if let Some(sysroot) = options.maybe_sysroot {
+        compiler.arg("--sysroot").arg(sysroot);
+    }
+    compiler.arg("--edition").arg(&edition.to_string());
+    compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", path);
+    compiler.env("UNSTABLE_RUSTDOC_TEST_LINE",
+                 format!("{}", line as isize - line_offset as isize));
+    compiler.arg("-o").arg(&output_file);
+    if as_test_harness {
+        compiler.arg("--test");
+    }
+    for lib_str in &options.lib_strs {
+        compiler.arg("-L").arg(&lib_str);
+    }
+    for extern_str in &options.extern_strs {
+        compiler.arg("--extern").arg(&extern_str);
+    }
+    for codegen_options_str in &options.codegen_options_strs {
+        compiler.arg("-C").arg(&codegen_options_str);
+    }
+    if let Some(linker) = options.linker {
+        compiler.arg(&format!("-C linker={:?}", linker));
+    }
+    if no_run {
+        compiler.arg("--emit=metadata");
+    }
 
-    let compile_result = panic::catch_unwind(AssertUnwindSafe(|| {
-        interface::run_compiler(config, |compiler| {
-            if no_run {
-                compiler.global_ctxt().and_then(|global_ctxt| global_ctxt.take().enter(|tcx| {
-                    tcx.analysis(LOCAL_CRATE)
-                })).ok();
-            } else {
-                compiler.compile().ok();
-            };
-            compiler.session().compile_status()
-        })
-    })).map_err(|_| ()).and_then(|s| s.map_err(|_| ()));
+    compiler.arg("-");
+    compiler.stdin(Stdio::piped());
+    compiler.stderr(Stdio::piped());
+
+    let mut child = compiler.spawn().expect("Failed to spawn rustc process");
+    {
+        let stdin = child.stdin.as_mut().expect("Failed to open stdin");
+        stdin.write_all(test.as_bytes()).expect("could write out test sources");
+    }
+    let output = child.wait_with_output().expect("Failed to read stdout");
 
-    match (compile_result, compile_fail) {
-        (Ok(()), true) => {
+    struct Bomb<'a>(&'a str);
+    impl Drop for Bomb<'_> {
+        fn drop(&mut self) {
+            eprint!("{}",self.0);
+        }
+    }
+
+    let out = str::from_utf8(&output.stderr).unwrap();
+    let _bomb = Bomb(&out);
+    match (output.status.success(), compile_fail) {
+        (true, true) => {
             return Err(TestFailure::UnexpectedCompilePass);
         }
-        (Ok(()), false) => {}
-        (Err(_), true) => {
+        (true, false) => {}
+        (false, true) => {
             if !error_codes.is_empty() {
-                let out = String::from_utf8(data.lock().unwrap().to_vec()).unwrap();
                 error_codes.retain(|err| !out.contains(err));
 
                 if !error_codes.is_empty() {
@@ -356,7 +307,7 @@ fn run_test(
                 }
             }
         }
-        (Err(_), false) => {
+        (false, false) => {
             return Err(TestFailure::CompileError);
         }
     }
@@ -652,45 +603,28 @@ pub struct Collector {
     // the `names` vector of that test will be `["Title", "Subtitle"]`.
     names: Vec<String>,
 
-    cfgs: Vec<String>,
-    libs: Vec<SearchPath>,
-    cg: CodegenOptions,
-    externs: Externs,
+    options: Options,
     use_headers: bool,
     cratename: String,
     opts: TestOptions,
-    maybe_sysroot: Option<PathBuf>,
     position: Span,
     source_map: Option<Lrc<SourceMap>>,
     filename: Option<PathBuf>,
-    linker: Option<PathBuf>,
-    edition: Edition,
-    persist_doctests: Option<PathBuf>,
 }
 
 impl Collector {
-    pub fn new(cratename: String, cfgs: Vec<String>, libs: Vec<SearchPath>, cg: CodegenOptions,
-               externs: Externs, use_headers: bool, opts: TestOptions,
-               maybe_sysroot: Option<PathBuf>, source_map: Option<Lrc<SourceMap>>,
-               filename: Option<PathBuf>, linker: Option<PathBuf>, edition: Edition,
-               persist_doctests: Option<PathBuf>) -> Collector {
+    pub fn new(cratename: String, options: Options, use_headers: bool, opts: TestOptions,
+               source_map: Option<Lrc<SourceMap>>, filename: Option<PathBuf>,) -> Collector {
         Collector {
             tests: Vec::new(),
             names: Vec::new(),
-            cfgs,
-            libs,
-            cg,
-            externs,
+            options,
             use_headers,
             cratename,
             opts,
-            maybe_sysroot,
             position: DUMMY_SP,
             source_map,
             filename,
-            linker,
-            edition,
-            persist_doctests,
         }
     }
 
@@ -725,16 +659,10 @@ impl Tester for Collector {
     fn add_test(&mut self, test: String, config: LangString, line: usize) {
         let filename = self.get_filename();
         let name = self.generate_name(line, &filename);
-        let cfgs = self.cfgs.clone();
-        let libs = self.libs.clone();
-        let cg = self.cg.clone();
-        let externs = self.externs.clone();
         let cratename = self.cratename.to_string();
         let opts = self.opts.clone();
-        let maybe_sysroot = self.maybe_sysroot.clone();
-        let linker = self.linker.clone();
-        let edition = config.edition.unwrap_or(self.edition);
-        let persist_doctests = self.persist_doctests.clone();
+        let edition = config.edition.unwrap_or(self.options.edition.clone());
+        let options = self.options.clone();
 
         debug!("creating test {}: {}", name, test);
         self.tests.push(testing::TestDescAndFn {
@@ -751,20 +679,14 @@ impl Tester for Collector {
                     &cratename,
                     &filename,
                     line,
-                    cfgs,
-                    libs,
-                    cg,
-                    externs,
+                    options,
                     config.should_panic,
                     config.no_run,
                     config.test_harness,
                     config.compile_fail,
                     config.error_codes,
                     &opts,
-                    maybe_sysroot,
-                    linker,
                     edition,
-                    persist_doctests
                 );
 
                 if let Err(err) = res {