Skip to content

Commit f24ff18

Browse files
committed
rustc_errors: add downgrade_to_delayed_bug to Diagnostic itself.
1 parent 5bd1ec3 commit f24ff18

File tree

10 files changed

+129
-48
lines changed

10 files changed

+129
-48
lines changed

compiler/rustc_codegen_ssa/src/back/write.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1709,7 +1709,7 @@ impl Emitter for SharedEmitter {
17091709
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
17101710
msg: diag.message(),
17111711
code: diag.code.clone(),
1712-
lvl: diag.level,
1712+
lvl: diag.level(),
17131713
})));
17141714
for child in &diag.children {
17151715
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {

compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ fn source_string(file: Lrc<SourceFile>, line: &Line) -> String {
6666
/// Maps `Diagnostic::Level` to `snippet::AnnotationType`
6767
fn annotation_type_for_level(level: Level) -> AnnotationType {
6868
match level {
69-
Level::Bug | Level::Fatal | Level::Error { .. } => AnnotationType::Error,
69+
Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error { .. } => {
70+
AnnotationType::Error
71+
}
7072
Level::Warning => AnnotationType::Warning,
7173
Level::Note => AnnotationType::Note,
7274
Level::Help => AnnotationType::Help,

compiler/rustc_errors/src/diagnostic.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ pub struct SuggestionsDisabled;
1919
#[must_use]
2020
#[derive(Clone, Debug, Encodable, Decodable)]
2121
pub struct Diagnostic {
22-
pub level: Level,
22+
// NOTE(eddyb) this is private to disallow arbitrary after-the-fact changes,
23+
// outside of what methods in this crate themselves allow.
24+
crate level: Level,
25+
2326
pub message: Vec<(String, Style)>,
2427
pub code: Option<DiagnosticId>,
2528
pub span: MultiSpan,
@@ -117,9 +120,18 @@ impl Diagnostic {
117120
}
118121
}
119122

123+
#[inline(always)]
124+
pub fn level(&self) -> Level {
125+
self.level
126+
}
127+
120128
pub fn is_error(&self) -> bool {
121129
match self.level {
122-
Level::Bug | Level::Fatal | Level::Error { .. } | Level::FailureNote => true,
130+
Level::Bug
131+
| Level::DelayedBug
132+
| Level::Fatal
133+
| Level::Error { .. }
134+
| Level::FailureNote => true,
123135

124136
Level::Warning | Level::Note | Level::Help | Level::Cancelled | Level::Allow => false,
125137
}
@@ -150,6 +162,33 @@ impl Diagnostic {
150162
self.level == Level::Cancelled
151163
}
152164

165+
/// Delay emission of this diagnostic as a bug.
166+
///
167+
/// This can be useful in contexts where an error indicates a bug but
168+
/// typically this only happens when other compilation errors have already
169+
/// happened. In those cases this can be used to defer emission of this
170+
/// diagnostic as a bug in the compiler only if no other errors have been
171+
/// emitted.
172+
///
173+
/// In the meantime, though, callsites are required to deal with the "bug"
174+
/// locally in whichever way makes the most sense.
175+
#[track_caller]
176+
pub fn downgrade_to_delayed_bug(&mut self) -> &mut Self {
177+
// FIXME(eddyb) this check is only necessary because cancellation exists,
178+
// but hopefully that can be removed in the future, if enough callers
179+
// of `.cancel()` can take `DiagnosticBuilder`, and by-value.
180+
if !self.cancelled() {
181+
assert!(
182+
self.is_error(),
183+
"downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
184+
self.level
185+
);
186+
self.level = Level::DelayedBug;
187+
}
188+
189+
self
190+
}
191+
153192
/// Adds a span/label to be included in the resulting snippet.
154193
///
155194
/// This is pushed onto the [`MultiSpan`] that was created when the diagnostic

compiler/rustc_errors/src/diagnostic_builder.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,9 @@ impl<'a> DiagnosticBuilder<'a> {
105105
/// See `emit` and `delay_as_bug` for details.
106106
pub fn emit_unless(&mut self, delay: bool) {
107107
if delay {
108-
self.delay_as_bug();
109-
} else {
110-
self.emit();
108+
self.downgrade_to_delayed_bug();
111109
}
110+
self.emit();
112111
}
113112

114113
/// Stashes diagnostic for possible later improvement in a different,
@@ -162,12 +161,17 @@ impl<'a> DiagnosticBuilder<'a> {
162161
///
163162
/// In the meantime, though, callsites are required to deal with the "bug"
164163
/// locally in whichever way makes the most sense.
164+
#[track_caller]
165165
pub fn delay_as_bug(&mut self) {
166-
self.level = Level::Bug;
167-
self.handler.delay_as_bug((*self.diagnostic).clone());
168-
self.cancel();
166+
self.downgrade_to_delayed_bug();
167+
self.emit();
169168
}
170169

170+
forward!(
171+
#[track_caller]
172+
pub fn downgrade_to_delayed_bug(&mut self,) -> &mut Self
173+
);
174+
171175
/// Appends a labeled span to the diagnostic.
172176
///
173177
/// Labels are used to convey additional context for the diagnostic's primary span. They will

compiler/rustc_errors/src/lib.rs

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -491,10 +491,15 @@ impl Drop for HandlerInner {
491491
self.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued");
492492
}
493493

494+
// FIXME(eddyb) this explains what `delayed_good_path_bugs` are!
495+
// They're `delayed_span_bugs` but for "require some diagnostic happened"
496+
// instead of "require some error happened". Sadly that isn't ideal, as
497+
// lints can be `#[allow]`'d, potentially leading to this triggering.
498+
// Also, "good path" should be replaced with a better naming.
494499
if !self.has_any_message() {
495500
let bugs = std::mem::replace(&mut self.delayed_good_path_bugs, Vec::new());
496501
self.flush_delayed(
497-
bugs.into_iter().map(DelayedDiagnostic::decorate).collect(),
502+
bugs.into_iter().map(DelayedDiagnostic::decorate),
498503
"no warnings or errors encountered even though `delayed_good_path_bugs` issued",
499504
);
500505
}
@@ -815,6 +820,8 @@ impl Handler {
815820
self.inner.borrow_mut().delay_span_bug(span, msg)
816821
}
817822

823+
// FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's
824+
// where the explanation of what "good path" is (also, it should be renamed).
818825
pub fn delay_good_path_bug(&self, msg: &str) {
819826
self.inner.borrow_mut().delay_good_path_bug(msg)
820827
}
@@ -915,10 +922,6 @@ impl Handler {
915922
pub fn emit_unused_externs(&self, lint_level: &str, unused_externs: &[&str]) {
916923
self.inner.borrow_mut().emit_unused_externs(lint_level, unused_externs)
917924
}
918-
919-
pub fn delay_as_bug(&self, diagnostic: Diagnostic) {
920-
self.inner.borrow_mut().delay_as_bug(diagnostic)
921-
}
922925
}
923926

924927
impl HandlerInner {
@@ -936,11 +939,24 @@ impl HandlerInner {
936939
diags.iter().for_each(|diag| self.emit_diagnostic(diag));
937940
}
938941

942+
// FIXME(eddyb) this should ideally take `diagnostic` by value.
939943
fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) {
940944
if diagnostic.cancelled() {
941945
return;
942946
}
943947

948+
if diagnostic.level == Level::DelayedBug {
949+
// FIXME(eddyb) this should check for `has_errors` and stop pushing
950+
// once *any* errors were emitted (and truncate `delayed_span_bugs`
951+
// when an error is first emitted, also), but maybe there's a case
952+
// in which that's not sound? otherwise this is really inefficient.
953+
self.delayed_span_bugs.push(diagnostic.clone());
954+
955+
if !self.flags.report_delayed_bugs {
956+
return;
957+
}
958+
}
959+
944960
if diagnostic.has_future_breakage() {
945961
self.future_breakage_diagnostics.push(diagnostic.clone());
946962
}
@@ -1119,14 +1135,16 @@ impl HandlerInner {
11191135
// FIXME: don't abort here if report_delayed_bugs is off
11201136
self.span_bug(sp, msg);
11211137
}
1122-
let mut diagnostic = Diagnostic::new(Level::Bug, msg);
1138+
let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg);
11231139
diagnostic.set_span(sp.into());
11241140
diagnostic.note(&format!("delayed at {}", std::panic::Location::caller()));
1125-
self.delay_as_bug(diagnostic)
1141+
self.emit_diagnostic(&diagnostic)
11261142
}
11271143

1144+
// FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's
1145+
// where the explanation of what "good path" is (also, it should be renamed).
11281146
fn delay_good_path_bug(&mut self, msg: &str) {
1129-
let diagnostic = Diagnostic::new(Level::Bug, msg);
1147+
let diagnostic = Diagnostic::new(Level::DelayedBug, msg);
11301148
if self.flags.report_delayed_bugs {
11311149
self.emit_diagnostic(&diagnostic);
11321150
}
@@ -1160,20 +1178,34 @@ impl HandlerInner {
11601178
panic::panic_any(ExplicitBug);
11611179
}
11621180

1163-
fn delay_as_bug(&mut self, diagnostic: Diagnostic) {
1164-
if self.flags.report_delayed_bugs {
1165-
self.emit_diagnostic(&diagnostic);
1166-
}
1167-
self.delayed_span_bugs.push(diagnostic);
1168-
}
1181+
fn flush_delayed(&mut self, bugs: impl IntoIterator<Item = Diagnostic>, explanation: &str) {
1182+
let mut no_bugs = true;
1183+
for mut bug in bugs {
1184+
if no_bugs {
1185+
// Put the overall explanation before the `DelayedBug`s, to
1186+
// frame them better (e.g. separate warnings from them).
1187+
self.emit_diagnostic(&Diagnostic::new(Bug, explanation));
1188+
no_bugs = false;
1189+
}
1190+
1191+
// "Undelay" the `DelayedBug`s (into plain `Bug`s).
1192+
if bug.level != Level::DelayedBug {
1193+
// NOTE(eddyb) not panicking here because we're already producing
1194+
// an ICE, and the more information the merrier.
1195+
bug.note(&format!(
1196+
"`flushed_delayed` got diagnostic with level {:?}, \
1197+
instead of the expected `DelayedBug`",
1198+
bug.level,
1199+
));
1200+
}
1201+
bug.level = Level::Bug;
11691202

1170-
fn flush_delayed(&mut self, bugs: Vec<Diagnostic>, explanation: &str) {
1171-
let has_bugs = !bugs.is_empty();
1172-
for bug in bugs {
11731203
self.emit_diagnostic(&bug);
11741204
}
1175-
if has_bugs {
1176-
panic!("{}", explanation);
1205+
1206+
// Panic with `ExplicitBug` to avoid "unexpected panic" messages.
1207+
if !no_bugs {
1208+
panic::panic_any(ExplicitBug);
11771209
}
11781210
}
11791211

@@ -1227,6 +1259,7 @@ impl DelayedDiagnostic {
12271259
#[derive(Copy, PartialEq, Clone, Hash, Debug, Encodable, Decodable)]
12281260
pub enum Level {
12291261
Bug,
1262+
DelayedBug,
12301263
Fatal,
12311264
Error {
12321265
/// If this error comes from a lint, don't abort compilation even when abort_if_errors() is called.
@@ -1250,7 +1283,7 @@ impl Level {
12501283
fn color(self) -> ColorSpec {
12511284
let mut spec = ColorSpec::new();
12521285
match self {
1253-
Bug | Fatal | Error { .. } => {
1286+
Bug | DelayedBug | Fatal | Error { .. } => {
12541287
spec.set_fg(Some(Color::Red)).set_intense(true);
12551288
}
12561289
Warning => {
@@ -1270,7 +1303,7 @@ impl Level {
12701303

12711304
pub fn to_str(self) -> &'static str {
12721305
match self {
1273-
Bug => "error: internal compiler error",
1306+
Bug | DelayedBug => "error: internal compiler error",
12741307
Fatal | Error { .. } => "error",
12751308
Warning => "warning",
12761309
Note => "note",

compiler/rustc_resolve/src/late/diagnostics.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,7 +1066,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
10661066
})
10671067
.unwrap_or(false)
10681068
{
1069-
err.delay_as_bug();
1069+
err.downgrade_to_delayed_bug();
10701070
// We already suggested changing `:` into `::` during parsing.
10711071
return false;
10721072
}
@@ -1472,7 +1472,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
14721472
.borrow_mut()
14731473
.insert(colon_sp)
14741474
{
1475-
err.delay_as_bug();
1475+
err.downgrade_to_delayed_bug();
14761476
}
14771477
}
14781478
if let Ok(base_snippet) = base_snippet {

compiler/rustc_typeck/src/check/demand.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
251251
if !lhs.is_syntactic_place_expr() {
252252
// We already emitted E0070 "invalid left-hand side of assignment", so we
253253
// silence this.
254-
err.delay_as_bug();
254+
err.downgrade_to_delayed_bug();
255255
}
256256
}
257257
_ => {}

compiler/rustc_typeck/src/check/fn_ctxt/checks.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
837837
kind: hir::ExprKind::Loop(_, _, hir::LoopSource::While, _),
838838
..
839839
})) => {
840-
err.delay_as_bug();
840+
err.downgrade_to_delayed_bug();
841841
}
842842
_ => {}
843843
}

src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2098,8 +2098,15 @@ fn resolution_failure(
20982098
)
20992099
}
21002100
ResolutionFailure::NoParentItem => {
2101-
diag.level = rustc_errors::Level::Bug;
2102-
"all intra-doc links should have a parent item".to_owned()
2101+
// FIXME(eddyb) this doesn't belong here, whatever made
2102+
// the `ResolutionFailure::NoParentItem` should emit an
2103+
// immediate or delayed `span_bug` about the issue.
2104+
tcx.sess.delay_span_bug(
2105+
sp.unwrap_or(DUMMY_SP),
2106+
"intra-doc link missing parent item",
2107+
);
2108+
2109+
"BUG: all intra-doc links should have a parent item".to_owned()
21032110
}
21042111
ResolutionFailure::MalformedGenerics(variant) => match variant {
21052112
MalformedGenerics::UnbalancedAngleBrackets => {

src/tools/rustfmt/src/parse/session.rs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ impl Emitter for SilentOnIgnoredFilesEmitter {
6060
None
6161
}
6262
fn emit_diagnostic(&mut self, db: &Diagnostic) {
63-
if db.level == DiagnosticLevel::Fatal {
63+
if db.level() == DiagnosticLevel::Fatal {
6464
return self.handle_non_ignoreable_error(db);
6565
}
6666
if let Some(primary_span) = &db.span.primary_span() {
@@ -292,7 +292,7 @@ mod tests {
292292
use super::*;
293293
use crate::config::IgnoreList;
294294
use crate::utils::mk_sp;
295-
use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName, DUMMY_SP};
295+
use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName};
296296
use std::path::PathBuf;
297297
use std::sync::atomic::AtomicU32;
298298

@@ -310,16 +310,12 @@ mod tests {
310310
}
311311

312312
fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
313-
Diagnostic {
314-
level,
315-
code: None,
316-
message: vec![],
317-
children: vec![],
318-
suggestions: Ok(vec![]),
319-
span: span.unwrap_or_else(MultiSpan::new),
320-
sort_span: DUMMY_SP,
321-
is_lint: false,
313+
let mut diag = Diagnostic::new(level, "");
314+
diag.message.clear();
315+
if let Some(span) = span {
316+
diag.span = span;
322317
}
318+
diag
323319
}
324320

325321
fn build_emitter(

0 commit comments

Comments
 (0)