Handle fs errors through errors::Handler instead of eprintln and panic

This commit is contained in:
Guillaume Gomez 2019-05-26 14:54:50 +02:00
parent 6392bc9fcd
commit 3eeb543504
3 changed files with 67 additions and 13 deletions

View File

@ -3254,6 +3254,7 @@ dependencies = [
"minifier 0.0.30 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
] ]

View File

@ -9,9 +9,14 @@
//! needs to read-after-write from a file, then it would be added to this //! needs to read-after-write from a file, then it would be added to this
//! abstraction. //! abstraction.
use errors;
use std::cell::RefCell;
use std::fs; use std::fs;
use std::io; use std::io;
use std::path::Path; use std::path::Path;
use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver, Sender};
macro_rules! try_err { macro_rules! try_err {
($e:expr, $file:expr) => {{ ($e:expr, $file:expr) => {{
@ -26,14 +31,45 @@ pub trait PathError {
fn new<P: AsRef<Path>>(e: io::Error, path: P) -> Self; fn new<P: AsRef<Path>>(e: io::Error, path: P) -> Self;
} }
pub struct ErrorStorage {
sender: Sender<Option<String>>,
receiver: Receiver<Option<String>>,
}
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 { pub struct DocFS {
sync_only: bool, sync_only: bool,
errors: Arc<ErrorStorage>,
} }
impl DocFS { impl DocFS {
pub fn new() -> DocFS { pub fn new(errors: &Arc<ErrorStorage>) -> DocFS {
DocFS { DocFS {
sync_only: false, sync_only: false,
errors: Arc::clone(errors),
} }
} }
@ -59,16 +95,19 @@ impl DocFS {
// be to create the file sync so errors are reported eagerly. // be to create the file sync so errors are reported eagerly.
let contents = contents.as_ref().to_vec(); let contents = contents.as_ref().to_vec();
let path = path.as_ref().to_path_buf(); 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) { match fs::write(&path, &contents) {
Ok(_) => (), Ok(_) => {
Err(e) => { sender.send(None)
// In principle these should get displayed at the top .expect(&format!("failed to send error on \"{}\"", path.display()));
// level, but just in case, send to stderr as well.
eprintln!("\"{}\": {}", path.display(), e);
panic!("\"{}\": {}", path.display(), e);
} }
}); Err(e) => {
sender.send(Some(format!("\"{}\": {}", path.display(), e)))
.expect(&format!("failed to send non-error on \"{}\"", path.display()));
}
}
});
Ok(()) Ok(())
} else { } else {
Ok(try_err!(fs::write(&path, contents), path)) Ok(try_err!(fs::write(&path, contents), path))

View File

@ -61,7 +61,7 @@ use rustc_data_structures::flock;
use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy, Mutability}; use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy, Mutability};
use crate::config::RenderOptions; use crate::config::RenderOptions;
use crate::docfs::{DocFS, PathError}; use crate::docfs::{DocFS, ErrorStorage, PathError};
use crate::doctree; use crate::doctree;
use crate::fold::DocFolder; use crate::fold::DocFolder;
use crate::html::escape::Escape; use crate::html::escape::Escape;
@ -104,7 +104,12 @@ impl error::Error for Error {
impl Display for Error { impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 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(), _ => PathBuf::new(),
}; };
let errors = Arc::new(ErrorStorage::new());
let mut scx = SharedContext { let mut scx = SharedContext {
src_root, src_root,
passes, passes,
@ -567,7 +573,7 @@ pub fn run(mut krate: clean::Crate,
static_root_path, static_root_path,
generate_search_filter, generate_search_filter,
generate_redirect_pages, generate_redirect_pages,
fs: DocFS::new(), fs: DocFS::new(&errors),
}; };
// If user passed in `--playground-url` arg, we fill in crate name here // 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); Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false);
// And finally render the whole crate's documentation // 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 /// Builds the search index from the collected metadata