From 3eeb543504602d04c2f4374834ee9c67bb525300 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 26 May 2019 14:54:50 +0200 Subject: [PATCH] Handle fs errors through errors::Handler instead of eprintln and panic --- Cargo.lock | 1 + src/librustdoc/docfs.rs | 55 ++++++++++++++++++++++++++++++----- src/librustdoc/html/render.rs | 22 +++++++++++--- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cf448937e03..40b8cf507e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3254,6 +3254,7 @@ dependencies = [ "minifier 0.0.30 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "pulldown-cmark 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-rayon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/src/librustdoc/docfs.rs b/src/librustdoc/docfs.rs index eddd426a920..96399b8e36f 100644 --- a/src/librustdoc/docfs.rs +++ b/src/librustdoc/docfs.rs @@ -9,9 +9,14 @@ //! needs to read-after-write from a file, then it would be added to this //! abstraction. +use errors; + +use std::cell::RefCell; use std::fs; use std::io; use std::path::Path; +use std::sync::Arc; +use std::sync::mpsc::{channel, Receiver, Sender}; macro_rules! try_err { ($e:expr, $file:expr) => {{ @@ -26,14 +31,45 @@ pub trait PathError { fn new>(e: io::Error, path: P) -> Self; } +pub struct ErrorStorage { + sender: Sender>, + receiver: Receiver>, +} + +impl ErrorStorage { + pub fn new() -> ErrorStorage { + let (sender, receiver) = channel(); + ErrorStorage { + sender, + receiver, + } + } + + /// Prints all stored errors. Returns the number of printed errors. + pub fn write_errors(&self, diag: &errors::Handler) -> usize { + let mut printed = 0; + drop(self.sender); + + for msg in self.receiver.iter() { + if let Some(ref error) = msg { + diag.struct_err(&error).emit(); + printed += 1; + } + } + printed + } +} + pub struct DocFS { sync_only: bool, + errors: Arc, } impl DocFS { - pub fn new() -> DocFS { + pub fn new(errors: &Arc) -> DocFS { DocFS { sync_only: false, + errors: Arc::clone(errors), } } @@ -59,16 +95,19 @@ pub fn write(&self, path: P, contents: C) -> Result<(), E> // be to create the file sync so errors are reported eagerly. let contents = contents.as_ref().to_vec(); let path = path.as_ref().to_path_buf(); - rayon::spawn(move || + let sender = self.errors.sender.clone(); + rayon::spawn(move || { match fs::write(&path, &contents) { - Ok(_) => (), + Ok(_) => { + sender.send(None) + .expect(&format!("failed to send error on \"{}\"", path.display())); + } Err(e) => { - // In principle these should get displayed at the top - // level, but just in case, send to stderr as well. - eprintln!("\"{}\": {}", path.display(), e); - panic!("\"{}\": {}", path.display(), e); + sender.send(Some(format!("\"{}\": {}", path.display(), e))) + .expect(&format!("failed to send non-error on \"{}\"", path.display())); } - }); + } + }); Ok(()) } else { Ok(try_err!(fs::write(&path, contents), path)) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 8b514cb09ae..fe5fb4d73d8 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -61,7 +61,7 @@ use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy, Mutability}; use crate::config::RenderOptions; -use crate::docfs::{DocFS, PathError}; +use crate::docfs::{DocFS, ErrorStorage, PathError}; use crate::doctree; use crate::fold::DocFolder; use crate::html::escape::Escape; @@ -104,7 +104,12 @@ fn description(&self) -> &str { impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "\"{}\": {}", self.file.display(), self.error) + let file = self.file.display().to_string(); + if file.is_empty() { + write!(f, "{}", self.error) + } else { + write!(f, "\"{}\": {}", self.file.display(), self.error) + } } } @@ -547,6 +552,7 @@ pub fn run(mut krate: clean::Crate, }, _ => PathBuf::new(), }; + let errors = Arc::new(ErrorStorage::new()); let mut scx = SharedContext { src_root, passes, @@ -567,7 +573,7 @@ pub fn run(mut krate: clean::Crate, static_root_path, generate_search_filter, generate_redirect_pages, - fs: DocFS::new(), + fs: DocFS::new(&errors), }; // If user passed in `--playground-url` arg, we fill in crate name here @@ -715,7 +721,15 @@ pub fn run(mut krate: clean::Crate, Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false); // And finally render the whole crate's documentation - cx.krate(krate) + let ret = cx.krate(krate); + let nb_errors = errors.write_errors(diag); + if ret.is_err() { + ret + } else if nb_errors > 0 { + Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), "")) + } else { + Ok(()) + } } /// Builds the search index from the collected metadata -- 2.44.0