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 | |
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')
-rw-r--r-- | compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs | 4 | ||||
-rw-r--r-- | compiler/rustc_errors/src/diagnostic.rs | 77 | ||||
-rw-r--r-- | compiler/rustc_errors/src/diagnostic_builder.rs | 298 | ||||
-rw-r--r-- | compiler/rustc_errors/src/diagnostic_impls.rs | 24 | ||||
-rw-r--r-- | compiler/rustc_errors/src/emitter.rs | 37 | ||||
-rw-r--r-- | compiler/rustc_errors/src/json.rs | 65 | ||||
-rw-r--r-- | compiler/rustc_errors/src/json/tests.rs | 13 | ||||
-rw-r--r-- | compiler/rustc_errors/src/lib.rs | 620 | ||||
-rw-r--r-- | compiler/rustc_errors/src/markdown/parse.rs | 2 | ||||
-rw-r--r-- | compiler/rustc_errors/src/markdown/tests/term.rs | 1 |
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"]; |