diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
commit | 9918693037dce8aa4bb6f08741b6812923486c18 (patch) | |
tree | 21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /compiler/rustc_errors/src/lib.rs | |
parent | Releasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff) | |
download | rustc-9918693037dce8aa4bb6f08741b6812923486c18.tar.xz rustc-9918693037dce8aa4bb6f08741b6812923486c18.zip |
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_errors/src/lib.rs')
-rw-r--r-- | compiler/rustc_errors/src/lib.rs | 620 |
1 files changed, 281 insertions, 339 deletions
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index dd462cc64..959e26fec 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -3,14 +3,13 @@ //! This module contains the code for creating and emitting diagnostics. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] #![feature(array_windows)] #![feature(extract_if)] #![feature(if_let_guard)] #![feature(let_chains)] #![feature(never_type)] -#![feature(result_option_inspect)] #![feature(rustc_attrs)] #![feature(yeet_expr)] #![feature(try_blocks)] @@ -42,7 +41,6 @@ pub use rustc_error_messages::{ fallback_fluent_bundle, fluent_bundle, DelayDm, DiagnosticMessage, FluentBundle, LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagnosticMessage, }; -use rustc_fluent_macro::fluent_messages; pub use rustc_lint_defs::{pluralize, Applicability}; use rustc_span::source_map::SourceMap; pub use rustc_span::ErrorGuaranteed; @@ -83,7 +81,7 @@ pub use snippet::Style; pub type PErr<'a> = DiagnosticBuilder<'a, ErrorGuaranteed>; pub type PResult<'a, T> = Result<T, PErr<'a>>; -fluent_messages! { "../messages.ftl" } +rustc_fluent_macro::fluent_messages! { "../messages.ftl" } // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger. // (See also the comment on `DiagnosticBuilderInner`'s `diagnostic` field.) @@ -414,15 +412,15 @@ use std::backtrace::{Backtrace, BacktraceStatus}; /// A handler deals with errors and other compiler output. /// Certain errors (fatal, bug, unimpl) may cause immediate exit, /// others log errors for later reporting. -pub struct Handler { - inner: Lock<HandlerInner>, +pub struct DiagCtxt { + inner: Lock<DiagCtxtInner>, } /// This inner struct exists to keep it all behind a single lock; /// this is done to prevent possible deadlocks in a multi-threaded compiler, /// as well as inconsistent state observation. -struct HandlerInner { - flags: HandlerFlags, +struct DiagCtxtInner { + flags: DiagCtxtFlags, /// The number of lint errors that have been emitted. lint_err_count: usize, /// The number of errors that have been emitted, including duplicates. @@ -433,10 +431,10 @@ struct HandlerInner { warn_count: usize, deduplicated_err_count: usize, emitter: Box<DynEmitter>, - delayed_span_bugs: Vec<DelayedDiagnostic>, - delayed_good_path_bugs: Vec<DelayedDiagnostic>, + span_delayed_bugs: Vec<DelayedDiagnostic>, + good_path_delayed_bugs: Vec<DelayedDiagnostic>, /// This flag indicates that an expected diagnostic was emitted and suppressed. - /// This is used for the `delayed_good_path_bugs` check. + /// This is used for the `good_path_delayed_bugs` check. suppressed_expected_diag: bool, /// This set contains the `DiagnosticId` of all emitted diagnostics to avoid @@ -520,7 +518,7 @@ pub static TRACK_DIAGNOSTICS: AtomicRef<fn(&mut Diagnostic, &mut dyn FnMut(&mut AtomicRef::new(&(default_track_diagnostic as _)); #[derive(Copy, Clone, Default)] -pub struct HandlerFlags { +pub struct DiagCtxtFlags { /// If false, warning-level lints are suppressed. /// (rustc: see `--allow warnings` and `--cap-lints`) pub can_emit_warnings: bool, @@ -530,7 +528,7 @@ pub struct HandlerFlags { /// If true, immediately emit diagnostics that would otherwise be buffered. /// (rustc: see `-Z dont-buffer-diagnostics` and `-Z treat-err-as-bug`) pub dont_buffer_diagnostics: bool, - /// If true, immediately print bugs registered with `delay_span_bug`. + /// If true, immediately print bugs registered with `span_delayed_bug`. /// (rustc: see `-Z report-delayed-bugs`) pub report_delayed_bugs: bool, /// Show macro backtraces. @@ -542,25 +540,26 @@ pub struct HandlerFlags { pub track_diagnostics: bool, } -impl Drop for HandlerInner { +impl Drop for DiagCtxtInner { fn drop(&mut self) { self.emit_stashed_diagnostics(); if !self.has_errors() { - let bugs = std::mem::replace(&mut self.delayed_span_bugs, Vec::new()); - self.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued"); + let bugs = std::mem::replace(&mut self.span_delayed_bugs, Vec::new()); + self.flush_delayed(bugs, "no errors encountered even though `span_delayed_bug` issued"); } - // FIXME(eddyb) this explains what `delayed_good_path_bugs` are! - // They're `delayed_span_bugs` but for "require some diagnostic happened" + // FIXME(eddyb) this explains what `good_path_delayed_bugs` are! + // They're `span_delayed_bugs` but for "require some diagnostic happened" // instead of "require some error happened". Sadly that isn't ideal, as // lints can be `#[allow]`'d, potentially leading to this triggering. // Also, "good path" should be replaced with a better naming. - if !self.has_any_message() && !self.suppressed_expected_diag && !std::thread::panicking() { - let bugs = std::mem::replace(&mut self.delayed_good_path_bugs, Vec::new()); + let has_any_message = self.err_count > 0 || self.lint_err_count > 0 || self.warn_count > 0; + if !has_any_message && !self.suppressed_expected_diag && !std::thread::panicking() { + let bugs = std::mem::replace(&mut self.good_path_delayed_bugs, Vec::new()); self.flush_delayed( bugs, - "no warnings or errors encountered even though `delayed_good_path_bugs` issued", + "no warnings or errors encountered even though `good_path_delayed_bugs` issued", ); } @@ -573,7 +572,7 @@ impl Drop for HandlerInner { } } -impl Handler { +impl DiagCtxt { pub fn with_tty_emitter( sm: Option<Lrc<SourceMap>>, fallback_bundle: LazyFallbackBundle, @@ -586,12 +585,7 @@ impl Handler { self } - pub fn treat_err_as_bug(mut self, treat_err_as_bug: NonZeroUsize) -> Self { - self.inner.get_mut().flags.treat_err_as_bug = Some(treat_err_as_bug); - self - } - - pub fn with_flags(mut self, flags: HandlerFlags) -> Self { + pub fn with_flags(mut self, flags: DiagCtxtFlags) -> Self { self.inner.get_mut().flags = flags; self } @@ -603,16 +597,16 @@ impl Handler { pub fn with_emitter(emitter: Box<DynEmitter>) -> Self { Self { - inner: Lock::new(HandlerInner { - flags: HandlerFlags { can_emit_warnings: true, ..Default::default() }, + inner: Lock::new(DiagCtxtInner { + flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() }, lint_err_count: 0, err_count: 0, warn_count: 0, deduplicated_err_count: 0, deduplicated_warn_count: 0, emitter, - delayed_span_bugs: Vec::new(), - delayed_good_path_bugs: Vec::new(), + span_delayed_bugs: Vec::new(), + good_path_delayed_bugs: Vec::new(), suppressed_expected_diag: false, taught_diagnostics: Default::default(), emitted_diagnostic_codes: Default::default(), @@ -650,7 +644,7 @@ impl Handler { // This is here to not allow mutation of flags; // as of this writing it's only used in tests in librustc_middle. pub fn can_emit_warnings(&self) -> bool { - self.inner.lock().flags.can_emit_warnings + self.inner.borrow_mut().flags.can_emit_warnings } /// Resets the diagnostic error count as well as the cached emitted diagnostics. @@ -666,8 +660,8 @@ impl Handler { inner.deduplicated_warn_count = 0; // actually free the underlying memory (which `clear` would not do) - inner.delayed_span_bugs = Default::default(); - inner.delayed_good_path_bugs = Default::default(); + inner.span_delayed_bugs = Default::default(); + inner.good_path_delayed_bugs = Default::default(); inner.taught_diagnostics = Default::default(); inner.emitted_diagnostic_codes = Default::default(); inner.emitted_diagnostics = Default::default(); @@ -678,15 +672,45 @@ impl Handler { /// Retrieve a stashed diagnostic with `steal_diagnostic`. pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: Diagnostic) { let mut inner = self.inner.borrow_mut(); - inner.stash((span.with_parent(None), key), diag); + + let key = (span.with_parent(None), key); + + if diag.is_error() { + if matches!(diag.level, Level::Error { lint: true }) { + inner.lint_err_count += 1; + } else { + inner.err_count += 1; + } + } else { + // Warnings are only automatically flushed if they're forced. + if diag.is_force_warn() { + inner.warn_count += 1; + } + } + + // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic + // if/when we have a more robust macro-friendly replacement for `(span, key)` as a key. + // See the PR for a discussion. + inner.stashed_diagnostics.insert(key, diag); } /// Steal a previously stashed diagnostic with the given `Span` and [`StashKey`] as the key. pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_, ()>> { let mut inner = self.inner.borrow_mut(); - inner - .steal((span.with_parent(None), key)) - .map(|diag| DiagnosticBuilder::new_diagnostic(self, diag)) + let key = (span.with_parent(None), key); + let diag = inner.stashed_diagnostics.remove(&key)?; + if diag.is_error() { + if matches!(diag.level, Level::Error { lint: true }) { + inner.lint_err_count -= 1; + } else { + inner.err_count -= 1; + } + } else { + if diag.is_force_warn() { + inner.warn_count -= 1; + } + } + Some(DiagnosticBuilder::new_diagnostic(self, diag)) } pub fn has_stashed_diagnostic(&self, span: Span, key: StashKey) -> bool { @@ -698,7 +722,12 @@ impl Handler { self.inner.borrow_mut().emit_stashed_diagnostics() } - /// Construct a builder with the `msg` at the level appropriate for the specific `EmissionGuarantee`. + /// Construct a builder with the `msg` at the level appropriate for the + /// specific `EmissionGuarantee`. + /// + /// Note: this is necessary for `derive(Diagnostic)`, but shouldn't be used + /// outside of that. Instead use `struct_err`, `struct_warn`, etc., which + /// make the diagnostic kind clearer. #[rustc_lint_diagnostics] #[track_caller] pub fn struct_diagnostic<G: EmissionGuarantee>( @@ -850,7 +879,7 @@ impl Handler { &self, msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { - DiagnosticBuilder::new_guaranteeing_error(self, msg) + DiagnosticBuilder::new(self, Level::Error { lint: false }, msg) } /// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors. @@ -913,11 +942,21 @@ impl Handler { result } - /// Construct a builder at the `Error` level with the `msg`. + /// Construct a builder at the `Fatal` level with the `msg`. #[rustc_lint_diagnostics] #[track_caller] pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> { - DiagnosticBuilder::new_fatal(self, msg) + DiagnosticBuilder::new(self, Level::Fatal, msg) + } + + /// Construct a builder at the `Fatal` level with the `msg`, that doesn't abort. + #[rustc_lint_diagnostics] + #[track_caller] + pub fn struct_almost_fatal( + &self, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, FatalError> { + DiagnosticBuilder::new(self, Level::Fatal, msg) } /// Construct a builder at the `Help` level with the `msg`. @@ -929,18 +968,14 @@ impl Handler { /// Construct a builder at the `Note` level with the `msg`. #[rustc_lint_diagnostics] #[track_caller] - pub fn struct_note_without_error( - &self, - msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_, ()> { + pub fn struct_note(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Note, msg) } #[rustc_lint_diagnostics] #[track_caller] pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { - self.emit_diag_at_span(Diagnostic::new(Fatal, msg), span); - FatalError.raise() + self.struct_span_fatal(span, msg).emit() } #[rustc_lint_diagnostics] @@ -951,8 +986,7 @@ impl Handler { msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> ! { - self.emit_diag_at_span(Diagnostic::new_with_code(Fatal, Some(code), msg), span); - FatalError.raise() + self.struct_span_fatal_with_code(span, msg, code).emit() } #[rustc_lint_diagnostics] @@ -962,7 +996,7 @@ impl Handler { span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>, ) -> ErrorGuaranteed { - self.emit_diag_at_span(Diagnostic::new(Error { lint: false }, msg), span).unwrap() + self.struct_span_err(span, msg).emit() } #[rustc_lint_diagnostics] @@ -972,17 +1006,14 @@ impl Handler { span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>, code: DiagnosticId, - ) { - self.emit_diag_at_span( - Diagnostic::new_with_code(Error { lint: false }, Some(code), msg), - span, - ); + ) -> ErrorGuaranteed { + self.struct_span_err_with_code(span, msg, code).emit() } #[rustc_lint_diagnostics] #[track_caller] pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) { - self.emit_diag_at_span(Diagnostic::new(Warning(None), msg), span); + self.struct_span_warn(span, msg).emit() } #[rustc_lint_diagnostics] @@ -993,49 +1024,70 @@ impl Handler { msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) { - self.emit_diag_at_span(Diagnostic::new_with_code(Warning(None), Some(code), msg), span); + self.struct_span_warn_with_code(span, msg, code).emit() } - pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<String>) -> ! { + pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { self.inner.borrow_mut().span_bug(span, msg) } - /// For documentation on this, see `Session::delay_span_bug`. + /// For documentation on this, see `Session::span_delayed_bug`. + /// + /// Note: this function used to be called `delay_span_bug`. It was renamed + /// to match similar functions like `span_bug`, `span_err`, etc. #[track_caller] - pub fn delay_span_bug( + pub fn span_delayed_bug( &self, - span: impl Into<MultiSpan>, - msg: impl Into<String>, + sp: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, ) -> ErrorGuaranteed { - self.inner.borrow_mut().delay_span_bug(span, msg) + let mut inner = self.inner.borrow_mut(); + + // This is technically `self.treat_err_as_bug()` but `span_delayed_bug` is called before + // incrementing `err_count` by one, so we need to +1 the comparing. + // FIXME: Would be nice to increment err_count in a more coherent way. + if inner.flags.treat_err_as_bug.is_some_and(|c| { + inner.err_count + inner.lint_err_count + inner.delayed_bug_count() + 1 >= c.get() + }) { + // FIXME: don't abort here if report_delayed_bugs is off + inner.span_bug(sp, msg); + } + let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg); + diagnostic.set_span(sp); + inner.emit_diagnostic(diagnostic).unwrap() } - // FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, 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). - pub fn delay_good_path_bug(&self, msg: impl Into<DiagnosticMessage>) { - self.inner.borrow_mut().delay_good_path_bug(msg) + pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) { + let mut inner = self.inner.borrow_mut(); + + let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg); + if inner.flags.report_delayed_bugs { + inner.emit_diagnostic_without_consuming(&mut diagnostic); + } + let backtrace = std::backtrace::Backtrace::capture(); + inner.good_path_delayed_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace)); } #[track_caller] pub fn span_bug_no_panic(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) { - self.emit_diag_at_span(Diagnostic::new(Bug, msg), span); + let mut diag = Diagnostic::new(Bug, msg); + diag.set_span(span); + self.emit_diagnostic(diag); } #[track_caller] #[rustc_lint_diagnostics] - pub fn span_note_without_error( - &self, - span: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, - ) { - self.emit_diag_at_span(Diagnostic::new(Note, msg), span); + pub fn span_note(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) { + self.struct_span_note(span, msg).emit() } #[track_caller] #[rustc_lint_diagnostics] - pub fn span_note_diag( + pub fn struct_span_note( &self, - span: Span, + span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ()> { let mut db = DiagnosticBuilder::new(self, Note, msg); @@ -1043,35 +1095,34 @@ impl Handler { db } - // NOTE: intentionally doesn't raise an error so rustc_codegen_ssa only reports fatal errors in the main thread #[rustc_lint_diagnostics] - pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> FatalError { - self.inner.borrow_mut().fatal(msg) + pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> ! { + self.struct_fatal(msg).emit() } #[rustc_lint_diagnostics] pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { - self.inner.borrow_mut().err(msg) + self.struct_err(msg).emit() } #[rustc_lint_diagnostics] pub fn warn(&self, msg: impl Into<DiagnosticMessage>) { - let mut db = DiagnosticBuilder::new(self, Warning(None), msg); - db.emit(); + self.struct_warn(msg).emit() } #[rustc_lint_diagnostics] - pub fn note_without_error(&self, msg: impl Into<DiagnosticMessage>) { - DiagnosticBuilder::new(self, Note, msg).emit(); + pub fn note(&self, msg: impl Into<DiagnosticMessage>) { + self.struct_note(msg).emit() } pub fn bug(&self, msg: impl Into<DiagnosticMessage>) -> ! { - self.inner.borrow_mut().bug(msg) + DiagnosticBuilder::<diagnostic_builder::Bug>::new(self, Bug, msg).emit(); + panic::panic_any(ExplicitBug); } #[inline] pub fn err_count(&self) -> usize { - self.inner.borrow().err_count() + self.inner.borrow().err_count } pub fn has_errors(&self) -> Option<ErrorGuaranteed> { @@ -1082,26 +1133,103 @@ impl Handler { } pub fn has_errors_or_lint_errors(&self) -> Option<ErrorGuaranteed> { - self.inner.borrow().has_errors_or_lint_errors().then(|| { + let inner = self.inner.borrow(); + let has_errors_or_lint_errors = inner.has_errors() || inner.lint_err_count > 0; + has_errors_or_lint_errors.then(|| { #[allow(deprecated)] ErrorGuaranteed::unchecked_claim_error_was_emitted() }) } - pub fn has_errors_or_delayed_span_bugs(&self) -> Option<ErrorGuaranteed> { - self.inner.borrow().has_errors_or_delayed_span_bugs().then(|| { + + pub fn has_errors_or_span_delayed_bugs(&self) -> Option<ErrorGuaranteed> { + let inner = self.inner.borrow(); + let has_errors_or_span_delayed_bugs = + inner.has_errors() || !inner.span_delayed_bugs.is_empty(); + has_errors_or_span_delayed_bugs.then(|| { #[allow(deprecated)] ErrorGuaranteed::unchecked_claim_error_was_emitted() }) } + pub fn is_compilation_going_to_fail(&self) -> Option<ErrorGuaranteed> { - self.inner.borrow().is_compilation_going_to_fail().then(|| { + let inner = self.inner.borrow(); + let will_fail = + inner.has_errors() || inner.lint_err_count > 0 || !inner.span_delayed_bugs.is_empty(); + will_fail.then(|| { #[allow(deprecated)] ErrorGuaranteed::unchecked_claim_error_was_emitted() }) } pub fn print_error_count(&self, registry: &Registry) { - self.inner.borrow_mut().print_error_count(registry) + let mut inner = self.inner.borrow_mut(); + + inner.emit_stashed_diagnostics(); + + let warnings = match inner.deduplicated_warn_count { + 0 => Cow::from(""), + 1 => Cow::from("1 warning emitted"), + count => Cow::from(format!("{count} warnings emitted")), + }; + let errors = match inner.deduplicated_err_count { + 0 => Cow::from(""), + 1 => Cow::from("aborting due to 1 previous error"), + count => Cow::from(format!("aborting due to {count} previous errors")), + }; + if inner.treat_err_as_bug() { + return; + } + + match (errors.len(), warnings.len()) { + (0, 0) => return, + (0, _) => inner.emitter.emit_diagnostic(&Diagnostic::new( + Level::Warning(None), + DiagnosticMessage::Str(warnings), + )), + (_, 0) => { + inner.emit_diagnostic(Diagnostic::new(Fatal, errors)); + } + (_, _) => { + inner.emit_diagnostic(Diagnostic::new(Fatal, format!("{errors}; {warnings}"))); + } + } + + let can_show_explain = inner.emitter.should_show_explain(); + let are_there_diagnostics = !inner.emitted_diagnostic_codes.is_empty(); + if can_show_explain && are_there_diagnostics { + let mut error_codes = inner + .emitted_diagnostic_codes + .iter() + .filter_map(|x| match &x { + DiagnosticId::Error(s) if registry.try_find_description(s).is_ok() => { + Some(s.clone()) + } + _ => None, + }) + .collect::<Vec<_>>(); + if !error_codes.is_empty() { + error_codes.sort(); + if error_codes.len() > 1 { + let limit = if error_codes.len() > 9 { 9 } else { error_codes.len() }; + inner.failure_note(format!( + "Some errors have detailed explanations: {}{}", + error_codes[..limit].join(", "), + if error_codes.len() > 9 { "..." } else { "." } + )); + inner.failure_note(format!( + "For more information about an error, try \ + `rustc --explain {}`.", + &error_codes[0] + )); + } else { + inner.failure_note(format!( + "For more information about this error, try \ + `rustc --explain {}`.", + &error_codes[0] + )); + } + } + } } pub fn take_future_breakage_diagnostics(&self) -> Vec<Diagnostic> { @@ -1109,7 +1237,11 @@ impl Handler { } pub fn abort_if_errors(&self) { - self.inner.borrow_mut().abort_if_errors() + let mut inner = self.inner.borrow_mut(); + inner.emit_stashed_diagnostics(); + if inner.has_errors() { + FatalError.raise(); + } } /// `true` if we haven't taught a diagnostic with this code already. @@ -1118,15 +1250,24 @@ impl Handler { /// Used to suppress emitting the same error multiple times with extended explanation when /// calling `-Zteach`. pub fn must_teach(&self, code: &DiagnosticId) -> bool { - self.inner.borrow_mut().must_teach(code) + self.inner.borrow_mut().taught_diagnostics.insert(code.clone()) } pub fn force_print_diagnostic(&self, db: Diagnostic) { - self.inner.borrow_mut().force_print_diagnostic(db) + self.inner.borrow_mut().emitter.emit_diagnostic(&db); + } + + pub fn emit_diagnostic(&self, mut diagnostic: Diagnostic) -> Option<ErrorGuaranteed> { + self.emit_diagnostic_without_consuming(&mut diagnostic) } - pub fn emit_diagnostic(&self, diagnostic: &mut Diagnostic) -> Option<ErrorGuaranteed> { - self.inner.borrow_mut().emit_diagnostic(diagnostic) + // It's unfortunate this exists. `emit_diagnostic` is preferred, because it + // consumes the diagnostic, thus ensuring it is emitted just once. + pub(crate) fn emit_diagnostic_without_consuming( + &self, + diagnostic: &mut Diagnostic, + ) -> Option<ErrorGuaranteed> { + self.inner.borrow_mut().emit_diagnostic_without_consuming(diagnostic) } pub fn emit_err<'a>(&'a self, err: impl IntoDiagnostic<'a>) -> ErrorGuaranteed { @@ -1201,17 +1342,8 @@ impl Handler { note.into_diagnostic(self) } - fn emit_diag_at_span( - &self, - mut diag: Diagnostic, - sp: impl Into<MultiSpan>, - ) -> Option<ErrorGuaranteed> { - let mut inner = self.inner.borrow_mut(); - inner.emit_diagnostic(diag.set_span(sp)) - } - pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) { - self.inner.borrow_mut().emit_artifact_notification(path, artifact_type) + self.inner.borrow_mut().emitter.emit_artifact_notification(path, artifact_type); } pub fn emit_future_breakage_report(&self, diags: Vec<Diagnostic>) { @@ -1230,7 +1362,7 @@ impl Handler { inner.bump_err_count(); } - inner.emit_unused_externs(lint_level, unused_externs) + inner.emitter.emit_unused_externs(lint_level, unused_externs) } pub fn update_unstable_expectation_id( @@ -1249,7 +1381,7 @@ impl Handler { // Here the diagnostic is given back to `emit_diagnostic` where it was first // intercepted. Now it should be processed as usual, since the unstable expectation // id is now stable. - inner.emit_diagnostic(&mut diag); + inner.emit_diagnostic(diag); } } @@ -1264,38 +1396,34 @@ impl Handler { } /// This methods steals all [`LintExpectationId`]s that are stored inside - /// [`HandlerInner`] and indicate that the linked expectation has been fulfilled. + /// [`DiagCtxtInner`] and indicate that the linked expectation has been fulfilled. #[must_use] pub fn steal_fulfilled_expectation_ids(&self) -> FxHashSet<LintExpectationId> { assert!( self.inner.borrow().unstable_expect_diagnostics.is_empty(), - "`HandlerInner::unstable_expect_diagnostics` should be empty at this point", + "`DiagCtxtInner::unstable_expect_diagnostics` should be empty at this point", ); std::mem::take(&mut self.inner.borrow_mut().fulfilled_expectations) } pub fn flush_delayed(&self) { - let mut inner = self.inner.lock(); - let bugs = std::mem::replace(&mut inner.delayed_span_bugs, Vec::new()); - inner.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued"); + let mut inner = self.inner.borrow_mut(); + 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"); } } -impl HandlerInner { - fn must_teach(&mut self, code: &DiagnosticId) -> bool { - self.taught_diagnostics.insert(code.clone()) - } - - fn force_print_diagnostic(&mut self, db: Diagnostic) { - self.emitter.emit_diagnostic(&db); - } - +// Note: we prefer implementing operations on `DiagCtxt`, rather than +// `DiagCtxtInner`, whenever possible. This minimizes functions where +// `DiagCtxt::foo()` just borrows `inner` and forwards a call to +// `HanderInner::foo`. +impl DiagCtxtInner { /// Emit all stashed diagnostics. fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> { let has_errors = self.has_errors(); let diags = self.stashed_diagnostics.drain(..).map(|x| x.1).collect::<Vec<_>>(); let mut reported = None; - for mut diag in diags { + for diag in diags { // Decrement the count tracking the stash; emitting will increment it. if diag.is_error() { if matches!(diag.level, Level::Error { lint: true }) { @@ -1315,14 +1443,25 @@ impl HandlerInner { } } } - let reported_this = self.emit_diagnostic(&mut diag); + let reported_this = self.emit_diagnostic(diag); reported = reported.or(reported_this); } reported } - // FIXME(eddyb) this should ideally take `diagnostic` by value. - fn emit_diagnostic(&mut self, diagnostic: &mut Diagnostic) -> Option<ErrorGuaranteed> { + fn emit_diagnostic(&mut self, mut diagnostic: Diagnostic) -> Option<ErrorGuaranteed> { + self.emit_diagnostic_without_consuming(&mut diagnostic) + } + + fn emit_diagnostic_without_consuming( + &mut self, + diagnostic: &mut Diagnostic, + ) -> Option<ErrorGuaranteed> { + if matches!(diagnostic.level, Level::Error { .. } | Level::Fatal) && self.treat_err_as_bug() + { + diagnostic.level = Level::Bug; + } + // The `LintExpectationId` can be stable or unstable depending on when it was created. // Diagnostics created before the definition of `HirId`s are unstable and can not yet // be stored. Instead, they are buffered until the `LintExpectationId` is replaced by @@ -1334,11 +1473,11 @@ impl HandlerInner { if diagnostic.level == Level::DelayedBug { // FIXME(eddyb) this should check for `has_errors` and stop pushing - // once *any* errors were emitted (and truncate `delayed_span_bugs` + // once *any* errors were emitted (and truncate `span_delayed_bugs` // when an error is first emitted, also), but maybe there's a case // in which that's not sound? otherwise this is really inefficient. let backtrace = std::backtrace::Backtrace::capture(); - self.delayed_span_bugs + self.span_delayed_bugs .push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace)); if !self.flags.report_delayed_bugs { @@ -1438,227 +1577,30 @@ impl HandlerInner { guaranteed } - fn emit_artifact_notification(&mut self, path: &Path, artifact_type: &str) { - self.emitter.emit_artifact_notification(path, artifact_type); - } - - fn emit_unused_externs(&mut self, lint_level: rustc_lint_defs::Level, unused_externs: &[&str]) { - self.emitter.emit_unused_externs(lint_level, unused_externs); - } - fn treat_err_as_bug(&self) -> bool { self.flags.treat_err_as_bug.is_some_and(|c| { - self.err_count() + self.lint_err_count + self.delayed_bug_count() >= c.get() + self.err_count + self.lint_err_count + self.delayed_bug_count() >= c.get() }) } fn delayed_bug_count(&self) -> usize { - self.delayed_span_bugs.len() + self.delayed_good_path_bugs.len() - } - - fn print_error_count(&mut self, registry: &Registry) { - self.emit_stashed_diagnostics(); - - let warnings = match self.deduplicated_warn_count { - 0 => Cow::from(""), - 1 => Cow::from("1 warning emitted"), - count => Cow::from(format!("{count} warnings emitted")), - }; - let errors = match self.deduplicated_err_count { - 0 => Cow::from(""), - 1 => Cow::from("aborting due to previous error"), - count => Cow::from(format!("aborting due to {count} previous errors")), - }; - if self.treat_err_as_bug() { - return; - } - - match (errors.len(), warnings.len()) { - (0, 0) => return, - (0, _) => self.emitter.emit_diagnostic(&Diagnostic::new( - Level::Warning(None), - DiagnosticMessage::Str(warnings), - )), - (_, 0) => { - let _ = self.fatal(errors); - } - (_, _) => { - let _ = self.fatal(format!("{errors}; {warnings}")); - } - } - - let can_show_explain = self.emitter.should_show_explain(); - let are_there_diagnostics = !self.emitted_diagnostic_codes.is_empty(); - if can_show_explain && are_there_diagnostics { - let mut error_codes = self - .emitted_diagnostic_codes - .iter() - .filter_map(|x| match &x { - DiagnosticId::Error(s) if registry.try_find_description(s).is_ok() => { - Some(s.clone()) - } - _ => None, - }) - .collect::<Vec<_>>(); - if !error_codes.is_empty() { - error_codes.sort(); - if error_codes.len() > 1 { - let limit = if error_codes.len() > 9 { 9 } else { error_codes.len() }; - self.failure(format!( - "Some errors have detailed explanations: {}{}", - error_codes[..limit].join(", "), - if error_codes.len() > 9 { "..." } else { "." } - )); - self.failure(format!( - "For more information about an error, try \ - `rustc --explain {}`.", - &error_codes[0] - )); - } else { - self.failure(format!( - "For more information about this error, try \ - `rustc --explain {}`.", - &error_codes[0] - )); - } - } - } - } - - fn stash(&mut self, key: (Span, StashKey), diagnostic: Diagnostic) { - // Track the diagnostic for counts, but don't panic-if-treat-err-as-bug - // yet; that happens when we actually emit the diagnostic. - if diagnostic.is_error() { - if matches!(diagnostic.level, Level::Error { lint: true }) { - self.lint_err_count += 1; - } else { - self.err_count += 1; - } - } else { - // Warnings are only automatically flushed if they're forced. - if diagnostic.is_force_warn() { - self.warn_count += 1; - } - } - - // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic - // if/when we have a more robust macro-friendly replacement for `(span, key)` as a key. - // See the PR for a discussion. - self.stashed_diagnostics.insert(key, diagnostic); - } - - fn steal(&mut self, key: (Span, StashKey)) -> Option<Diagnostic> { - let diagnostic = self.stashed_diagnostics.remove(&key)?; - if diagnostic.is_error() { - if matches!(diagnostic.level, Level::Error { lint: true }) { - self.lint_err_count -= 1; - } else { - self.err_count -= 1; - } - } else { - if diagnostic.is_force_warn() { - self.warn_count -= 1; - } - } - Some(diagnostic) - } - - #[inline] - fn err_count(&self) -> usize { - self.err_count + self.span_delayed_bugs.len() + self.good_path_delayed_bugs.len() } fn has_errors(&self) -> bool { - self.err_count() > 0 - } - fn has_errors_or_lint_errors(&self) -> bool { - self.has_errors() || self.lint_err_count > 0 - } - fn has_errors_or_delayed_span_bugs(&self) -> bool { - self.has_errors() || !self.delayed_span_bugs.is_empty() - } - fn has_any_message(&self) -> bool { - self.err_count() > 0 || self.lint_err_count > 0 || self.warn_count > 0 - } - - fn is_compilation_going_to_fail(&self) -> bool { - self.has_errors() || self.lint_err_count > 0 || !self.delayed_span_bugs.is_empty() - } - - fn abort_if_errors(&mut self) { - self.emit_stashed_diagnostics(); - - if self.has_errors() { - FatalError.raise(); - } + self.err_count > 0 } #[track_caller] - fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: impl Into<String>) -> ! { - self.emit_diag_at_span(Diagnostic::new(Bug, msg.into()), sp); + fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { + let mut diag = Diagnostic::new(Bug, msg); + diag.set_span(sp); + self.emit_diagnostic(diag); panic::panic_any(ExplicitBug); } - fn emit_diag_at_span(&mut self, mut diag: Diagnostic, sp: impl Into<MultiSpan>) { - self.emit_diagnostic(diag.set_span(sp)); - } - - /// For documentation on this, see `Session::delay_span_bug`. - #[track_caller] - fn delay_span_bug( - &mut self, - sp: impl Into<MultiSpan>, - msg: impl Into<String>, - ) -> ErrorGuaranteed { - // This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before - // incrementing `err_count` by one, so we need to +1 the comparing. - // FIXME: Would be nice to increment err_count in a more coherent way. - if self.flags.treat_err_as_bug.is_some_and(|c| { - self.err_count() + self.lint_err_count + self.delayed_bug_count() + 1 >= c.get() - }) { - // FIXME: don't abort here if report_delayed_bugs is off - self.span_bug(sp, msg.into()); - } - let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg.into()); - diagnostic.set_span(sp.into()); - self.emit_diagnostic(&mut diagnostic).unwrap() - } - - // FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's - // where the explanation of what "good path" is (also, it should be renamed). - fn delay_good_path_bug(&mut self, msg: impl Into<DiagnosticMessage>) { - let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg); - if self.flags.report_delayed_bugs { - self.emit_diagnostic(&mut diagnostic); - } - let backtrace = std::backtrace::Backtrace::capture(); - self.delayed_good_path_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace)); - } - - fn failure(&mut self, msg: impl Into<DiagnosticMessage>) { - self.emit_diagnostic(&mut Diagnostic::new(FailureNote, msg)); - } - - fn fatal(&mut self, msg: impl Into<DiagnosticMessage>) -> FatalError { - self.emit(Fatal, msg); - FatalError - } - - fn err(&mut self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { - self.emit(Error { lint: false }, msg) - } - - /// Emit an error; level should be `Error` or `Fatal`. - fn emit(&mut self, level: Level, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { - if self.treat_err_as_bug() { - self.bug(msg); - } - self.emit_diagnostic(&mut Diagnostic::new(level, msg)).unwrap() - } - - fn bug(&mut self, msg: impl Into<DiagnosticMessage>) -> ! { - self.emit_diagnostic(&mut Diagnostic::new(Bug, msg)); - panic::panic_any(ExplicitBug); + fn failure_note(&mut self, msg: impl Into<DiagnosticMessage>) { + self.emit_diagnostic(Diagnostic::new(FailureNote, msg)); } fn flush_delayed( @@ -1690,7 +1632,7 @@ impl HandlerInner { if no_bugs { // Put the overall explanation before the `DelayedBug`s, to // frame them better (e.g. separate warnings from them). - self.emit_diagnostic(&mut Diagnostic::new(Bug, explanation)); + self.emit_diagnostic(Diagnostic::new(Bug, explanation)); no_bugs = false; } @@ -1705,7 +1647,7 @@ impl HandlerInner { } bug.level = Level::Bug; - self.emit_diagnostic(&mut bug); + self.emit_diagnostic(bug); } // Panic with `DelayedBugPanic` to avoid "unexpected panic" messages. @@ -1731,7 +1673,7 @@ impl HandlerInner { fn panic_if_treat_err_as_bug(&self) { if self.treat_err_as_bug() { match ( - self.err_count() + self.lint_err_count, + self.err_count + self.lint_err_count, self.delayed_bug_count(), self.flags.treat_err_as_bug.map(|c| c.get()).unwrap(), ) { |