Skip to content

Commit 7621a5b

Browse files
committed
Refactor DocFS to fix error handling bugs
1 parent cee8023 commit 7621a5b

File tree

3 files changed

+34
-55
lines changed

3 files changed

+34
-55
lines changed

src/librustdoc/docfs.rs

+16-45
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ use std::fs;
1313
use std::io;
1414
use std::path::Path;
1515
use std::string::ToString;
16-
use std::sync::mpsc::{channel, Receiver, Sender};
17-
use std::sync::Arc;
16+
use std::sync::mpsc::Sender;
1817

1918
macro_rules! try_err {
2019
($e:expr, $file:expr) => {
@@ -31,47 +30,24 @@ pub trait PathError {
3130
S: ToString + Sized;
3231
}
3332

34-
pub struct ErrorStorage {
35-
sender: Option<Sender<Option<String>>>,
36-
receiver: Receiver<Option<String>>,
37-
}
38-
39-
impl ErrorStorage {
40-
pub fn new() -> ErrorStorage {
41-
let (sender, receiver) = channel();
42-
ErrorStorage { sender: Some(sender), receiver }
43-
}
44-
45-
/// Prints all stored errors. Returns the number of printed errors.
46-
pub fn write_errors(&mut self, diag: &rustc_errors::Handler) -> usize {
47-
let mut printed = 0;
48-
// In order to drop the sender part of the channel.
49-
self.sender = None;
50-
51-
for msg in self.receiver.iter() {
52-
if let Some(ref error) = msg {
53-
diag.struct_err(&error).emit();
54-
printed += 1;
55-
}
56-
}
57-
printed
58-
}
59-
}
60-
6133
pub struct DocFS {
6234
sync_only: bool,
63-
errors: Arc<ErrorStorage>,
35+
errors: Option<Sender<String>>,
6436
}
6537

6638
impl DocFS {
67-
pub fn new(errors: &Arc<ErrorStorage>) -> DocFS {
68-
DocFS { sync_only: false, errors: Arc::clone(errors) }
39+
pub fn new(errors: &Sender<String>) -> DocFS {
40+
DocFS { sync_only: false, errors: Some(errors.clone()) }
6941
}
7042

7143
pub fn set_sync_only(&mut self, sync_only: bool) {
7244
self.sync_only = sync_only;
7345
}
7446

47+
pub fn close(&mut self) {
48+
self.errors = None;
49+
}
50+
7551
pub fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
7652
// For now, dir creation isn't a huge time consideration, do it
7753
// synchronously, which avoids needing ordering between write() actions
@@ -88,20 +64,15 @@ impl DocFS {
8864
if !self.sync_only && cfg!(windows) {
8965
// A possible future enhancement after more detailed profiling would
9066
// be to create the file sync so errors are reported eagerly.
91-
let contents = contents.as_ref().to_vec();
9267
let path = path.as_ref().to_path_buf();
93-
let sender = self.errors.sender.clone().unwrap();
94-
rayon::spawn(move || match fs::write(&path, &contents) {
95-
Ok(_) => {
96-
sender.send(None).unwrap_or_else(|_| {
97-
panic!("failed to send error on \"{}\"", path.display())
98-
});
99-
}
100-
Err(e) => {
101-
sender.send(Some(format!("\"{}\": {}", path.display(), e))).unwrap_or_else(
102-
|_| panic!("failed to send non-error on \"{}\"", path.display()),
103-
);
104-
}
68+
let contents = contents.as_ref().to_vec();
69+
let sender = self.errors.clone().expect("can't write after closing");
70+
rayon::spawn(move || {
71+
fs::write(&path, contents).unwrap_or_else(|e| {
72+
sender
73+
.send(format!("\"{}\": {}", path.display(), e))
74+
.expect(&format!("failed to send error on \"{}\"", path.display()));
75+
});
10576
});
10677
Ok(())
10778
} else {

src/librustdoc/html/render/mod.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ use std::path::{Component, Path, PathBuf};
4444
use std::rc::Rc;
4545
use std::str;
4646
use std::string::ToString;
47+
use std::sync::mpsc::{channel, Receiver};
4748
use std::sync::Arc;
4849

4950
use itertools::Itertools;
@@ -65,7 +66,7 @@ use serde::{Serialize, Serializer};
6566
use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy, TypeKind};
6667
use crate::config::RenderInfo;
6768
use crate::config::RenderOptions;
68-
use crate::docfs::{DocFS, ErrorStorage, PathError};
69+
use crate::docfs::{DocFS, PathError};
6970
use crate::doctree;
7071
use crate::error::Error;
7172
use crate::formats::cache::{cache, Cache};
@@ -113,7 +114,9 @@ crate struct Context {
113114
id_map: Rc<RefCell<IdMap>>,
114115
pub shared: Arc<SharedContext>,
115116
all: Rc<RefCell<AllTypes>>,
116-
pub errors: Arc<ErrorStorage>,
117+
/// Storage for the errors produced while generating documentation so they
118+
/// can be printed together at the end.
119+
pub errors: Rc<Receiver<String>>,
117120
}
118121

119122
crate struct SharedContext {
@@ -403,7 +406,6 @@ impl FormatRenderer for Context {
403406
},
404407
_ => PathBuf::new(),
405408
};
406-
let errors = Arc::new(ErrorStorage::new());
407409
// If user passed in `--playground-url` arg, we fill in crate name here
408410
let mut playground = None;
409411
if let Some(url) = playground_url {
@@ -447,6 +449,7 @@ impl FormatRenderer for Context {
447449
}
448450
}
449451
}
452+
let (sender, receiver) = channel();
450453
let mut scx = SharedContext {
451454
collapsed: krate.collapsed,
452455
src_root,
@@ -459,7 +462,7 @@ impl FormatRenderer for Context {
459462
style_files,
460463
resource_suffix,
461464
static_root_path,
462-
fs: DocFS::new(&errors),
465+
fs: DocFS::new(&sender),
463466
edition,
464467
codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()),
465468
playground,
@@ -493,7 +496,7 @@ impl FormatRenderer for Context {
493496
id_map: Rc::new(RefCell::new(id_map)),
494497
shared: Arc::new(scx),
495498
all: Rc::new(RefCell::new(AllTypes::new())),
496-
errors,
499+
errors: Rc::new(receiver),
497500
};
498501

499502
CURRENT_DEPTH.with(|s| s.set(0));
@@ -506,8 +509,8 @@ impl FormatRenderer for Context {
506509
}
507510

508511
fn after_run(&mut self, diag: &rustc_errors::Handler) -> Result<(), Error> {
509-
let nb_errors =
510-
Arc::get_mut(&mut self.errors).map_or_else(|| 0, |errors| errors.write_errors(diag));
512+
Arc::get_mut(&mut self.shared).unwrap().fs.close();
513+
let nb_errors = self.errors.iter().map(|err| diag.struct_err(&err).emit()).count();
511514
if nb_errors > 0 {
512515
Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), ""))
513516
} else {

src/librustdoc/lib.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -507,9 +507,14 @@ fn main_options(options: config::Options) -> i32 {
507507
) {
508508
Ok(_) => rustc_driver::EXIT_SUCCESS,
509509
Err(e) => {
510-
diag.struct_err(&format!("couldn't generate documentation: {}", e.error))
511-
.note(&format!("failed to create or modify \"{}\"", e.file.display()))
512-
.emit();
510+
let mut msg =
511+
diag.struct_err(&format!("couldn't generate documentation: {}", e.error));
512+
let file = e.file.display().to_string();
513+
if file.is_empty() {
514+
msg.emit()
515+
} else {
516+
msg.note(&format!("failed to create or modify \"{}\"", file)).emit()
517+
}
513518
rustc_driver::EXIT_FAILURE
514519
}
515520
}

0 commit comments

Comments
 (0)