about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2016-01-05 23:38:11 +0100
committerGuillaume Gomez <guillaume1.gomez@gmail.com>2016-02-07 13:03:32 +0100
commiteb5b9037ad9027cab82cbda5d915477cf8eb2594 (patch)
treee8fe5c0d8f793f71fccdf918a2f829aa1fbd5ec9
parentd0ef74026690cffccb543fc274d73a078eba797d (diff)
downloadrust-eb5b9037ad9027cab82cbda5d915477cf8eb2594.tar.gz
rust-eb5b9037ad9027cab82cbda5d915477cf8eb2594.zip
Add compile-fail test in rustdoc
-rw-r--r--src/error-index-generator/main.rs154
-rw-r--r--src/librustc_driver/driver.rs2
-rw-r--r--src/librustc_driver/lib.rs2
-rw-r--r--src/librustdoc/html/markdown.rs38
-rw-r--r--src/librustdoc/lib.rs2
-rw-r--r--src/librustdoc/test.rs46
-rw-r--r--src/test/run-make/issue-19371/foo.rs2
7 files changed, 183 insertions, 63 deletions
diff --git a/src/error-index-generator/main.rs b/src/error-index-generator/main.rs
index 4b10b02f2d4..2271e55ee28 100644
--- a/src/error-index-generator/main.rs
+++ b/src/error-index-generator/main.rs
@@ -21,37 +21,41 @@ use std::env;
 use std::path::Path;
 use std::error::Error;
 
-use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap};
+use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap, ErrorMetadata};
 
 use rustdoc::html::markdown::Markdown;
 use rustc_serialize::json;
 
-/// Load all the metadata files from `metadata_dir` into an in-memory map.
-fn load_all_errors(metadata_dir: &Path) -> Result<ErrorMetadataMap, Box<Error>> {
-    let mut all_errors = BTreeMap::new();
-
-    for entry in try!(read_dir(metadata_dir)) {
-        let path = try!(entry).path();
-
-        let mut metadata_str = String::new();
-        try!(File::open(&path).and_then(|mut f| f.read_to_string(&mut metadata_str)));
-
-        let some_errors: ErrorMetadataMap = try!(json::decode(&metadata_str));
+enum OutputFormat {
+    HTML(HTMLFormatter),
+    Markdown(MarkdownFormatter),
+    Unknown(String),
+}
 
-        for (err_code, info) in some_errors {
-            all_errors.insert(err_code, info);
+impl OutputFormat {
+    fn from(format: &str) -> OutputFormat {
+        match &*format.to_lowercase() {
+            "html"     => OutputFormat::HTML(HTMLFormatter),
+            "markdown" => OutputFormat::Markdown(MarkdownFormatter),
+            s          => OutputFormat::Unknown(s.to_owned()),
         }
     }
+}
 
-    Ok(all_errors)
+trait Formatter {
+    fn header(&self, output: &mut Write) -> Result<(), Box<Error>>;
+    fn title(&self, output: &mut Write) -> Result<(), Box<Error>>;
+    fn error_code_block(&self, output: &mut Write, info: &ErrorMetadata,
+                        err_code: &str) -> Result<(), Box<Error>>;
+    fn footer(&self, output: &mut Write) -> Result<(), Box<Error>>;
 }
 
-/// Output an HTML page for the errors in `err_map` to `output_path`.
-fn render_error_page(err_map: &ErrorMetadataMap, output_path: &Path) -> Result<(), Box<Error>> {
-    let mut output_file = try!(File::create(output_path));
+struct HTMLFormatter;
+struct MarkdownFormatter;
 
-    try!(write!(&mut output_file,
-r##"<!DOCTYPE html>
+impl Formatter for HTMLFormatter {
+    fn header(&self, output: &mut Write) -> Result<(), Box<Error>> {
+        try!(write!(output, r##"<!DOCTYPE html>
 <html>
 <head>
 <title>Rust Compiler Error Index</title>
@@ -66,12 +70,17 @@ r##"<!DOCTYPE html>
 </style>
 </head>
 <body>
-"##
-    ));
+"##));
+        Ok(())
+    }
 
-    try!(write!(&mut output_file, "<h1>Rust Compiler Error Index</h1>\n"));
+    fn title(&self, output: &mut Write) -> Result<(), Box<Error>> {
+        try!(write!(output, "<h1>Rust Compiler Error Index</h1>\n"));
+        Ok(())
+    }
 
-    for (err_code, info) in err_map {
+    fn error_code_block(&self, output: &mut Write, info: &ErrorMetadata,
+                        err_code: &str) -> Result<(), Box<Error>> {
         // Enclose each error in a div so they can be shown/hidden en masse.
         let desc_desc = match info.description {
             Some(_) => "error-described",
@@ -81,37 +90,114 @@ r##"<!DOCTYPE html>
             Some(_) => "error-used",
             None => "error-unused",
         };
-        try!(write!(&mut output_file, "<div class=\"{} {}\">", desc_desc, use_desc));
+        try!(write!(output, "<div class=\"{} {}\">", desc_desc, use_desc));
 
         // Error title (with self-link).
-        try!(write!(&mut output_file,
+        try!(write!(output,
                     "<h2 id=\"{0}\" class=\"section-header\"><a href=\"#{0}\">{0}</a></h2>\n",
                     err_code));
 
         // Description rendered as markdown.
         match info.description {
-            Some(ref desc) => try!(write!(&mut output_file, "{}", Markdown(desc))),
-            None => try!(write!(&mut output_file, "<p>No description.</p>\n")),
+            Some(ref desc) => try!(write!(output, "{}", Markdown(desc))),
+            None => try!(write!(output, "<p>No description.</p>\n")),
         }
 
-        try!(write!(&mut output_file, "</div>\n"));
+        try!(write!(output, "</div>\n"));
+        Ok(())
     }
 
-    try!(write!(&mut output_file, "</body>\n</html>"));
+    fn footer(&self, output: &mut Write) -> Result<(), Box<Error>> {
+        try!(write!(output, "</body>\n</html>"));
+        Ok(())
+    }
+}
 
-    Ok(())
+impl Formatter for MarkdownFormatter {
+    #[allow(unused_variables)]
+    fn header(&self, output: &mut Write) -> Result<(), Box<Error>> {
+        Ok(())
+    }
+
+    fn title(&self, output: &mut Write) -> Result<(), Box<Error>> {
+        try!(write!(output, "# Rust Compiler Error Index\n"));
+        Ok(())
+    }
+
+    fn error_code_block(&self, output: &mut Write, info: &ErrorMetadata,
+                        err_code: &str) -> Result<(), Box<Error>> {
+        Ok(match info.description {
+            Some(ref desc) => try!(write!(output, "## {}\n{}\n", err_code, desc)),
+            None => (),
+        })
+    }
+
+    #[allow(unused_variables)]
+    fn footer(&self, output: &mut Write) -> Result<(), Box<Error>> {
+        Ok(())
+    }
+}
+
+/// Load all the metadata files from `metadata_dir` into an in-memory map.
+fn load_all_errors(metadata_dir: &Path) -> Result<ErrorMetadataMap, Box<Error>> {
+    let mut all_errors = BTreeMap::new();
+
+    for entry in try!(read_dir(metadata_dir)) {
+        let path = try!(entry).path();
+
+        let mut metadata_str = String::new();
+        try!(File::open(&path).and_then(|mut f| f.read_to_string(&mut metadata_str)));
+
+        let some_errors: ErrorMetadataMap = try!(json::decode(&metadata_str));
+
+        for (err_code, info) in some_errors {
+            all_errors.insert(err_code, info);
+        }
+    }
+
+    Ok(all_errors)
+}
+
+/// Output an HTML page for the errors in `err_map` to `output_path`.
+fn render_error_page<T: Formatter>(err_map: &ErrorMetadataMap, output_path: &Path,
+                                   formatter: T) -> Result<(), Box<Error>> {
+    let mut output_file = try!(File::create(output_path));
+
+    try!(formatter.header(&mut output_file));
+    try!(formatter.title(&mut output_file));
+
+    for (err_code, info) in err_map {
+        try!(formatter.error_code_block(&mut output_file, info, err_code));
+    }
+
+    formatter.footer(&mut output_file)
 }
 
-fn main_with_result() -> Result<(), Box<Error>> {
+fn main_with_result(format: OutputFormat) -> Result<(), Box<Error>> {
     let build_arch = try!(env::var("CFG_BUILD"));
     let metadata_dir = get_metadata_dir(&build_arch);
     let err_map = try!(load_all_errors(&metadata_dir));
-    try!(render_error_page(&err_map, Path::new("doc/error-index.html")));
+    match format {
+        OutputFormat::Unknown(s)    => panic!("Unknown output format: {}", s),
+        OutputFormat::HTML(h)       => try!(render_error_page(&err_map,
+                                                              Path::new("doc/error-index.html"),
+                                                              h)),
+        OutputFormat::Markdown(m)   => try!(render_error_page(&err_map,
+                                                              Path::new("doc/error-index.html"),
+                                                              m)),
+    }
     Ok(())
 }
 
+fn parse_args() -> OutputFormat {
+    for arg in env::args().skip(1) {
+        return OutputFormat::from(&arg);
+    }
+    OutputFormat::from("html")
+}
+
 fn main() {
-    if let Err(e) = main_with_result() {
+    if let Err(e) = main_with_result(parse_args()) {
         panic!("{}", e.description());
     }
 }
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index c189df18a82..dd1fd02ecb3 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -65,7 +65,7 @@ pub fn compile_input(sess: &Session,
                      outdir: &Option<PathBuf>,
                      output: &Option<PathBuf>,
                      addl_plugins: Option<Vec<String>>,
-                     control: CompileController) -> CompileResult {
+                     control: &CompileController) -> CompileResult {
     macro_rules! controller_entry_point {
         ($point: ident, $tsess: expr, $make_state: expr, $phase_result: expr) => {{
             let state = $make_state;
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index 70bd938321a..be14bab1f26 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -199,7 +199,7 @@ pub fn run_compiler<'a>(args: &[String],
     let plugins = sess.opts.debugging_opts.extra_plugins.clone();
     let control = callbacks.build_controller(&sess);
     (driver::compile_input(&sess, &cstore, cfg, &input, &odir, &ofile,
-                           Some(plugins), control),
+                           Some(plugins), &control),
      Some(sess))
 }
 
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index a5436886a7e..e919911e6fd 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -400,7 +400,8 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) {
             let text = lines.collect::<Vec<&str>>().join("\n");
             tests.add_test(text.to_owned(),
                            block_info.should_panic, block_info.no_run,
-                           block_info.ignore, block_info.test_harness);
+                           block_info.ignore, block_info.test_harness,
+                           block_info.compile_fail);
         }
     }
 
@@ -445,6 +446,7 @@ struct LangString {
     ignore: bool,
     rust: bool,
     test_harness: bool,
+    compile_fail: bool,
 }
 
 impl LangString {
@@ -455,6 +457,7 @@ impl LangString {
             ignore: false,
             rust: true,  // NB This used to be `notrust = false`
             test_harness: false,
+            compile_fail: false,
         }
     }
 
@@ -474,7 +477,9 @@ impl LangString {
                 "no_run" => { data.no_run = true; seen_rust_tags = true; },
                 "ignore" => { data.ignore = true; seen_rust_tags = true; },
                 "rust" => { data.rust = true; seen_rust_tags = true; },
-                "test_harness" => { data.test_harness = true; seen_rust_tags = true; }
+                "test_harness" => { data.test_harness = true; seen_rust_tags = true; },
+                "compile_fail" => { data.compile_fail = true; seen_rust_tags = true;
+                                    data.no_run = true; },
                 _ => { seen_other_tags = true }
             }
         }
@@ -557,28 +562,31 @@ mod tests {
     #[test]
     fn test_lang_string_parse() {
         fn t(s: &str,
-            should_panic: bool, no_run: bool, ignore: bool, rust: bool, test_harness: bool) {
+            should_panic: bool, no_run: bool, ignore: bool, rust: bool, test_harness: bool,
+            compile_fail: bool) {
             assert_eq!(LangString::parse(s), LangString {
                 should_panic: should_panic,
                 no_run: no_run,
                 ignore: ignore,
                 rust: rust,
                 test_harness: test_harness,
+                compile_fail: compile_fail,
             })
         }
 
-        // marker                | should_panic| no_run | ignore | rust | test_harness
-        t("",                      false,        false,   false,   true,  false);
-        t("rust",                  false,        false,   false,   true,  false);
-        t("sh",                    false,        false,   false,   false, false);
-        t("ignore",                false,        false,   true,    true,  false);
-        t("should_panic",          true,         false,   false,   true,  false);
-        t("no_run",                false,        true,    false,   true,  false);
-        t("test_harness",          false,        false,   false,   true,  true);
-        t("{.no_run .example}",    false,        true,    false,   true,  false);
-        t("{.sh .should_panic}",   true,         false,   false,   true,  false);
-        t("{.example .rust}",      false,        false,   false,   true,  false);
-        t("{.test_harness .rust}", false,        false,   false,   true,  true);
+        // marker                | should_panic| no_run| ignore| rust | test_harness| compile_fail
+        t("",                      false,        false,  false,  true,  false,        false);
+        t("rust",                  false,        false,  false,  true,  false,        false);
+        t("sh",                    false,        false,  false,  false, false,        false);
+        t("ignore",                false,        false,  true,   true,  false,        false);
+        t("should_panic",          true,         false,  false,  true,  false,        false);
+        t("no_run",                false,        true,   false,  true,  false,        false);
+        t("test_harness",          false,        false,  false,  true,  true,         false);
+        t("compile_fail",          false,        false,  false,  true,  false,        true);
+        t("{.no_run .example}",    false,        true,   false,  true,  false,        false);
+        t("{.sh .should_panic}",   true,         false,  false,  true,  false,        false);
+        t("{.example .rust}",      false,        false,  false,  true,  false,        false);
+        t("{.test_harness .rust}", false,        false,  false,  true,  true,         false);
     }
 
     #[test]
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index c52459f6c10..6cad0d7d940 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -22,10 +22,12 @@
 #![feature(box_syntax)]
 #![feature(dynamic_lib)]
 #![feature(libc)]
+#![feature(recover)]
 #![feature(rustc_private)]
 #![feature(set_stdio)]
 #![feature(slice_patterns)]
 #![feature(staged_api)]
+#![feature(std_panic)]
 #![feature(test)]
 #![feature(unicode)]
 
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index 81f984a5927..36878e7b468 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -18,6 +18,7 @@ use std::ffi::OsString;
 use std::io::prelude::*;
 use std::io;
 use std::path::PathBuf;
+use std::panic::{self, AssertRecoverSafe};
 use std::process::Command;
 use std::rc::Rc;
 use std::str;
@@ -175,7 +176,7 @@ fn scrape_test_config(krate: &::rustc_front::hir::Crate) -> TestOptions {
 fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
            externs: core::Externs,
            should_panic: bool, no_run: bool, as_test_harness: bool,
-           opts: &TestOptions) {
+           compile_fail: bool, opts: &TestOptions) {
     // the test harness wants its own `main` & top level functions, so
     // never wrap the test in `fn main() { ... }`
     let test = maketest(test, Some(cratename), as_test_harness, opts);
@@ -241,19 +242,41 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
                                        cstore.clone());
     rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
 
-    let outdir = TempDir::new("rustdoctest").ok().expect("rustdoc needs a tempdir");
-    let out = Some(outdir.path().to_path_buf());
-    let mut cfg = config::build_configuration(&sess);
-    cfg.extend(config::parse_cfgspecs(cfgs));
+    let outdir = Mutex::new(TempDir::new("rustdoctest").ok().expect("rustdoc needs a tempdir"));
     let libdir = sess.target_filesearch(PathKind::All).get_lib_path();
     let mut control = driver::CompileController::basic();
+    let mut cfg = config::build_configuration(&sess);
+    cfg.extend(config::parse_cfgspecs(cfgs.clone()));
+    let out = Some(outdir.lock().unwrap().path().to_path_buf());
+
     if no_run {
         control.after_analysis.stop = Compilation::Stop;
     }
-    let result = driver::compile_input(&sess, &cstore, cfg, &input,
-                                       &out, &None, None, control);
-    match result {
-        Err(count) if count > 0 => sess.fatal("aborting due to previous error(s)"),
+
+    match {
+        let b_sess = AssertRecoverSafe::new(&sess);
+        let b_cstore = AssertRecoverSafe::new(&cstore);
+        let b_cfg = AssertRecoverSafe::new(cfg.clone());
+        let b_input = AssertRecoverSafe::new(&input);
+        let b_out = AssertRecoverSafe::new(&out);
+        let b_control = AssertRecoverSafe::new(&control);
+
+        panic::recover(|| {
+            AssertRecoverSafe::new(driver::compile_input(&b_sess, &b_cstore, (*b_cfg).clone(),
+                                                         &b_input, &b_out,
+                                                         &None, None, &b_control))
+        })
+    } {
+        Ok(r) => {
+            match *r {
+                Err(count) if count > 0 && compile_fail == false => {
+                    sess.fatal("aborting due to previous error(s)")
+                }
+                Ok(()) if compile_fail => panic!("test compiled while it wasn't supposed to"),
+                _ => {}
+            }
+        }
+        Err(_) if compile_fail == false => panic!("couldn't compile the test"),
         _ => {}
     }
 
@@ -265,7 +288,7 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
     // environment to ensure that the target loads the right libraries at
     // runtime. It would be a sad day if the *host* libraries were loaded as a
     // mistake.
-    let mut cmd = Command::new(&outdir.path().join("rust_out"));
+    let mut cmd = Command::new(&outdir.lock().unwrap().path().join("rust_out"));
     let var = DynamicLibrary::envvar();
     let newpath = {
         let path = env::var_os(var).unwrap_or(OsString::new());
@@ -389,7 +412,7 @@ impl Collector {
 
     pub fn add_test(&mut self, test: String,
                     should_panic: bool, no_run: bool, should_ignore: bool,
-                    as_test_harness: bool) {
+                    as_test_harness: bool, compile_fail: bool) {
         let name = if self.use_headers {
             let s = self.current_header.as_ref().map(|s| &**s).unwrap_or("");
             format!("{}_{}", s, self.cnt)
@@ -419,6 +442,7 @@ impl Collector {
                         should_panic,
                         no_run,
                         as_test_harness,
+                        compile_fail,
                         &opts);
             }))
         });
diff --git a/src/test/run-make/issue-19371/foo.rs b/src/test/run-make/issue-19371/foo.rs
index b2aed915458..43ae356feed 100644
--- a/src/test/run-make/issue-19371/foo.rs
+++ b/src/test/run-make/issue-19371/foo.rs
@@ -71,5 +71,5 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf) {
             &None,
             &Some(output),
             None,
-            control);
+            &control);
 }