Skip to content

Commit ae8b3e8

Browse files
committed
Introduce a diagnostic stashing API.
1 parent 66bf391 commit ae8b3e8

File tree

4 files changed

+102
-27
lines changed

4 files changed

+102
-27
lines changed

src/librustc/session/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ impl Session {
321321
}
322322
pub fn compile_status(&self) -> Result<(), ErrorReported> {
323323
if self.has_errors() {
324+
self.diagnostic().emit_stashed_diagnostics();
324325
Err(ErrorReported)
325326
} else {
326327
Ok(())

src/librustc_driver/lib.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ pub fn run_compiler(
296296
);
297297
Ok(())
298298
})?;
299-
return sess.compile_status();
300299
} else {
301300
let mut krate = compiler.parse()?.take();
302301
pretty::visit_crate(sess, &mut krate, ppm);
@@ -307,8 +306,8 @@ pub fn run_compiler(
307306
ppm,
308307
compiler.output_file().as_ref().map(|p| &**p),
309308
);
310-
return sess.compile_status();
311309
}
310+
return sess.compile_status();
312311
}
313312

314313
if callbacks.after_parsing(compiler) == Compilation::Stop {

src/librustc_errors/diagnostic_builder.rs

+28-13
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
use crate::Diagnostic;
2-
use crate::DiagnosticId;
3-
use crate::DiagnosticStyledString;
4-
use crate::Applicability;
1+
use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString};
2+
use crate::{Applicability, Level, Handler, StashKey};
53

6-
use crate::Level;
7-
use crate::Handler;
84
use std::fmt::{self, Debug};
95
use std::ops::{Deref, DerefMut};
106
use std::thread::panicking;
@@ -117,18 +113,30 @@ impl<'a> DiagnosticBuilder<'a> {
117113
}
118114
}
119115

120-
/// Buffers the diagnostic for later emission, unless handler
121-
/// has disabled such buffering.
122-
pub fn buffer(mut self, buffered_diagnostics: &mut Vec<Diagnostic>) {
116+
/// Stashes diagnostic for possible later improvement in a different,
117+
/// later stage of the compiler. The diagnostic can be accessed with
118+
/// the provided `span` and `key` through `.steal_diagnostic` on `Handler`.
119+
///
120+
/// As with `buffer`, this is unless the handler has disabled such buffering.
121+
pub fn stash(self, span: Span, key: StashKey) {
122+
if let Some((diag, handler)) = self.into_diagnostic() {
123+
handler.stash_diagnostic(span, key, diag);
124+
}
125+
}
126+
127+
/// Converts the builder to a `Diagnostic` for later emission,
128+
/// unless handler has disabled such buffering.
129+
pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> {
123130
if self.0.handler.flags.dont_buffer_diagnostics ||
124131
self.0.handler.flags.treat_err_as_bug.is_some()
125132
{
126133
self.emit();
127-
return;
134+
return None;
128135
}
129136

130-
// We need to use `ptr::read` because `DiagnosticBuilder`
131-
// implements `Drop`.
137+
let handler = self.0.handler;
138+
139+
// We need to use `ptr::read` because `DiagnosticBuilder` implements `Drop`.
132140
let diagnostic;
133141
unsafe {
134142
diagnostic = std::ptr::read(&self.0.diagnostic);
@@ -137,7 +145,14 @@ impl<'a> DiagnosticBuilder<'a> {
137145
// Logging here is useful to help track down where in logs an error was
138146
// actually emitted.
139147
debug!("buffer: diagnostic={:?}", diagnostic);
140-
buffered_diagnostics.push(diagnostic);
148+
149+
Some((diagnostic, handler))
150+
}
151+
152+
/// Buffers the diagnostic for later emission,
153+
/// unless handler has disabled such buffering.
154+
pub fn buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>) {
155+
buffered_diagnostics.extend(self.into_diagnostic().map(|(diag, _)| diag));
141156
}
142157

143158
/// Convenience function for internal use, clients should use one of the

src/librustc_errors/lib.rs

+72-12
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use emitter::{Emitter, EmitterWriter};
1717
use registry::Registry;
1818

1919
use rustc_data_structures::sync::{self, Lrc, Lock};
20-
use rustc_data_structures::fx::FxHashSet;
20+
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
2121
use rustc_data_structures::stable_hasher::StableHasher;
2222

2323
use std::borrow::Cow;
@@ -326,6 +326,18 @@ struct HandlerInner {
326326
/// this handler. These hashes is used to avoid emitting the same error
327327
/// twice.
328328
emitted_diagnostics: FxHashSet<u128>,
329+
330+
/// Stashed diagnostics emitted in one stage of the compiler that may be
331+
/// stolen by other stages (e.g. to improve them and add more information).
332+
/// The stashed diagnostics count towards the total error count.
333+
/// When `.abort_if_errors()` is called, these are also emitted.
334+
stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>,
335+
}
336+
337+
/// A key denoting where from a diagnostic was stashed.
338+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
339+
pub enum StashKey {
340+
ItemNoType,
329341
}
330342

331343
fn default_track_diagnostic(_: &Diagnostic) {}
@@ -354,7 +366,9 @@ pub struct HandlerFlags {
354366

355367
impl Drop for HandlerInner {
356368
fn drop(&mut self) {
357-
if self.err_count == 0 {
369+
self.emit_stashed_diagnostics();
370+
371+
if !self.has_errors() {
358372
let bugs = std::mem::replace(&mut self.delayed_span_bugs, Vec::new());
359373
let has_bugs = !bugs.is_empty();
360374
for bug in bugs {
@@ -419,6 +433,7 @@ impl Handler {
419433
taught_diagnostics: Default::default(),
420434
emitted_diagnostic_codes: Default::default(),
421435
emitted_diagnostics: Default::default(),
436+
stashed_diagnostics: Default::default(),
422437
}),
423438
}
424439
}
@@ -445,6 +460,31 @@ impl Handler {
445460
inner.emitted_diagnostics = Default::default();
446461
inner.deduplicated_err_count = 0;
447462
inner.err_count = 0;
463+
inner.stashed_diagnostics.clear();
464+
}
465+
466+
/// Stash a given diagnostic with the given `Span` and `StashKey` as the key for later stealing.
467+
/// If the diagnostic with this `(span, key)` already exists, this will result in an ICE.
468+
pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: Diagnostic) {
469+
if let Some(old) = self.inner.borrow_mut().stashed_diagnostics.insert((span, key), diag) {
470+
// We are removing a previously stashed diagnostic which should not happen.
471+
// Create a builder and drop it on the floor to get an ICE.
472+
drop(DiagnosticBuilder::new_diagnostic(self, old));
473+
}
474+
}
475+
476+
/// Steal a previously stashed diagnostic with the given `Span` and `StashKey` as the key.
477+
pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_>> {
478+
self.inner
479+
.borrow_mut()
480+
.stashed_diagnostics
481+
.remove(&(span, key))
482+
.map(|diag| DiagnosticBuilder::new_diagnostic(self, diag))
483+
}
484+
485+
/// Emit all stashed diagnostics.
486+
pub fn emit_stashed_diagnostics(&self) {
487+
self.inner.borrow_mut().emit_stashed_diagnostics();
448488
}
449489

450490
pub fn struct_dummy(&self) -> DiagnosticBuilder<'_> {
@@ -617,23 +657,23 @@ impl Handler {
617657
}
618658

619659
pub fn err_count(&self) -> usize {
620-
self.inner.borrow().err_count
660+
self.inner.borrow().err_count()
621661
}
622662

623663
pub fn has_errors(&self) -> bool {
624-
self.err_count() > 0
664+
self.inner.borrow().has_errors()
625665
}
626666

627667
pub fn print_error_count(&self, registry: &Registry) {
628668
self.inner.borrow_mut().print_error_count(registry)
629669
}
630670

631671
pub fn abort_if_errors(&self) {
632-
self.inner.borrow().abort_if_errors()
672+
self.inner.borrow_mut().abort_if_errors()
633673
}
634674

635675
pub fn abort_if_errors_and_should_abort(&self) {
636-
self.inner.borrow().abort_if_errors_and_should_abort()
676+
self.inner.borrow_mut().abort_if_errors_and_should_abort()
637677
}
638678

639679
pub fn must_teach(&self, code: &DiagnosticId) -> bool {
@@ -671,6 +711,12 @@ impl HandlerInner {
671711
self.emitter.emit_diagnostic(&db);
672712
}
673713

714+
/// Emit all stashed diagnostics.
715+
fn emit_stashed_diagnostics(&mut self) {
716+
let diags = self.stashed_diagnostics.drain(..).map(|x| x.1).collect::<Vec<_>>();
717+
diags.iter().for_each(|diag| self.emit_diagnostic(diag));
718+
}
719+
674720
fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) {
675721
if diagnostic.cancelled() {
676722
return;
@@ -713,10 +759,12 @@ impl HandlerInner {
713759
}
714760

715761
fn treat_err_as_bug(&self) -> bool {
716-
self.flags.treat_err_as_bug.map(|c| self.err_count >= c).unwrap_or(false)
762+
self.flags.treat_err_as_bug.map(|c| self.err_count() >= c).unwrap_or(false)
717763
}
718764

719765
fn print_error_count(&mut self, registry: &Registry) {
766+
self.emit_stashed_diagnostics();
767+
720768
let s = match self.deduplicated_err_count {
721769
0 => return,
722770
1 => "aborting due to previous error".to_string(),
@@ -760,14 +808,26 @@ impl HandlerInner {
760808
}
761809
}
762810

763-
fn abort_if_errors_and_should_abort(&self) {
764-
if self.err_count > 0 && !self.continue_after_error {
811+
fn err_count(&self) -> usize {
812+
self.err_count + self.stashed_diagnostics.len()
813+
}
814+
815+
fn has_errors(&self) -> bool {
816+
self.err_count() > 0
817+
}
818+
819+
fn abort_if_errors_and_should_abort(&mut self) {
820+
self.emit_stashed_diagnostics();
821+
822+
if self.has_errors() && !self.continue_after_error {
765823
FatalError.raise();
766824
}
767825
}
768826

769-
fn abort_if_errors(&self) {
770-
if self.err_count > 0 {
827+
fn abort_if_errors(&mut self) {
828+
self.emit_stashed_diagnostics();
829+
830+
if self.has_errors() {
771831
FatalError.raise();
772832
}
773833
}
@@ -826,7 +886,7 @@ impl HandlerInner {
826886

827887
fn panic_if_treat_err_as_bug(&self) {
828888
if self.treat_err_as_bug() {
829-
let s = match (self.err_count, self.flags.treat_err_as_bug.unwrap_or(0)) {
889+
let s = match (self.err_count(), self.flags.treat_err_as_bug.unwrap_or(0)) {
830890
(0, _) => return,
831891
(1, 1) => "aborting due to `-Z treat-err-as-bug=1`".to_string(),
832892
(1, _) => return,

0 commit comments

Comments
 (0)