Rollup merge of #119872 - compiler-errors:eagerly-emit-delayed-bugs, r=oli-obk,nnethercote
Give me a way to emit all the delayed bugs as errors (add `-Zeagerly-emit-delayed-bugs`) This is probably a *better* way to inspect all the delayed bugs in a program that what exists currently (and therefore makes it very easy to choose the right number `N` with `-Zemit-err-as-bug=N`, though I guess the naming is a bit ironic when you pair both of the flags together, but that feels like naming bikeshed more than anything). This pacifies my only concern with https://github.com/rust-lang/rust/pull/119871#issuecomment-1888170259, because (afaict?) that PR doesn't allow you to intercept a delayed bug's stack trace anymore, which as someone who debugs the compiler a lot, is something that I can *promise* that I do. r? `@nnethercote` or `@oli-obk`
This commit is contained in:
commit
504794b908
@ -85,7 +85,7 @@ fn source_string(file: Lrc<SourceFile>, line: &Line) -> String {
|
|||||||
/// Maps `Diagnostic::Level` to `snippet::AnnotationType`
|
/// Maps `Diagnostic::Level` to `snippet::AnnotationType`
|
||||||
fn annotation_type_for_level(level: Level) -> AnnotationType {
|
fn annotation_type_for_level(level: Level) -> AnnotationType {
|
||||||
match level {
|
match level {
|
||||||
Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error => AnnotationType::Error,
|
Level::Bug | Level::DelayedBug(_) | Level::Fatal | Level::Error => AnnotationType::Error,
|
||||||
Level::ForceWarning(_) | Level::Warning => AnnotationType::Warning,
|
Level::ForceWarning(_) | Level::Warning => AnnotationType::Warning,
|
||||||
Level::Note | Level::OnceNote => AnnotationType::Note,
|
Level::Note | Level::OnceNote => AnnotationType::Note,
|
||||||
Level::Help | Level::OnceHelp => AnnotationType::Help,
|
Level::Help | Level::OnceHelp => AnnotationType::Help,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::snippet::Style;
|
use crate::snippet::Style;
|
||||||
use crate::{
|
use crate::{
|
||||||
CodeSuggestion, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Level, MultiSpan,
|
CodeSuggestion, DelayedBugKind, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Level,
|
||||||
SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle,
|
MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle,
|
||||||
};
|
};
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||||
use rustc_error_messages::fluent_value_from_str_list_sep_by_and;
|
use rustc_error_messages::fluent_value_from_str_list_sep_by_and;
|
||||||
@ -243,12 +243,15 @@ impl Diagnostic {
|
|||||||
|
|
||||||
pub fn is_error(&self) -> bool {
|
pub fn is_error(&self) -> bool {
|
||||||
match self.level {
|
match self.level {
|
||||||
Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error | Level::FailureNote => {
|
Level::Bug
|
||||||
true
|
| Level::DelayedBug(DelayedBugKind::Normal)
|
||||||
}
|
| Level::Fatal
|
||||||
|
| Level::Error
|
||||||
|
| Level::FailureNote => true,
|
||||||
|
|
||||||
Level::ForceWarning(_)
|
Level::ForceWarning(_)
|
||||||
| Level::Warning
|
| Level::Warning
|
||||||
|
| Level::DelayedBug(DelayedBugKind::GoodPath)
|
||||||
| Level::Note
|
| Level::Note
|
||||||
| Level::OnceNote
|
| Level::OnceNote
|
||||||
| Level::Help
|
| Level::Help
|
||||||
@ -318,7 +321,7 @@ impl Diagnostic {
|
|||||||
"downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
|
"downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
|
||||||
self.level
|
self.level
|
||||||
);
|
);
|
||||||
self.level = Level::DelayedBug;
|
self.level = Level::DelayedBug(DelayedBugKind::Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Appends a labeled span to the diagnostic.
|
/// Appends a labeled span to the diagnostic.
|
||||||
|
@ -519,6 +519,12 @@ fn default_track_diagnostic(diag: Diagnostic, f: &mut dyn FnMut(Diagnostic)) {
|
|||||||
pub static TRACK_DIAGNOSTIC: AtomicRef<fn(Diagnostic, &mut dyn FnMut(Diagnostic))> =
|
pub static TRACK_DIAGNOSTIC: AtomicRef<fn(Diagnostic, &mut dyn FnMut(Diagnostic))> =
|
||||||
AtomicRef::new(&(default_track_diagnostic as _));
|
AtomicRef::new(&(default_track_diagnostic as _));
|
||||||
|
|
||||||
|
#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)]
|
||||||
|
pub enum DelayedBugKind {
|
||||||
|
Normal,
|
||||||
|
GoodPath,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
pub struct DiagCtxtFlags {
|
pub struct DiagCtxtFlags {
|
||||||
/// If false, warning-level lints are suppressed.
|
/// If false, warning-level lints are suppressed.
|
||||||
@ -527,6 +533,9 @@ pub struct DiagCtxtFlags {
|
|||||||
/// If Some, the Nth error-level diagnostic is upgraded to bug-level.
|
/// If Some, the Nth error-level diagnostic is upgraded to bug-level.
|
||||||
/// (rustc: see `-Z treat-err-as-bug`)
|
/// (rustc: see `-Z treat-err-as-bug`)
|
||||||
pub treat_err_as_bug: Option<NonZeroUsize>,
|
pub treat_err_as_bug: Option<NonZeroUsize>,
|
||||||
|
/// Eagerly emit delayed bugs as errors, so that the compiler debugger may
|
||||||
|
/// see all of the errors being emitted at once.
|
||||||
|
pub eagerly_emit_delayed_bugs: bool,
|
||||||
/// Show macro backtraces.
|
/// Show macro backtraces.
|
||||||
/// (rustc: see `-Z macro-backtrace`)
|
/// (rustc: see `-Z macro-backtrace`)
|
||||||
pub macro_backtrace: bool,
|
pub macro_backtrace: bool,
|
||||||
@ -541,8 +550,7 @@ impl Drop for DiagCtxtInner {
|
|||||||
self.emit_stashed_diagnostics();
|
self.emit_stashed_diagnostics();
|
||||||
|
|
||||||
if !self.has_errors() {
|
if !self.has_errors() {
|
||||||
let bugs = std::mem::replace(&mut self.span_delayed_bugs, Vec::new());
|
self.flush_delayed(DelayedBugKind::Normal)
|
||||||
self.flush_delayed(bugs, "no errors encountered even though `span_delayed_bug` issued");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(eddyb) this explains what `good_path_delayed_bugs` are!
|
// FIXME(eddyb) this explains what `good_path_delayed_bugs` are!
|
||||||
@ -551,11 +559,7 @@ impl Drop for DiagCtxtInner {
|
|||||||
// lints can be `#[allow]`'d, potentially leading to this triggering.
|
// lints can be `#[allow]`'d, potentially leading to this triggering.
|
||||||
// Also, "good path" should be replaced with a better naming.
|
// Also, "good path" should be replaced with a better naming.
|
||||||
if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() {
|
if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() {
|
||||||
let bugs = std::mem::replace(&mut self.good_path_delayed_bugs, Vec::new());
|
self.flush_delayed(DelayedBugKind::GoodPath);
|
||||||
self.flush_delayed(
|
|
||||||
bugs,
|
|
||||||
"no warnings or errors encountered even though `good_path_delayed_bugs` issued",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.check_unstable_expect_diagnostics {
|
if self.check_unstable_expect_diagnostics {
|
||||||
@ -865,7 +869,8 @@ impl DiagCtxt {
|
|||||||
if treat_next_err_as_bug {
|
if treat_next_err_as_bug {
|
||||||
self.bug(msg);
|
self.bug(msg);
|
||||||
}
|
}
|
||||||
DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).emit()
|
DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug(DelayedBugKind::Normal), msg)
|
||||||
|
.emit()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like `delayed_bug`, but takes an additional span.
|
/// Like `delayed_bug`, but takes an additional span.
|
||||||
@ -882,16 +887,15 @@ impl DiagCtxt {
|
|||||||
if treat_next_err_as_bug {
|
if treat_next_err_as_bug {
|
||||||
self.span_bug(sp, msg);
|
self.span_bug(sp, msg);
|
||||||
}
|
}
|
||||||
DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).with_span(sp).emit()
|
DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug(DelayedBugKind::Normal), msg)
|
||||||
|
.with_span(sp)
|
||||||
|
.emit()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(eddyb) note the comment inside `impl Drop for DiagCtxtInner`, that's
|
// FIXME(eddyb) note the comment inside `impl Drop for DiagCtxtInner`, that's
|
||||||
// where the explanation of what "good path" is (also, it should be renamed).
|
// where the explanation of what "good path" is (also, it should be renamed).
|
||||||
pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) {
|
pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) {
|
||||||
let mut inner = self.inner.borrow_mut();
|
DiagnosticBuilder::<()>::new(self, DelayedBug(DelayedBugKind::GoodPath), msg).emit()
|
||||||
let diagnostic = Diagnostic::new(DelayedBug, msg);
|
|
||||||
let backtrace = std::backtrace::Backtrace::capture();
|
|
||||||
inner.good_path_delayed_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
@ -1218,9 +1222,7 @@ impl DiagCtxt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush_delayed(&self) {
|
pub fn flush_delayed(&self) {
|
||||||
let mut inner = self.inner.borrow_mut();
|
self.inner.borrow_mut().flush_delayed(DelayedBugKind::Normal);
|
||||||
let bugs = std::mem::replace(&mut inner.span_delayed_bugs, Vec::new());
|
|
||||||
inner.flush_delayed(bugs, "no errors encountered even though `span_delayed_bug` issued");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1270,17 +1272,30 @@ impl DiagCtxtInner {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if diagnostic.level == DelayedBug {
|
// FIXME(eddyb) this should check for `has_errors` and stop pushing
|
||||||
// FIXME(eddyb) this should check for `has_errors` and stop pushing
|
// once *any* errors were emitted (and truncate `span_delayed_bugs`
|
||||||
// once *any* errors were emitted (and truncate `span_delayed_bugs`
|
// when an error is first emitted, also), but maybe there's a case
|
||||||
// when an error is first emitted, also), but maybe there's a case
|
// in which that's not sound? otherwise this is really inefficient.
|
||||||
// in which that's not sound? otherwise this is really inefficient.
|
match diagnostic.level {
|
||||||
let backtrace = std::backtrace::Backtrace::capture();
|
DelayedBug(_) if self.flags.eagerly_emit_delayed_bugs => {
|
||||||
self.span_delayed_bugs
|
diagnostic.level = Error;
|
||||||
.push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace));
|
}
|
||||||
|
DelayedBug(DelayedBugKind::Normal) => {
|
||||||
|
let backtrace = std::backtrace::Backtrace::capture();
|
||||||
|
self.span_delayed_bugs
|
||||||
|
.push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace));
|
||||||
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted());
|
return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted());
|
||||||
|
}
|
||||||
|
DelayedBug(DelayedBugKind::GoodPath) => {
|
||||||
|
let backtrace = std::backtrace::Backtrace::capture();
|
||||||
|
self.good_path_delayed_bugs
|
||||||
|
.push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace));
|
||||||
|
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if diagnostic.has_future_breakage() {
|
if diagnostic.has_future_breakage() {
|
||||||
@ -1396,11 +1411,18 @@ impl DiagCtxtInner {
|
|||||||
self.emit_diagnostic(Diagnostic::new(FailureNote, msg));
|
self.emit_diagnostic(Diagnostic::new(FailureNote, msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush_delayed(
|
fn flush_delayed(&mut self, kind: DelayedBugKind) {
|
||||||
&mut self,
|
let (bugs, explanation) = match kind {
|
||||||
bugs: Vec<DelayedDiagnostic>,
|
DelayedBugKind::Normal => (
|
||||||
explanation: impl Into<DiagnosticMessage> + Copy,
|
std::mem::take(&mut self.span_delayed_bugs),
|
||||||
) {
|
"no errors encountered even though `span_delayed_bug` issued",
|
||||||
|
),
|
||||||
|
DelayedBugKind::GoodPath => (
|
||||||
|
std::mem::take(&mut self.good_path_delayed_bugs),
|
||||||
|
"no warnings or errors encountered even though `good_path_delayed_bugs` issued",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
if bugs.is_empty() {
|
if bugs.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1433,7 +1455,7 @@ impl DiagCtxtInner {
|
|||||||
if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner };
|
if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner };
|
||||||
|
|
||||||
// "Undelay" the `DelayedBug`s (into plain `Bug`s).
|
// "Undelay" the `DelayedBug`s (into plain `Bug`s).
|
||||||
if bug.level != DelayedBug {
|
if !matches!(bug.level, DelayedBug(_)) {
|
||||||
// NOTE(eddyb) not panicking here because we're already producing
|
// NOTE(eddyb) not panicking here because we're already producing
|
||||||
// an ICE, and the more information the merrier.
|
// an ICE, and the more information the merrier.
|
||||||
bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel {
|
bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel {
|
||||||
@ -1521,8 +1543,9 @@ pub enum Level {
|
|||||||
/// silently dropped. I.e. "expect other errors are emitted" semantics. Useful on code paths
|
/// silently dropped. I.e. "expect other errors are emitted" semantics. Useful on code paths
|
||||||
/// that should only be reached when compiling erroneous code.
|
/// that should only be reached when compiling erroneous code.
|
||||||
///
|
///
|
||||||
/// Its `EmissionGuarantee` is `ErrorGuaranteed`.
|
/// Its `EmissionGuarantee` is `ErrorGuaranteed` for `Normal` delayed bugs, and `()` for
|
||||||
DelayedBug,
|
/// `GoodPath` delayed bugs.
|
||||||
|
DelayedBug(DelayedBugKind),
|
||||||
|
|
||||||
/// An error that causes an immediate abort. Used for things like configuration errors,
|
/// An error that causes an immediate abort. Used for things like configuration errors,
|
||||||
/// internal overflows, some file operation errors.
|
/// internal overflows, some file operation errors.
|
||||||
@ -1597,7 +1620,7 @@ impl Level {
|
|||||||
fn color(self) -> ColorSpec {
|
fn color(self) -> ColorSpec {
|
||||||
let mut spec = ColorSpec::new();
|
let mut spec = ColorSpec::new();
|
||||||
match self {
|
match self {
|
||||||
Bug | DelayedBug | Fatal | Error => {
|
Bug | DelayedBug(_) | Fatal | Error => {
|
||||||
spec.set_fg(Some(Color::Red)).set_intense(true);
|
spec.set_fg(Some(Color::Red)).set_intense(true);
|
||||||
}
|
}
|
||||||
ForceWarning(_) | Warning => {
|
ForceWarning(_) | Warning => {
|
||||||
@ -1617,7 +1640,7 @@ impl Level {
|
|||||||
|
|
||||||
pub fn to_str(self) -> &'static str {
|
pub fn to_str(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Bug | DelayedBug => "error: internal compiler error",
|
Bug | DelayedBug(_) => "error: internal compiler error",
|
||||||
Fatal | Error => "error",
|
Fatal | Error => "error",
|
||||||
ForceWarning(_) | Warning => "warning",
|
ForceWarning(_) | Warning => "warning",
|
||||||
Note | OnceNote => "note",
|
Note | OnceNote => "note",
|
||||||
|
@ -1146,6 +1146,7 @@ impl UnstableOptions {
|
|||||||
DiagCtxtFlags {
|
DiagCtxtFlags {
|
||||||
can_emit_warnings,
|
can_emit_warnings,
|
||||||
treat_err_as_bug: self.treat_err_as_bug,
|
treat_err_as_bug: self.treat_err_as_bug,
|
||||||
|
eagerly_emit_delayed_bugs: self.eagerly_emit_delayed_bugs,
|
||||||
macro_backtrace: self.macro_backtrace,
|
macro_backtrace: self.macro_backtrace,
|
||||||
deduplicate_diagnostics: self.deduplicate_diagnostics,
|
deduplicate_diagnostics: self.deduplicate_diagnostics,
|
||||||
track_diagnostics: self.track_diagnostics,
|
track_diagnostics: self.track_diagnostics,
|
||||||
|
@ -1583,6 +1583,9 @@ options! {
|
|||||||
"version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
|
"version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
|
||||||
dylib_lto: bool = (false, parse_bool, [UNTRACKED],
|
dylib_lto: bool = (false, parse_bool, [UNTRACKED],
|
||||||
"enables LTO for dylib crate type"),
|
"enables LTO for dylib crate type"),
|
||||||
|
eagerly_emit_delayed_bugs: bool = (false, parse_bool, [UNTRACKED],
|
||||||
|
"emit delayed bugs eagerly as errors instead of stashing them and emitting \
|
||||||
|
them only if an error has not been emitted"),
|
||||||
ehcont_guard: bool = (false, parse_bool, [TRACKED],
|
ehcont_guard: bool = (false, parse_bool, [TRACKED],
|
||||||
"generate Windows EHCont Guard tables"),
|
"generate Windows EHCont Guard tables"),
|
||||||
emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
|
emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
|
||||||
|
11
tests/ui/treat-err-as-bug/eagerly-emit.rs
Normal file
11
tests/ui/treat-err-as-bug/eagerly-emit.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// compile-flags: -Zeagerly-emit-delayed-bugs
|
||||||
|
|
||||||
|
trait Foo {}
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
fn f() -> impl Foo {
|
||||||
|
//~^ ERROR the trait bound `i32: Foo` is not satisfied
|
||||||
|
//~| ERROR `report_selection_error` did not emit an error
|
||||||
|
1i32
|
||||||
|
}
|
28
tests/ui/treat-err-as-bug/eagerly-emit.stderr
Normal file
28
tests/ui/treat-err-as-bug/eagerly-emit.stderr
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
error: `report_selection_error` did not emit an error
|
||||||
|
--> $DIR/eagerly-emit.rs:7:11
|
||||||
|
|
|
||||||
|
LL | fn f() -> impl Foo {
|
||||||
|
| ^^^^^^^^
|
||||||
|
|
||||||
|
error: trimmed_def_paths constructed but no error emitted; use `DelayDm` for lints or `with_no_trimmed_paths` for debugging
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `i32: Foo` is not satisfied
|
||||||
|
--> $DIR/eagerly-emit.rs:7:11
|
||||||
|
|
|
||||||
|
LL | fn f() -> impl Foo {
|
||||||
|
| ^^^^^^^^ the trait `Foo` is not implemented for `i32`
|
||||||
|
...
|
||||||
|
LL | 1i32
|
||||||
|
| ---- return type was inferred to be `i32` here
|
||||||
|
|
|
||||||
|
help: this trait has no implementations, consider adding one
|
||||||
|
--> $DIR/eagerly-emit.rs:3:1
|
||||||
|
|
|
||||||
|
LL | trait Foo {}
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
||||||
|
error: expected fulfillment errors
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
Loading…
x
Reference in New Issue
Block a user