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,11 +21,123 @@ 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; +enum OutputFormat { + HTML(HTMLFormatter), + Markdown(MarkdownFormatter), + Unknown(String), +} + +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()), + } + } +} + +trait Formatter { + fn header(&self, output: &mut Write) -> Result<(), Box>; + fn title(&self, output: &mut Write) -> Result<(), Box>; + fn error_code_block(&self, output: &mut Write, info: &ErrorMetadata, + err_code: &str) -> Result<(), Box>; + fn footer(&self, output: &mut Write) -> Result<(), Box>; +} + +struct HTMLFormatter; +struct MarkdownFormatter; + +impl Formatter for HTMLFormatter { + fn header(&self, output: &mut Write) -> Result<(), Box> { + try!(write!(output, r##" + + +Rust Compiler Error Index + + + + + + + +"##)); + Ok(()) + } + + fn title(&self, output: &mut Write) -> Result<(), Box> { + try!(write!(output, "

Rust Compiler Error Index

\n")); + Ok(()) + } + + fn error_code_block(&self, output: &mut Write, info: &ErrorMetadata, + err_code: &str) -> Result<(), Box> { + // Enclose each error in a div so they can be shown/hidden en masse. + let desc_desc = match info.description { + Some(_) => "error-described", + None => "error-undescribed", + }; + let use_desc = match info.use_site { + Some(_) => "error-used", + None => "error-unused", + }; + try!(write!(output, "
", desc_desc, use_desc)); + + // Error title (with self-link). + try!(write!(output, + "

{0}

\n", + err_code)); + + // Description rendered as markdown. + match info.description { + Some(ref desc) => try!(write!(output, "{}", Markdown(desc))), + None => try!(write!(output, "

No description.

\n")), + } + + try!(write!(output, "
\n")); + Ok(()) + } + + fn footer(&self, output: &mut Write) -> Result<(), Box> { + try!(write!(output, "\n")); + Ok(()) + } +} + +impl Formatter for MarkdownFormatter { + #[allow(unused_variables)] + fn header(&self, output: &mut Write) -> Result<(), Box> { + Ok(()) + } + + fn title(&self, output: &mut Write) -> Result<(), Box> { + try!(write!(output, "# Rust Compiler Error Index\n")); + Ok(()) + } + + fn error_code_block(&self, output: &mut Write, info: &ErrorMetadata, + err_code: &str) -> Result<(), Box> { + 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> { + Ok(()) + } +} + /// Load all the metadata files from `metadata_dir` into an in-memory map. fn load_all_errors(metadata_dir: &Path) -> Result> { let mut all_errors = BTreeMap::new(); @@ -47,71 +159,45 @@ fn load_all_errors(metadata_dir: &Path) -> Result> } /// 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> { +fn render_error_page(err_map: &ErrorMetadataMap, output_path: &Path, + formatter: T) -> Result<(), Box> { let mut output_file = try!(File::create(output_path)); - try!(write!(&mut output_file, -r##" - - -Rust Compiler Error Index - - - - - - - -"## - )); - - try!(write!(&mut output_file, "

Rust Compiler Error Index

\n")); + try!(formatter.header(&mut output_file)); + try!(formatter.title(&mut output_file)); for (err_code, info) in err_map { - // Enclose each error in a div so they can be shown/hidden en masse. - let desc_desc = match info.description { - Some(_) => "error-described", - None => "error-undescribed", - }; - let use_desc = match info.use_site { - Some(_) => "error-used", - None => "error-unused", - }; - try!(write!(&mut output_file, "
", desc_desc, use_desc)); - - // Error title (with self-link). - try!(write!(&mut output_file, - "

{0}

\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, "

No description.

\n")), - } - - try!(write!(&mut output_file, "
\n")); + try!(formatter.error_code_block(&mut output_file, info, err_code)); } - try!(write!(&mut output_file, "\n")); - - Ok(()) + formatter.footer(&mut output_file) } -fn main_with_result() -> Result<(), Box> { +fn main_with_result(format: OutputFormat) -> Result<(), Box> { 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, output: &Option, addl_plugins: Option>, - 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::>().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, 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, 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, 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); }