diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 6222edd5450f0..c9a80d4779177 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -117,6 +117,57 @@ impl DocAccessLevels for AccessLevels { } } +/// Creates a new diagnostic `Handler` that can be used to emit warnings and errors. +/// +/// If the given `error_format` is `ErrorOutputType::Json` and no `CodeMap` is given, a new one +/// will be created for the handler. +pub fn new_handler(error_format: ErrorOutputType, codemap: Option>) + -> errors::Handler +{ + // rustdoc doesn't override (or allow to override) anything from this that is relevant here, so + // stick to the defaults + let sessopts = config::basic_options(); + let emitter: Box = match error_format { + ErrorOutputType::HumanReadable(color_config) => Box::new( + EmitterWriter::stderr( + color_config, + codemap.map(|cm| cm as _), + false, + sessopts.debugging_opts.teach, + ).ui_testing(sessopts.debugging_opts.ui_testing) + ), + ErrorOutputType::Json(pretty) => { + let codemap = codemap.unwrap_or_else( + || Lrc::new(codemap::CodeMap::new(sessopts.file_path_mapping()))); + Box::new( + JsonEmitter::stderr( + None, + codemap, + pretty, + sessopts.debugging_opts.suggestion_applicability, + ).ui_testing(sessopts.debugging_opts.ui_testing) + ) + }, + ErrorOutputType::Short(color_config) => Box::new( + EmitterWriter::stderr( + color_config, + codemap.map(|cm| cm as _), + true, + false) + ), + }; + + errors::Handler::with_emitter_and_flags( + emitter, + errors::HandlerFlags { + can_emit_warnings: true, + treat_err_as_bug: false, + external_macro_backtrace: false, + ..Default::default() + }, + ) +} + pub fn run_core(search_paths: SearchPaths, cfgs: Vec, externs: config::Externs, @@ -159,41 +210,11 @@ pub fn run_core(search_paths: SearchPaths, }, error_format, edition, - ..config::basic_options().clone() + ..config::basic_options() }; driver::spawn_thread_pool(sessopts, move |sessopts| { let codemap = Lrc::new(codemap::CodeMap::new(sessopts.file_path_mapping())); - let emitter: Box = match error_format { - ErrorOutputType::HumanReadable(color_config) => Box::new( - EmitterWriter::stderr( - color_config, - Some(codemap.clone()), - false, - sessopts.debugging_opts.teach, - ).ui_testing(sessopts.debugging_opts.ui_testing) - ), - ErrorOutputType::Json(pretty) => Box::new( - JsonEmitter::stderr( - None, - codemap.clone(), - pretty, - sessopts.debugging_opts.suggestion_applicability, - ).ui_testing(sessopts.debugging_opts.ui_testing) - ), - ErrorOutputType::Short(color_config) => Box::new( - EmitterWriter::stderr(color_config, Some(codemap.clone()), true, false) - ), - }; - - let diagnostic_handler = errors::Handler::with_emitter_and_flags( - emitter, - errors::HandlerFlags { - can_emit_warnings: true, - treat_err_as_bug: false, - external_macro_backtrace: false, - ..Default::default() - }, - ); + let diagnostic_handler = new_handler(error_format, Some(codemap.clone())); let mut sess = session::build_session_( sessopts, cpath, diagnostic_handler, codemap, diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs index 6c328a87208aa..10b6c9850ae77 100644 --- a/src/librustdoc/externalfiles.rs +++ b/src/librustdoc/externalfiles.rs @@ -11,6 +11,7 @@ use std::fs; use std::path::Path; use std::str; +use errors; use html::markdown::Markdown; #[derive(Clone)] @@ -28,23 +29,23 @@ pub struct ExternalHtml { impl ExternalHtml { pub fn load(in_header: &[String], before_content: &[String], after_content: &[String], - md_before_content: &[String], md_after_content: &[String]) + md_before_content: &[String], md_after_content: &[String], diag: &errors::Handler) -> Option { - load_external_files(in_header) + load_external_files(in_header, diag) .and_then(|ih| - load_external_files(before_content) + load_external_files(before_content, diag) .map(|bc| (ih, bc)) ) .and_then(|(ih, bc)| - load_external_files(md_before_content) + load_external_files(md_before_content, diag) .map(|m_bc| (ih, format!("{}{}", bc, Markdown(&m_bc, &[])))) ) .and_then(|(ih, bc)| - load_external_files(after_content) + load_external_files(after_content, diag) .map(|ac| (ih, bc, ac)) ) .and_then(|(ih, bc, ac)| - load_external_files(md_after_content) + load_external_files(md_after_content, diag) .map(|m_ac| (ih, bc, format!("{}{}", ac, Markdown(&m_ac, &[])))) ) .map(|(ih, bc, ac)| @@ -62,28 +63,30 @@ pub enum LoadStringError { BadUtf8, } -pub fn load_string>(file_path: P) -> Result { +pub fn load_string>(file_path: P, diag: &errors::Handler) + -> Result +{ let file_path = file_path.as_ref(); let contents = match fs::read(file_path) { Ok(bytes) => bytes, Err(e) => { - eprintln!("error reading `{}`: {}", file_path.display(), e); + diag.struct_err(&format!("error reading `{}`: {}", file_path.display(), e)).emit(); return Err(LoadStringError::ReadFail); } }; match str::from_utf8(&contents) { Ok(s) => Ok(s.to_string()), Err(_) => { - eprintln!("error reading `{}`: not UTF-8", file_path.display()); + diag.struct_err(&format!("error reading `{}`: not UTF-8", file_path.display())).emit(); Err(LoadStringError::BadUtf8) } } } -fn load_external_files(names: &[String]) -> Option { +fn load_external_files(names: &[String], diag: &errors::Handler) -> Option { let mut out = String::new(); for name in names { - let s = match load_string(name) { + let s = match load_string(name, diag) { Ok(s) => s, Err(_) => return None, }; diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index d4244bfdba075..7d98feaf53947 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -57,15 +57,13 @@ use errors::ColorConfig; use std::collections::{BTreeMap, BTreeSet}; use std::default::Default; use std::env; -use std::fmt::Display; -use std::io; -use std::io::Write; use std::path::{Path, PathBuf}; use std::process; use std::sync::mpsc::channel; use syntax::edition::Edition; use externalfiles::ExternalHtml; +use rustc::session::{early_warn, early_error}; use rustc::session::search_paths::SearchPaths; use rustc::session::config::{ErrorOutputType, RustcOptGroup, Externs, CodegenOptions}; use rustc::session::config::{nightly_options, build_codegen_options}; @@ -119,7 +117,8 @@ pub fn main() { fn get_args() -> Option> { env::args_os().enumerate() .map(|(i, arg)| arg.into_string().map_err(|arg| { - print_error(format!("Argument {} is not valid Unicode: {:?}", i, arg)); + early_warn(ErrorOutputType::default(), + &format!("Argument {} is not valid Unicode: {:?}", i, arg)); }).ok()) .collect() } @@ -324,16 +323,12 @@ pub fn main_args(args: &[String]) -> isize { let matches = match options.parse(&args[1..]) { Ok(m) => m, Err(err) => { - print_error(err); - return 1; + early_error(ErrorOutputType::default(), &err.to_string()); } }; // Check for unstable options. nightly_options::check_nightly_options(&matches, &opts()); - // check for deprecated options - check_deprecated_options(&matches); - if matches.opt_present("h") || matches.opt_present("help") { usage("rustdoc"); return 0; @@ -354,6 +349,35 @@ pub fn main_args(args: &[String]) -> isize { return 0; } + let color = match matches.opt_str("color").as_ref().map(|s| &s[..]) { + Some("auto") => ColorConfig::Auto, + Some("always") => ColorConfig::Always, + Some("never") => ColorConfig::Never, + None => ColorConfig::Auto, + Some(arg) => { + early_error(ErrorOutputType::default(), + &format!("argument for --color must be `auto`, `always` or `never` \ + (instead was `{}`)", arg)); + } + }; + let error_format = match matches.opt_str("error-format").as_ref().map(|s| &s[..]) { + Some("human") => ErrorOutputType::HumanReadable(color), + Some("json") => ErrorOutputType::Json(false), + Some("pretty-json") => ErrorOutputType::Json(true), + Some("short") => ErrorOutputType::Short(color), + None => ErrorOutputType::HumanReadable(color), + Some(arg) => { + early_error(ErrorOutputType::default(), + &format!("argument for --error-format must be `human`, `json` or \ + `short` (instead was `{}`)", arg)); + } + }; + + let diag = core::new_handler(error_format, None); + + // check for deprecated options + check_deprecated_options(&matches, &diag); + let to_check = matches.opt_strs("theme-checker"); if !to_check.is_empty() { let paths = theme::load_css_paths(include_bytes!("html/static/themes/light.css")); @@ -362,7 +386,7 @@ pub fn main_args(args: &[String]) -> isize { println!("rustdoc: [theme-checker] Starting tests!"); for theme_file in to_check.iter() { print!(" - Checking \"{}\"...", theme_file); - let (success, differences) = theme::test_theme_against(theme_file, &paths); + let (success, differences) = theme::test_theme_against(theme_file, &paths, &diag); if !differences.is_empty() || !success { println!(" FAILED"); errors += 1; @@ -380,39 +404,15 @@ pub fn main_args(args: &[String]) -> isize { } if matches.free.is_empty() { - print_error("missing file operand"); + diag.struct_err("missing file operand").emit(); return 1; } if matches.free.len() > 1 { - print_error("too many file operands"); + diag.struct_err("too many file operands").emit(); return 1; } let input = &matches.free[0]; - let color = match matches.opt_str("color").as_ref().map(|s| &s[..]) { - Some("auto") => ColorConfig::Auto, - Some("always") => ColorConfig::Always, - Some("never") => ColorConfig::Never, - None => ColorConfig::Auto, - Some(arg) => { - print_error(&format!("argument for --color must be `auto`, `always` or `never` \ - (instead was `{}`)", arg)); - return 1; - } - }; - let error_format = match matches.opt_str("error-format").as_ref().map(|s| &s[..]) { - Some("human") => ErrorOutputType::HumanReadable(color), - Some("json") => ErrorOutputType::Json(false), - Some("pretty-json") => ErrorOutputType::Json(true), - Some("short") => ErrorOutputType::Short(color), - None => ErrorOutputType::HumanReadable(color), - Some(arg) => { - print_error(&format!("argument for --error-format must be `human`, `json` or \ - `short` (instead was `{}`)", arg)); - return 1; - } - }; - let mut libs = SearchPaths::new(); for s in &matches.opt_strs("L") { libs.add_path(s, error_format); @@ -420,7 +420,7 @@ pub fn main_args(args: &[String]) -> isize { let externs = match parse_externs(&matches) { Ok(ex) => ex, Err(err) => { - print_error(err); + diag.struct_err(&err.to_string()).emit(); return 1; } }; @@ -441,10 +441,7 @@ pub fn main_args(args: &[String]) -> isize { if let Some(ref p) = css_file_extension { if !p.is_file() { - writeln!( - &mut io::stderr(), - "rustdoc: option --extend-css argument must be a file." - ).unwrap(); + diag.struct_err("option --extend-css argument must be a file").emit(); return 1; } } @@ -457,13 +454,14 @@ pub fn main_args(args: &[String]) -> isize { .iter() .map(|s| (PathBuf::from(&s), s.to_owned())) { if !theme_file.is_file() { - println!("rustdoc: option --themes arguments must all be files"); + diag.struct_err("option --themes arguments must all be files").emit(); return 1; } - let (success, ret) = theme::test_theme_against(&theme_file, &paths); + let (success, ret) = theme::test_theme_against(&theme_file, &paths, &diag); if !success || !ret.is_empty() { - println!("rustdoc: invalid theme: \"{}\"", theme_s); - println!(" Check what's wrong with the \"theme-checker\" option"); + diag.struct_err(&format!("invalid theme: \"{}\"", theme_s)) + .help("check what's wrong with the --theme-checker option") + .emit(); return 1; } themes.push(theme_file); @@ -475,7 +473,7 @@ pub fn main_args(args: &[String]) -> isize { &matches.opt_strs("html-before-content"), &matches.opt_strs("html-after-content"), &matches.opt_strs("markdown-before-content"), - &matches.opt_strs("markdown-after-content")) { + &matches.opt_strs("markdown-after-content"), &diag) { Some(eh) => eh, None => return 3, }; @@ -492,7 +490,7 @@ pub fn main_args(args: &[String]) -> isize { let edition = match edition.parse() { Ok(e) => e, Err(_) => { - print_error("could not parse edition"); + diag.struct_err("could not parse edition").emit(); return 1; } }; @@ -502,7 +500,7 @@ pub fn main_args(args: &[String]) -> isize { match (should_test, markdown_input) { (true, true) => { return markdown::test(input, cfgs, libs, externs, test_args, maybe_sysroot, - display_warnings, linker, edition, cg) + display_warnings, linker, edition, cg, &diag) } (true, false) => { return test::run(Path::new(input), cfgs, libs, externs, test_args, crate_name, @@ -511,7 +509,7 @@ pub fn main_args(args: &[String]) -> isize { (false, true) => return markdown::render(Path::new(input), output.unwrap_or(PathBuf::from("doc")), &matches, &external_html, - !matches.opt_present("markdown-no-toc")), + !matches.opt_present("markdown-no-toc"), &diag), (false, false) => {} } @@ -520,6 +518,7 @@ pub fn main_args(args: &[String]) -> isize { let res = acquire_input(PathBuf::from(input), externs, edition, cg, &matches, error_format, move |out| { let Output { krate, passes, renderinfo } = out; + let diag = core::new_handler(error_format, None); info!("going to format"); match output_format.as_ref().map(|s| &**s) { Some("html") | None => { @@ -536,26 +535,17 @@ pub fn main_args(args: &[String]) -> isize { 0 } Some(s) => { - print_error(format!("unknown output format: {}", s)); + diag.struct_err(&format!("unknown output format: {}", s)).emit(); 1 } } }); res.unwrap_or_else(|s| { - print_error(format!("input error: {}", s)); + diag.struct_err(&format!("input error: {}", s)).emit(); 1 }) } -/// Prints an uniformized error message on the standard error output -fn print_error(error_message: T) where T: Display { - writeln!( - &mut io::stderr(), - "rustdoc: {}\nTry 'rustdoc --help' for more information.", - error_message - ).unwrap(); -} - /// Looks inside the command line arguments to extract the relevant input format /// and files and then generates the necessary rustdoc output for formatting. fn acquire_input(input: PathBuf, @@ -722,7 +712,7 @@ where R: 'static + Send, } /// Prints deprecation warnings for deprecated options -fn check_deprecated_options(matches: &getopts::Matches) { +fn check_deprecated_options(matches: &getopts::Matches, diag: &errors::Handler) { let deprecated_flags = [ "input-format", "output-format", @@ -734,12 +724,15 @@ fn check_deprecated_options(matches: &getopts::Matches) { for flag in deprecated_flags.into_iter() { if matches.opt_present(flag) { - eprintln!("WARNING: the '{}' flag is considered deprecated", flag); - eprintln!("WARNING: please see https://github.com/rust-lang/rust/issues/44136"); - } - } + let mut err = diag.struct_warn(&format!("the '{}' flag is considered deprecated", + flag)); + err.warn("please see https://github.com/rust-lang/rust/issues/44136"); - if matches.opt_present("no-defaults") { - eprintln!("WARNING: (you may want to use --document-private-items)"); + if *flag == "no-defaults" { + err.help("you may want to use --document-private-items"); + } + + err.emit(); + } } } diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 8ada5ce1a4df9..bf7b025884d5a 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -13,6 +13,7 @@ use std::fs::File; use std::io::prelude::*; use std::path::{PathBuf, Path}; +use errors; use getopts; use testing; use rustc::session::search_paths::SearchPaths; @@ -50,7 +51,7 @@ fn extract_leading_metadata<'a>(s: &'a str) -> (Vec<&'a str>, &'a str) { /// Render `input` (e.g. "foo.md") into an HTML file in `output` /// (e.g. output = "bar" => "bar/foo.html"). pub fn render(input: &Path, mut output: PathBuf, matches: &getopts::Matches, - external_html: &ExternalHtml, include_toc: bool) -> isize { + external_html: &ExternalHtml, include_toc: bool, diag: &errors::Handler) -> isize { output.push(input.file_stem().unwrap()); output.set_extension("html"); @@ -60,7 +61,7 @@ pub fn render(input: &Path, mut output: PathBuf, matches: &getopts::Matches, css.push_str(&s) } - let input_str = match load_string(input) { + let input_str = match load_string(input, diag) { Ok(s) => s, Err(LoadStringError::ReadFail) => return 1, Err(LoadStringError::BadUtf8) => return 2, @@ -72,7 +73,7 @@ pub fn render(input: &Path, mut output: PathBuf, matches: &getopts::Matches, let mut out = match File::create(&output) { Err(e) => { - eprintln!("rustdoc: {}: {}", output.display(), e); + diag.struct_err(&format!("{}: {}", output.display(), e)).emit(); return 4; } Ok(f) => f @@ -80,7 +81,7 @@ pub fn render(input: &Path, mut output: PathBuf, matches: &getopts::Matches, let (metadata, text) = extract_leading_metadata(&input_str); if metadata.is_empty() { - eprintln!("rustdoc: invalid markdown file: no initial lines starting with `# ` or `%`"); + diag.struct_err("invalid markdown file: no initial lines starting with `# ` or `%`").emit(); return 5; } let title = metadata[0]; @@ -130,7 +131,7 @@ pub fn render(input: &Path, mut output: PathBuf, matches: &getopts::Matches, match err { Err(e) => { - eprintln!("rustdoc: cannot write to `{}`: {}", output.display(), e); + diag.struct_err(&format!("cannot write to `{}`: {}", output.display(), e)).emit(); 6 } Ok(_) => 0, @@ -141,8 +142,8 @@ pub fn render(input: &Path, mut output: PathBuf, matches: &getopts::Matches, pub fn test(input: &str, cfgs: Vec, libs: SearchPaths, externs: Externs, mut test_args: Vec, maybe_sysroot: Option, display_warnings: bool, linker: Option, edition: Edition, - cg: CodegenOptions) -> isize { - let input_str = match load_string(input) { + cg: CodegenOptions, diag: &errors::Handler) -> isize { + let input_str = match load_string(input, diag) { Ok(s) => s, Err(LoadStringError::ReadFail) => return 1, Err(LoadStringError::BadUtf8) => return 2, diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs index 1e4f64f5c52c9..96a67e0788758 100644 --- a/src/librustdoc/theme.rs +++ b/src/librustdoc/theme.rs @@ -14,12 +14,14 @@ use std::hash::{Hash, Hasher}; use std::io::Read; use std::path::Path; +use errors::Handler; + macro_rules! try_something { - ($e:expr, $out:expr) => ({ + ($e:expr, $diag:expr, $out:expr) => ({ match $e { Ok(c) => c, Err(e) => { - eprintln!("rustdoc: got an error: {}", e); + $diag.struct_err(&e.to_string()).emit(); return $out; } } @@ -273,11 +275,13 @@ pub fn get_differences(against: &CssPath, other: &CssPath, v: &mut Vec) } } -pub fn test_theme_against>(f: &P, against: &CssPath) -> (bool, Vec) { - let mut file = try_something!(File::open(f), (false, Vec::new())); +pub fn test_theme_against>(f: &P, against: &CssPath, diag: &Handler) + -> (bool, Vec) +{ + let mut file = try_something!(File::open(f), diag, (false, Vec::new())); let mut data = Vec::with_capacity(1000); - try_something!(file.read_to_end(&mut data), (false, Vec::new())); + try_something!(file.read_to_end(&mut data), diag, (false, Vec::new())); let paths = load_css_paths(&data); let mut ret = Vec::new(); get_differences(against, &paths, &mut ret);