summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_errors/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_errors/src')
-rw-r--r--compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs4
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs77
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs298
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs24
-rw-r--r--compiler/rustc_errors/src/emitter.rs37
-rw-r--r--compiler/rustc_errors/src/json.rs65
-rw-r--r--compiler/rustc_errors/src/json/tests.rs13
-rw-r--r--compiler/rustc_errors/src/lib.rs620
-rw-r--r--compiler/rustc_errors/src/markdown/parse.rs2
-rw-r--r--compiler/rustc_errors/src/markdown/tests/term.rs1
10 files changed, 464 insertions, 677 deletions
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index 203e52912..da266bf9c 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -138,7 +138,7 @@ impl AnnotateSnippetEmitterWriter {
let message = self.translate_messages(messages, args);
if let Some(source_map) = &self.source_map {
// Make sure our primary file comes first
- let primary_lo = if let Some(ref primary_span) = msp.primary_span().as_ref() {
+ let primary_lo = if let Some(primary_span) = msp.primary_span().as_ref() {
if primary_span.is_dummy() {
// FIXME(#59346): Not sure when this is the case and what
// should be done if it happens
@@ -203,7 +203,7 @@ impl AnnotateSnippetEmitterWriter {
Slice {
source,
line_start: *line_index,
- origin: Some(&file_name),
+ origin: Some(file_name),
// FIXME(#59346): Not really sure when `fold` should be true or false
fold: false,
annotations: annotations
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 470f318eb..be5068060 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -91,10 +91,7 @@ where
#[rustc_diagnostic_item = "DecorateLint"]
pub trait DecorateLint<'a, G: EmissionGuarantee> {
/// Decorate and emit a lint.
- fn decorate_lint<'b>(
- self,
- diag: &'b mut DiagnosticBuilder<'a, G>,
- ) -> &'b mut DiagnosticBuilder<'a, G>;
+ fn decorate_lint<'b>(self, diag: &'b mut DiagnosticBuilder<'a, G>);
fn msg(&self) -> DiagnosticMessage;
}
@@ -239,7 +236,7 @@ impl Diagnostic {
}
#[track_caller]
- pub fn new_with_code<M: Into<DiagnosticMessage>>(
+ pub(crate) fn new_with_code<M: Into<DiagnosticMessage>>(
level: Level,
code: Option<DiagnosticId>,
message: M,
@@ -281,7 +278,7 @@ impl Diagnostic {
}
}
- pub fn update_unstable_expectation_id(
+ pub(crate) fn update_unstable_expectation_id(
&mut self,
unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>,
) {
@@ -307,14 +304,14 @@ impl Diagnostic {
}
/// Indicates whether this diagnostic should show up in cargo's future breakage report.
- pub fn has_future_breakage(&self) -> bool {
+ pub(crate) fn has_future_breakage(&self) -> bool {
match self.code {
Some(DiagnosticId::Lint { has_future_breakage, .. }) => has_future_breakage,
_ => false,
}
}
- pub fn is_force_warn(&self) -> bool {
+ pub(crate) fn is_force_warn(&self) -> bool {
match self.code {
Some(DiagnosticId::Lint { is_force_warn, .. }) => is_force_warn,
_ => false,
@@ -391,29 +388,6 @@ impl Diagnostic {
self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"")
}
- pub fn note_unsuccessful_coercion(
- &mut self,
- expected: DiagnosticStyledString,
- found: DiagnosticStyledString,
- ) -> &mut Self {
- let mut msg: Vec<_> =
- vec![(Cow::from("required when trying to coerce from type `"), Style::NoStyle)];
- msg.extend(expected.0.iter().map(|x| match *x {
- StringPart::Normal(ref s) => (Cow::from(s.clone()), Style::NoStyle),
- StringPart::Highlighted(ref s) => (Cow::from(s.clone()), Style::Highlight),
- }));
- msg.push((Cow::from("` to type '"), Style::NoStyle));
- msg.extend(found.0.iter().map(|x| match *x {
- StringPart::Normal(ref s) => (Cow::from(s.clone()), Style::NoStyle),
- StringPart::Highlighted(ref s) => (Cow::from(s.clone()), Style::Highlight),
- }));
- msg.push((Cow::from("`"), Style::NoStyle));
-
- // For now, just attach these as notes
- self.highlighted_note(msg);
- self
- }
-
pub fn note_expected_found_extra(
&mut self,
expected_label: &dyn fmt::Display,
@@ -475,7 +449,7 @@ impl Diagnostic {
self
}
- pub fn highlighted_note<M: Into<SubdiagnosticMessage>>(
+ fn highlighted_note<M: Into<SubdiagnosticMessage>>(
&mut self,
msg: Vec<(M, Style)>,
) -> &mut Self {
@@ -572,14 +546,6 @@ impl Diagnostic {
self
}
- /// Clear any existing suggestions.
- pub fn clear_suggestions(&mut self) -> &mut Self {
- if let Ok(suggestions) = &mut self.suggestions {
- suggestions.clear();
- }
- self
- }
-
/// Helper for pushing to `self.suggestions`, if available (not disable).
fn push_suggestion(&mut self, suggestion: CodeSuggestion) {
if let Ok(suggestions) = &mut self.suggestions {
@@ -622,17 +588,18 @@ impl Diagnostic {
pub fn multipart_suggestion_with_style(
&mut self,
msg: impl Into<SubdiagnosticMessage>,
- suggestion: Vec<(Span, String)>,
+ mut suggestion: Vec<(Span, String)>,
applicability: Applicability,
style: SuggestionStyle,
) -> &mut Self {
- let mut parts = suggestion
+ suggestion.sort_unstable();
+ suggestion.dedup();
+
+ let parts = suggestion
.into_iter()
.map(|(span, snippet)| SubstitutionPart { snippet, span })
.collect::<Vec<_>>();
- parts.sort_unstable_by_key(|part| part.span);
-
assert!(!parts.is_empty());
debug_assert_eq!(
parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),
@@ -777,17 +744,15 @@ impl Diagnostic {
applicability: Applicability,
style: SuggestionStyle,
) -> &mut Self {
- let mut suggestions: Vec<_> = suggestions.into_iter().collect();
- suggestions.sort();
-
- debug_assert!(
- !(sp.is_empty() && suggestions.iter().any(|suggestion| suggestion.is_empty())),
- "Span must not be empty and have no suggestion"
- );
-
let substitutions = suggestions
.into_iter()
- .map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] })
+ .map(|snippet| {
+ debug_assert!(
+ !(sp.is_empty() && snippet.is_empty()),
+ "Span must not be empty and have no suggestion"
+ );
+ Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] }
+ })
.collect();
self.push_suggestion(CodeSuggestion {
substitutions,
@@ -921,13 +886,13 @@ impl Diagnostic {
/// interpolated variables).
pub fn eager_subdiagnostic(
&mut self,
- handler: &crate::Handler,
+ dcx: &crate::DiagCtxt,
subdiagnostic: impl AddToDiagnostic,
) -> &mut Self {
subdiagnostic.add_to_diagnostic_with(self, |diag, msg| {
let args = diag.args();
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
- handler.eagerly_translate(msg, args)
+ dcx.eagerly_translate(msg, args)
});
self
}
@@ -994,7 +959,7 @@ impl Diagnostic {
/// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by
/// combining it with the primary message of the diagnostic (if translatable, otherwise it just
/// passes the user's string along).
- pub(crate) fn subdiagnostic_message_to_diagnostic_message(
+ fn subdiagnostic_message_to_diagnostic_message(
&self,
attr: impl Into<SubdiagnosticMessage>,
) -> DiagnosticMessage {
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index 85acf8ab5..3f66af1fc 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -1,9 +1,9 @@
use crate::diagnostic::IntoDiagnosticArg;
+use crate::{DiagCtxt, Level, MultiSpan, StashKey};
use crate::{
Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed,
ExplicitBug, SubdiagnosticMessage,
};
-use crate::{Handler, Level, MultiSpan, StashKey};
use rustc_lint_defs::Applicability;
use rustc_span::source_map::Spanned;
@@ -18,19 +18,19 @@ use std::thread::panicking;
/// Trait implemented by error types. This should not be implemented manually. Instead, use
/// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic].
#[rustc_diagnostic_item = "IntoDiagnostic"]
-pub trait IntoDiagnostic<'a, T: EmissionGuarantee = ErrorGuaranteed> {
- /// Write out as a diagnostic out of `Handler`.
+pub trait IntoDiagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> {
+ /// Write out as a diagnostic out of `DiagCtxt`.
#[must_use]
- fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, T>;
+ fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, G>;
}
-impl<'a, T, E> IntoDiagnostic<'a, E> for Spanned<T>
+impl<'a, T, G> IntoDiagnostic<'a, G> for Spanned<T>
where
- T: IntoDiagnostic<'a, E>,
- E: EmissionGuarantee,
+ T: IntoDiagnostic<'a, G>,
+ G: EmissionGuarantee,
{
- fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, E> {
- let mut diag = self.node.into_diagnostic(handler);
+ fn into_diagnostic(self, dcx: &'a DiagCtxt) -> DiagnosticBuilder<'a, G> {
+ let mut diag = self.node.into_diagnostic(dcx);
diag.set_span(self.span);
diag
}
@@ -40,7 +40,7 @@ where
///
/// If there is some state in a downstream crate you would like to
/// access in the methods of `DiagnosticBuilder` here, consider
-/// extending `HandlerFlags`, accessed via `self.handler.flags`.
+/// extending `DiagCtxtFlags`.
#[must_use]
#[derive(Clone)]
pub struct DiagnosticBuilder<'a, G: EmissionGuarantee> {
@@ -74,8 +74,8 @@ struct DiagnosticBuilderInner<'a> {
enum DiagnosticBuilderState<'a> {
/// Initial state of a `DiagnosticBuilder`, before `.emit()` or `.cancel()`.
///
- /// The `Diagnostic` will be emitted through this `Handler`.
- Emittable(&'a Handler),
+ /// The `Diagnostic` will be emitted through this `DiagCtxt`.
+ Emittable(&'a DiagCtxt),
/// State of a `DiagnosticBuilder`, after `.emit()` or *during* `.cancel()`.
///
@@ -95,7 +95,7 @@ enum DiagnosticBuilderState<'a> {
// `DiagnosticBuilderState` should be pointer-sized.
rustc_data_structures::static_assert_size!(
DiagnosticBuilderState<'_>,
- std::mem::size_of::<&Handler>()
+ std::mem::size_of::<&DiagCtxt>()
);
/// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee"
@@ -110,32 +110,12 @@ pub trait EmissionGuarantee: Sized {
/// Creates a new `DiagnosticBuilder` that will return this type of guarantee.
#[track_caller]
fn make_diagnostic_builder(
- handler: &Handler,
+ dcx: &DiagCtxt,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, Self>;
}
impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
- /// Convenience function for internal use, clients should use one of the
- /// `struct_*` methods on [`Handler`].
- #[track_caller]
- pub(crate) fn new_guaranteeing_error<M: Into<DiagnosticMessage>>(
- handler: &'a Handler,
- message: M,
- ) -> Self {
- Self {
- inner: DiagnosticBuilderInner {
- state: DiagnosticBuilderState::Emittable(handler),
- diagnostic: Box::new(Diagnostic::new_with_code(
- Level::Error { lint: false },
- None,
- message,
- )),
- },
- _marker: PhantomData,
- }
- }
-
/// Discard the guarantee `.emit()` would return, in favor of having the
/// type `DiagnosticBuilder<'a, ()>`. This may be necessary whenever there
/// is a common codepath handling both errors and warnings.
@@ -148,11 +128,11 @@ impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
impl EmissionGuarantee for ErrorGuaranteed {
fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
match db.inner.state {
- // First `.emit()` call, the `&Handler` is still available.
- DiagnosticBuilderState::Emittable(handler) => {
+ // First `.emit()` call, the `&DiagCtxt` is still available.
+ DiagnosticBuilderState::Emittable(dcx) => {
db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
- let guar = handler.emit_diagnostic(&mut db.inner.diagnostic);
+ let guar = dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
// Only allow a guarantee if the `level` wasn't switched to a
// non-error - the field isn't `pub`, but the whole `Diagnostic`
@@ -186,38 +166,10 @@ impl EmissionGuarantee for ErrorGuaranteed {
#[track_caller]
fn make_diagnostic_builder(
- handler: &Handler,
+ dcx: &DiagCtxt,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, Self> {
- DiagnosticBuilder::new_guaranteeing_error(handler, msg)
- }
-}
-
-impl<'a> DiagnosticBuilder<'a, ()> {
- /// Convenience function for internal use, clients should use one of the
- /// `struct_*` methods on [`Handler`].
- #[track_caller]
- pub(crate) fn new<M: Into<DiagnosticMessage>>(
- handler: &'a Handler,
- level: Level,
- message: M,
- ) -> Self {
- let diagnostic = Diagnostic::new_with_code(level, None, message);
- Self::new_diagnostic(handler, diagnostic)
- }
-
- /// Creates a new `DiagnosticBuilder` with an already constructed
- /// diagnostic.
- #[track_caller]
- pub(crate) fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
- debug!("Created new diagnostic");
- Self {
- inner: DiagnosticBuilderInner {
- state: DiagnosticBuilderState::Emittable(handler),
- diagnostic: Box::new(diagnostic),
- },
- _marker: PhantomData,
- }
+ DiagnosticBuilder::new(dcx, Level::Error { lint: false }, msg)
}
}
@@ -225,11 +177,11 @@ impl<'a> DiagnosticBuilder<'a, ()> {
impl EmissionGuarantee for () {
fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
match db.inner.state {
- // First `.emit()` call, the `&Handler` is still available.
- DiagnosticBuilderState::Emittable(handler) => {
+ // First `.emit()` call, the `&DiagCtxt` is still available.
+ DiagnosticBuilderState::Emittable(dcx) => {
db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
- handler.emit_diagnostic(&mut db.inner.diagnostic);
+ dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
}
// `.emit()` was previously called, disallowed from repeating it.
DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
@@ -237,10 +189,10 @@ impl EmissionGuarantee for () {
}
fn make_diagnostic_builder(
- handler: &Handler,
+ dcx: &DiagCtxt,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, Self> {
- DiagnosticBuilder::new(handler, Level::Warning(None), msg)
+ DiagnosticBuilder::new(dcx, Level::Warning(None), msg)
}
}
@@ -249,35 +201,13 @@ impl EmissionGuarantee for () {
#[derive(Copy, Clone)]
pub struct Noted;
-impl<'a> DiagnosticBuilder<'a, Noted> {
- /// Convenience function for internal use, clients should use one of the
- /// `struct_*` methods on [`Handler`].
- pub(crate) fn new_note(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self {
- let diagnostic = Diagnostic::new_with_code(Level::Note, None, message);
- Self::new_diagnostic_note(handler, diagnostic)
- }
-
- /// Creates a new `DiagnosticBuilder` with an already constructed
- /// diagnostic.
- pub(crate) fn new_diagnostic_note(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
- debug!("Created new diagnostic");
- Self {
- inner: DiagnosticBuilderInner {
- state: DiagnosticBuilderState::Emittable(handler),
- diagnostic: Box::new(diagnostic),
- },
- _marker: PhantomData,
- }
- }
-}
-
impl EmissionGuarantee for Noted {
fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
match db.inner.state {
- // First `.emit()` call, the `&Handler` is still available.
- DiagnosticBuilderState::Emittable(handler) => {
+ // First `.emit()` call, the `&DiagCtxt` is still available.
+ DiagnosticBuilderState::Emittable(dcx) => {
db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
- handler.emit_diagnostic(&mut db.inner.diagnostic);
+ dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
}
// `.emit()` was previously called, disallowed from repeating it.
DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
@@ -287,10 +217,10 @@ impl EmissionGuarantee for Noted {
}
fn make_diagnostic_builder(
- handler: &Handler,
+ dcx: &DiagCtxt,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, Self> {
- DiagnosticBuilder::new_note(handler, msg)
+ DiagnosticBuilder::new(dcx, Level::Note, msg)
}
}
@@ -299,37 +229,14 @@ impl EmissionGuarantee for Noted {
#[derive(Copy, Clone)]
pub struct Bug;
-impl<'a> DiagnosticBuilder<'a, Bug> {
- /// Convenience function for internal use, clients should use one of the
- /// `struct_*` methods on [`Handler`].
- #[track_caller]
- pub(crate) fn new_bug(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self {
- let diagnostic = Diagnostic::new_with_code(Level::Bug, None, message);
- Self::new_diagnostic_bug(handler, diagnostic)
- }
-
- /// Creates a new `DiagnosticBuilder` with an already constructed
- /// diagnostic.
- pub(crate) fn new_diagnostic_bug(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
- debug!("Created new diagnostic bug");
- Self {
- inner: DiagnosticBuilderInner {
- state: DiagnosticBuilderState::Emittable(handler),
- diagnostic: Box::new(diagnostic),
- },
- _marker: PhantomData,
- }
- }
-}
-
impl EmissionGuarantee for Bug {
fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
match db.inner.state {
- // First `.emit()` call, the `&Handler` is still available.
- DiagnosticBuilderState::Emittable(handler) => {
+ // First `.emit()` call, the `&DiagCtxt` is still available.
+ DiagnosticBuilderState::Emittable(dcx) => {
db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
- handler.emit_diagnostic(&mut db.inner.diagnostic);
+ dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
}
// `.emit()` was previously called, disallowed from repeating it.
DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
@@ -339,44 +246,21 @@ impl EmissionGuarantee for Bug {
}
fn make_diagnostic_builder(
- handler: &Handler,
+ dcx: &DiagCtxt,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, Self> {
- DiagnosticBuilder::new_bug(handler, msg)
- }
-}
-
-impl<'a> DiagnosticBuilder<'a, !> {
- /// Convenience function for internal use, clients should use one of the
- /// `struct_*` methods on [`Handler`].
- #[track_caller]
- pub(crate) fn new_fatal(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self {
- let diagnostic = Diagnostic::new_with_code(Level::Fatal, None, message);
- Self::new_diagnostic_fatal(handler, diagnostic)
- }
-
- /// Creates a new `DiagnosticBuilder` with an already constructed
- /// diagnostic.
- pub(crate) fn new_diagnostic_fatal(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
- debug!("Created new diagnostic");
- Self {
- inner: DiagnosticBuilderInner {
- state: DiagnosticBuilderState::Emittable(handler),
- diagnostic: Box::new(diagnostic),
- },
- _marker: PhantomData,
- }
+ DiagnosticBuilder::new(dcx, Level::Bug, msg)
}
}
impl EmissionGuarantee for ! {
fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
match db.inner.state {
- // First `.emit()` call, the `&Handler` is still available.
- DiagnosticBuilderState::Emittable(handler) => {
+ // First `.emit()` call, the `&DiagCtxt` is still available.
+ DiagnosticBuilderState::Emittable(dcx) => {
db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
- handler.emit_diagnostic(&mut db.inner.diagnostic);
+ dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
}
// `.emit()` was previously called, disallowed from repeating it.
DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
@@ -386,50 +270,21 @@ impl EmissionGuarantee for ! {
}
fn make_diagnostic_builder(
- handler: &Handler,
+ dcx: &DiagCtxt,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, Self> {
- DiagnosticBuilder::new_fatal(handler, msg)
- }
-}
-
-impl<'a> DiagnosticBuilder<'a, rustc_span::fatal_error::FatalError> {
- /// Convenience function for internal use, clients should use one of the
- /// `struct_*` methods on [`Handler`].
- #[track_caller]
- pub(crate) fn new_almost_fatal(
- handler: &'a Handler,
- message: impl Into<DiagnosticMessage>,
- ) -> Self {
- let diagnostic = Diagnostic::new_with_code(Level::Fatal, None, message);
- Self::new_diagnostic_almost_fatal(handler, diagnostic)
- }
-
- /// Creates a new `DiagnosticBuilder` with an already constructed
- /// diagnostic.
- pub(crate) fn new_diagnostic_almost_fatal(
- handler: &'a Handler,
- diagnostic: Diagnostic,
- ) -> Self {
- debug!("Created new diagnostic");
- Self {
- inner: DiagnosticBuilderInner {
- state: DiagnosticBuilderState::Emittable(handler),
- diagnostic: Box::new(diagnostic),
- },
- _marker: PhantomData,
- }
+ DiagnosticBuilder::new(dcx, Level::Fatal, msg)
}
}
impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
match db.inner.state {
- // First `.emit()` call, the `&Handler` is still available.
- DiagnosticBuilderState::Emittable(handler) => {
+ // First `.emit()` call, the `&DiagCtxt` is still available.
+ DiagnosticBuilderState::Emittable(dcx) => {
db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
- handler.emit_diagnostic(&mut db.inner.diagnostic);
+ dcx.emit_diagnostic_without_consuming(&mut db.inner.diagnostic);
}
// `.emit()` was previously called, disallowed from repeating it.
DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
@@ -439,10 +294,10 @@ impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
}
fn make_diagnostic_builder(
- handler: &Handler,
+ dcx: &DiagCtxt,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, Self> {
- DiagnosticBuilder::new_almost_fatal(handler, msg)
+ DiagnosticBuilder::new(dcx, Level::Fatal, msg)
}
}
@@ -484,7 +339,35 @@ impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> {
}
impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
- /// Emit the diagnostic.
+ /// Convenience function for internal use, clients should use one of the
+ /// `struct_*` methods on [`DiagCtxt`].
+ #[track_caller]
+ pub(crate) fn new<M: Into<DiagnosticMessage>>(
+ dcx: &'a DiagCtxt,
+ level: Level,
+ message: M,
+ ) -> Self {
+ let diagnostic = Diagnostic::new(level, message);
+ Self::new_diagnostic(dcx, diagnostic)
+ }
+
+ /// Creates a new `DiagnosticBuilder` with an already constructed
+ /// diagnostic.
+ #[track_caller]
+ pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diagnostic: Diagnostic) -> Self {
+ debug!("Created new diagnostic");
+ Self {
+ inner: DiagnosticBuilderInner {
+ state: DiagnosticBuilderState::Emittable(dcx),
+ diagnostic: Box::new(diagnostic),
+ },
+ _marker: PhantomData,
+ }
+ }
+
+ /// Emit the diagnostic. Does not consume `self`, which may be surprising,
+ /// but there are various places that rely on continuing to use `self`
+ /// after calling `emit`.
#[track_caller]
pub fn emit(&mut self) -> G {
G::diagnostic_builder_emit_producing_guarantee(self)
@@ -515,7 +398,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
/// Stashes diagnostic for possible later improvement in a different,
/// later stage of the compiler. The diagnostic can be accessed with
- /// the provided `span` and `key` through [`Handler::steal_diagnostic()`].
+ /// the provided `span` and `key` through [`DiagCtxt::steal_diagnostic()`].
///
/// As with `buffer`, this is unless the handler has disabled such buffering.
pub fn stash(self, span: Span, key: StashKey) {
@@ -526,18 +409,18 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
/// Converts the builder to a `Diagnostic` for later emission,
/// unless handler has disabled such buffering, or `.emit()` was called.
- pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> {
- let handler = match self.inner.state {
- // No `.emit()` calls, the `&Handler` is still available.
- DiagnosticBuilderState::Emittable(handler) => handler,
+ pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a DiagCtxt)> {
+ let dcx = match self.inner.state {
+ // No `.emit()` calls, the `&DiagCtxt` is still available.
+ DiagnosticBuilderState::Emittable(dcx) => dcx,
// `.emit()` was previously called, nothing we can do.
DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {
return None;
}
};
- if handler.inner.lock().flags.dont_buffer_diagnostics
- || handler.inner.lock().flags.treat_err_as_bug.is_some()
+ if dcx.inner.lock().flags.dont_buffer_diagnostics
+ || dcx.inner.lock().flags.treat_err_as_bug.is_some()
{
self.emit();
return None;
@@ -554,13 +437,13 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
// actually emitted.
debug!("buffer: diagnostic={:?}", diagnostic);
- Some((diagnostic, handler))
+ Some((diagnostic, dcx))
}
- /// Retrieves the [`Handler`] if available
- pub fn handler(&self) -> Option<&Handler> {
+ /// Retrieves the [`DiagCtxt`] if available
+ pub fn dcx(&self) -> Option<&DiagCtxt> {
match self.inner.state {
- DiagnosticBuilderState::Emittable(handler) => Some(handler),
+ DiagnosticBuilderState::Emittable(dcx) => Some(dcx),
DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => None,
}
}
@@ -634,12 +517,6 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
found_extra: &dyn fmt::Display,
) -> &mut Self);
- forward!(pub fn note_unsuccessful_coercion(
- &mut self,
- expected: DiagnosticStyledString,
- found: DiagnosticStyledString,
- ) -> &mut Self);
-
forward!(pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
forward!(pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
forward!(pub fn span_note(
@@ -668,7 +545,6 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
forward!(pub fn set_is_lint(&mut self,) -> &mut Self);
forward!(pub fn disable_suggestions(&mut self,) -> &mut Self);
- forward!(pub fn clear_suggestions(&mut self,) -> &mut Self);
forward!(pub fn multipart_suggestion(
&mut self,
@@ -764,15 +640,15 @@ impl Drop for DiagnosticBuilderInner<'_> {
fn drop(&mut self) {
match self.state {
// No `.emit()` or `.cancel()` calls.
- DiagnosticBuilderState::Emittable(handler) => {
+ DiagnosticBuilderState::Emittable(dcx) => {
if !panicking() {
- handler.emit_diagnostic(&mut Diagnostic::new(
+ dcx.emit_diagnostic(Diagnostic::new(
Level::Bug,
DiagnosticMessage::from(
"the following error was constructed but not emitted",
),
));
- handler.emit_diagnostic(&mut self.diagnostic);
+ dcx.emit_diagnostic_without_consuming(&mut self.diagnostic);
panic!("error was constructed but not emitted");
}
}
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index 4f77f09b2..3e4b3ee75 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -1,6 +1,6 @@
use crate::diagnostic::DiagnosticLocation;
use crate::{fluent_generated as fluent, AddToDiagnostic};
-use crate::{DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg};
+use crate::{DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, IntoDiagnostic, IntoDiagnosticArg};
use rustc_ast as ast;
use rustc_ast_pretty::pprust;
use rustc_hir as hir;
@@ -246,18 +246,18 @@ impl<Id> IntoDiagnosticArg for hir::def::Res<Id> {
}
impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> {
- fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
+ fn into_diagnostic(self, dcx: &DiagCtxt) -> DiagnosticBuilder<'_, !> {
let mut diag;
match self {
TargetDataLayoutErrors::InvalidAddressSpace { addr_space, err, cause } => {
- diag = handler.struct_fatal(fluent::errors_target_invalid_address_space);
+ diag = dcx.struct_fatal(fluent::errors_target_invalid_address_space);
diag.set_arg("addr_space", addr_space);
diag.set_arg("cause", cause);
diag.set_arg("err", err);
diag
}
TargetDataLayoutErrors::InvalidBits { kind, bit, cause, err } => {
- diag = handler.struct_fatal(fluent::errors_target_invalid_bits);
+ diag = dcx.struct_fatal(fluent::errors_target_invalid_bits);
diag.set_arg("kind", kind);
diag.set_arg("bit", bit);
diag.set_arg("cause", cause);
@@ -265,31 +265,31 @@ impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> {
diag
}
TargetDataLayoutErrors::MissingAlignment { cause } => {
- diag = handler.struct_fatal(fluent::errors_target_missing_alignment);
+ diag = dcx.struct_fatal(fluent::errors_target_missing_alignment);
diag.set_arg("cause", cause);
diag
}
TargetDataLayoutErrors::InvalidAlignment { cause, err } => {
- diag = handler.struct_fatal(fluent::errors_target_invalid_alignment);
+ diag = dcx.struct_fatal(fluent::errors_target_invalid_alignment);
diag.set_arg("cause", cause);
diag.set_arg("err_kind", err.diag_ident());
diag.set_arg("align", err.align());
diag
}
TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => {
- diag = handler.struct_fatal(fluent::errors_target_inconsistent_architecture);
+ diag = dcx.struct_fatal(fluent::errors_target_inconsistent_architecture);
diag.set_arg("dl", dl);
diag.set_arg("target", target);
diag
}
TargetDataLayoutErrors::InconsistentTargetPointerWidth { pointer_size, target } => {
- diag = handler.struct_fatal(fluent::errors_target_inconsistent_pointer_width);
+ diag = dcx.struct_fatal(fluent::errors_target_inconsistent_pointer_width);
diag.set_arg("pointer_size", pointer_size);
diag.set_arg("target", target);
diag
}
TargetDataLayoutErrors::InvalidBitsSize { err } => {
- diag = handler.struct_fatal(fluent::errors_target_invalid_bits_size);
+ diag = dcx.struct_fatal(fluent::errors_target_invalid_bits_size);
diag.set_arg("err", err);
diag
}
@@ -378,3 +378,9 @@ pub struct IndicateAnonymousLifetime {
pub count: usize,
pub suggestion: String,
}
+
+impl IntoDiagnosticArg for type_ir::ClosureKind {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Str(self.as_str().into())
+ }
+}
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 68dba8602..3fb993c36 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -16,8 +16,8 @@ use crate::snippet::{
use crate::styled_buffer::StyledBuffer;
use crate::translation::{to_fluent_args, Translate};
use crate::{
- diagnostic::DiagnosticLocation, CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage,
- FluentBundle, Handler, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic,
+ diagnostic::DiagnosticLocation, CodeSuggestion, DiagCtxt, Diagnostic, DiagnosticId,
+ DiagnosticMessage, FluentBundle, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic,
SubstitutionHighlight, SuggestionStyle, TerminalUrl,
};
use rustc_lint_defs::pluralize;
@@ -553,10 +553,10 @@ impl Emitter for EmitterWriter {
}
/// An emitter that does nothing when emitting a non-fatal diagnostic.
-/// Fatal diagnostics are forwarded to `fatal_handler` to avoid silent
+/// Fatal diagnostics are forwarded to `fatal_dcx` to avoid silent
/// failures of rustc, as witnessed e.g. in issue #89358.
pub struct SilentEmitter {
- pub fatal_handler: Handler,
+ pub fatal_dcx: DiagCtxt,
pub fatal_note: Option<String>,
}
@@ -581,7 +581,7 @@ impl Emitter for SilentEmitter {
if let Some(ref note) = self.fatal_note {
d.note(note.clone());
}
- self.fatal_handler.emit_diagnostic(&mut d);
+ self.fatal_dcx.emit_diagnostic(d);
}
}
}
@@ -1297,7 +1297,7 @@ impl EmitterWriter {
buffer.append(line_number, line, style_or_override(*style, override_style));
}
} else {
- buffer.append(line_number, &text, style_or_override(*style, override_style));
+ buffer.append(line_number, text, style_or_override(*style, override_style));
}
}
}
@@ -1931,7 +1931,7 @@ impl EmitterWriter {
self.draw_code_line(
&mut buffer,
&mut row_num,
- &highlight_parts,
+ highlight_parts,
line_pos + line_start,
line,
show_code_change,
@@ -2338,7 +2338,7 @@ impl FileWithAnnotatedLines {
let mut output = vec![];
let mut multiline_annotations = vec![];
- if let Some(ref sm) = emitter.source_map() {
+ if let Some(sm) = emitter.source_map() {
for SpanLabel { span, is_primary, label } in msp.span_labels() {
// If we don't have a useful span, pick the primary span if that exists.
// Worst case we'll just print an error at the top of the main file.
@@ -2362,7 +2362,7 @@ impl FileWithAnnotatedLines {
let label = label.as_ref().map(|m| {
normalize_whitespace(
- &emitter.translate_message(m, &args).map_err(Report::new).unwrap(),
+ &emitter.translate_message(m, args).map_err(Report::new).unwrap(),
)
});
@@ -2674,6 +2674,11 @@ fn from_stderr(color: ColorConfig) -> Destination {
}
}
+/// On Windows, BRIGHT_BLUE is hard to read on black. Use cyan instead.
+///
+/// See #36178.
+const BRIGHT_BLUE: Color = if cfg!(windows) { Color::Cyan } else { Color::Blue };
+
impl Style {
fn color_spec(&self, lvl: Level) -> ColorSpec {
let mut spec = ColorSpec::new();
@@ -2688,11 +2693,7 @@ impl Style {
Style::LineNumber => {
spec.set_bold(true);
spec.set_intense(true);
- if cfg!(windows) {
- spec.set_fg(Some(Color::Cyan));
- } else {
- spec.set_fg(Some(Color::Blue));
- }
+ spec.set_fg(Some(BRIGHT_BLUE));
}
Style::Quotation => {}
Style::MainHeaderMsg => {
@@ -2707,11 +2708,7 @@ impl Style {
}
Style::UnderlineSecondary | Style::LabelSecondary => {
spec.set_bold(true).set_intense(true);
- if cfg!(windows) {
- spec.set_fg(Some(Color::Cyan));
- } else {
- spec.set_fg(Some(Color::Blue));
- }
+ spec.set_fg(Some(BRIGHT_BLUE));
}
Style::HeaderMsg | Style::NoStyle => {}
Style::Level(lvl) => {
@@ -2719,7 +2716,7 @@ impl Style {
spec.set_bold(true);
}
Style::Highlight => {
- spec.set_bold(true);
+ spec.set_bold(true).set_fg(Some(Color::Magenta));
}
}
spec
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index 0cb75c71b..aa3749334 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -145,6 +145,25 @@ impl JsonEmitter {
pub fn ignored_directories_in_source_blocks(self, value: Vec<String>) -> Self {
Self { ignored_directories_in_source_blocks: value, ..self }
}
+
+ fn emit(&mut self, val: EmitTyped<'_>) -> io::Result<()> {
+ if self.pretty {
+ serde_json::to_writer_pretty(&mut *self.dst, &val)?
+ } else {
+ serde_json::to_writer(&mut *self.dst, &val)?
+ };
+ self.dst.write_all(b"\n")?;
+ self.dst.flush()
+ }
+}
+
+#[derive(Serialize)]
+#[serde(tag = "$message_type", rename_all = "snake_case")]
+enum EmitTyped<'a> {
+ Diagnostic(Diagnostic),
+ Artifact(ArtifactNotification<'a>),
+ FutureIncompat(FutureIncompatReport<'a>),
+ UnusedExtern(UnusedExterns<'a, 'a, 'a>),
}
impl Translate for JsonEmitter {
@@ -160,12 +179,7 @@ impl Translate for JsonEmitter {
impl Emitter for JsonEmitter {
fn emit_diagnostic(&mut self, diag: &crate::Diagnostic) {
let data = Diagnostic::from_errors_diagnostic(diag, self);
- let result = if self.pretty {
- writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&data).unwrap())
- } else {
- writeln!(&mut self.dst, "{}", serde_json::to_string(&data).unwrap())
- }
- .and_then(|_| self.dst.flush());
+ let result = self.emit(EmitTyped::Diagnostic(data));
if let Err(e) = result {
panic!("failed to print diagnostics: {e:?}");
}
@@ -173,34 +187,28 @@ impl Emitter for JsonEmitter {
fn emit_artifact_notification(&mut self, path: &Path, artifact_type: &str) {
let data = ArtifactNotification { artifact: path, emit: artifact_type };
- let result = if self.pretty {
- writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&data).unwrap())
- } else {
- writeln!(&mut self.dst, "{}", serde_json::to_string(&data).unwrap())
- }
- .and_then(|_| self.dst.flush());
+ let result = self.emit(EmitTyped::Artifact(data));
if let Err(e) = result {
panic!("failed to print notification: {e:?}");
}
}
fn emit_future_breakage_report(&mut self, diags: Vec<crate::Diagnostic>) {
- let data: Vec<FutureBreakageItem> = diags
+ let data: Vec<FutureBreakageItem<'_>> = diags
.into_iter()
.map(|mut diag| {
if diag.level == crate::Level::Allow {
diag.level = crate::Level::Warning(None);
}
- FutureBreakageItem { diagnostic: Diagnostic::from_errors_diagnostic(&diag, self) }
+ FutureBreakageItem {
+ diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic(
+ &diag, self,
+ )),
+ }
})
.collect();
let report = FutureIncompatReport { future_incompat_report: data };
- let result = if self.pretty {
- writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&report).unwrap())
- } else {
- writeln!(&mut self.dst, "{}", serde_json::to_string(&report).unwrap())
- }
- .and_then(|_| self.dst.flush());
+ let result = self.emit(EmitTyped::FutureIncompat(report));
if let Err(e) = result {
panic!("failed to print future breakage report: {e:?}");
}
@@ -209,12 +217,7 @@ impl Emitter for JsonEmitter {
fn emit_unused_externs(&mut self, lint_level: rustc_lint_defs::Level, unused_externs: &[&str]) {
let lint_level = lint_level.as_str();
let data = UnusedExterns { lint_level, unused_extern_names: unused_externs };
- let result = if self.pretty {
- writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&data).unwrap())
- } else {
- writeln!(&mut self.dst, "{}", serde_json::to_string(&data).unwrap())
- }
- .and_then(|_| self.dst.flush());
+ let result = self.emit(EmitTyped::UnusedExtern(data));
if let Err(e) = result {
panic!("failed to print unused externs: {e:?}");
}
@@ -313,13 +316,15 @@ struct ArtifactNotification<'a> {
}
#[derive(Serialize)]
-struct FutureBreakageItem {
- diagnostic: Diagnostic,
+struct FutureBreakageItem<'a> {
+ // Always EmitTyped::Diagnostic, but we want to make sure it gets serialized
+ // with "$message_type".
+ diagnostic: EmitTyped<'a>,
}
#[derive(Serialize)]
-struct FutureIncompatReport {
- future_incompat_report: Vec<FutureBreakageItem>,
+struct FutureIncompatReport<'a> {
+ future_incompat_report: Vec<FutureBreakageItem<'a>>,
}
// NOTE: Keep this in sync with the equivalent structs in rustdoc's
diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs
index 1f9a2981e..303de0a93 100644
--- a/compiler/rustc_errors/src/json/tests.rs
+++ b/compiler/rustc_errors/src/json/tests.rs
@@ -1,11 +1,8 @@
use super::*;
-use crate::json::JsonEmitter;
-use rustc_span::source_map::{FilePathMapping, SourceMap};
-
-use crate::emitter::{ColorConfig, HumanReadableErrorType};
-use crate::{Handler, TerminalUrl};
-use rustc_span::{BytePos, Span};
+use crate::emitter::ColorConfig;
+use crate::DiagCtxt;
+use rustc_span::BytePos;
use std::str;
@@ -64,8 +61,8 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
);
let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1));
- let handler = Handler::with_emitter(Box::new(je));
- handler.span_err(span, "foo");
+ let dcx = DiagCtxt::with_emitter(Box::new(je));
+ dcx.span_err(span, "foo");
let bytes = output.lock().unwrap();
let actual_output = str::from_utf8(&bytes).unwrap();
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(),
) {
diff --git a/compiler/rustc_errors/src/markdown/parse.rs b/compiler/rustc_errors/src/markdown/parse.rs
index d3a08da62..67e4963fd 100644
--- a/compiler/rustc_errors/src/markdown/parse.rs
+++ b/compiler/rustc_errors/src/markdown/parse.rs
@@ -329,7 +329,7 @@ fn parse_with_end_pat<'a>(
end_sep: &[u8],
ignore_esc: bool,
) -> Option<(&'a [u8], &'a [u8])> {
- // Find positions that start with the end seperator
+ // Find positions that start with the end separator
for idx in (0..buf.len()).filter(|idx| buf[*idx..].starts_with(end_sep)) {
if !ignore_esc && idx > 0 && buf[idx - 1] == b'\\' {
continue;
diff --git a/compiler/rustc_errors/src/markdown/tests/term.rs b/compiler/rustc_errors/src/markdown/tests/term.rs
index 6f68fb25a..a0d956bf0 100644
--- a/compiler/rustc_errors/src/markdown/tests/term.rs
+++ b/compiler/rustc_errors/src/markdown/tests/term.rs
@@ -3,7 +3,6 @@ use std::path::PathBuf;
use termcolor::{BufferWriter, ColorChoice};
use super::*;
-use crate::markdown::MdStream;
const INPUT: &str = include_str!("input.md");
const OUTPUT_PATH: &[&str] = &[env!("CARGO_MANIFEST_DIR"), "src","markdown","tests","output.stdout"];