mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 16:54:01 +00:00
Add compile-fail test in rustdoc
This commit is contained in:
parent
d0ef740266
commit
eb5b9037ad
@ -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<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>>;
|
||||
}
|
||||
|
||||
struct HTMLFormatter;
|
||||
struct MarkdownFormatter;
|
||||
|
||||
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>
|
||||
<meta charset="utf-8">
|
||||
<!-- Include rust.css after main.css so its rules take priority. -->
|
||||
<link rel="stylesheet" type="text/css" href="main.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="rust.css"/>
|
||||
<style>
|
||||
.error-undescribed {{
|
||||
display: none;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
"##));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn title(&self, output: &mut Write) -> Result<(), Box<Error>> {
|
||||
try!(write!(output, "<h1>Rust Compiler Error Index</h1>\n"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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",
|
||||
None => "error-undescribed",
|
||||
};
|
||||
let use_desc = match info.use_site {
|
||||
Some(_) => "error-used",
|
||||
None => "error-unused",
|
||||
};
|
||||
try!(write!(output, "<div class=\"{} {}\">", desc_desc, use_desc));
|
||||
|
||||
// Error title (with self-link).
|
||||
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!(output, "{}", Markdown(desc))),
|
||||
None => try!(write!(output, "<p>No description.</p>\n")),
|
||||
}
|
||||
|
||||
try!(write!(output, "</div>\n"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn footer(&self, output: &mut Write) -> Result<(), Box<Error>> {
|
||||
try!(write!(output, "</body>\n</html>"));
|
||||
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();
|
||||
@ -47,71 +159,45 @@ fn load_all_errors(metadata_dir: &Path) -> Result<ErrorMetadataMap, 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>> {
|
||||
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!(write!(&mut output_file,
|
||||
r##"<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Rust Compiler Error Index</title>
|
||||
<meta charset="utf-8">
|
||||
<!-- Include rust.css after main.css so its rules take priority. -->
|
||||
<link rel="stylesheet" type="text/css" href="main.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="rust.css"/>
|
||||
<style>
|
||||
.error-undescribed {{
|
||||
display: none;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
"##
|
||||
));
|
||||
|
||||
try!(write!(&mut output_file, "<h1>Rust Compiler Error Index</h1>\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, "<div class=\"{} {}\">", desc_desc, use_desc));
|
||||
|
||||
// Error title (with self-link).
|
||||
try!(write!(&mut output_file,
|
||||
"<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")),
|
||||
}
|
||||
|
||||
try!(write!(&mut output_file, "</div>\n"));
|
||||
try!(formatter.error_code_block(&mut output_file, info, err_code));
|
||||
}
|
||||
|
||||
try!(write!(&mut output_file, "</body>\n</html>"));
|
||||
|
||||
Ok(())
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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)]
|
||||
|
||||
|
@ -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);
|
||||
}))
|
||||
});
|
||||
|
@ -71,5 +71,5 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf) {
|
||||
&None,
|
||||
&Some(output),
|
||||
None,
|
||||
control);
|
||||
&control);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user