diff options
Diffstat (limited to 'compiler/rustc_errors/src')
| -rw-r--r-- | compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs | 18 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic.rs | 199 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic_builder.rs | 784 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic_impls.rs | 90 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/emitter.rs | 123 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/json.rs | 75 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/json/tests.rs | 13 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/lib.rs | 1045 | ||||
| -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, 869 insertions, 1481 deletions
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 203e529120b..5c0e210f147 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -20,7 +20,7 @@ use rustc_span::source_map::SourceMap; use rustc_span::SourceFile; /// Generates diagnostics using annotate-snippet -pub struct AnnotateSnippetEmitterWriter { +pub struct AnnotateSnippetEmitter { source_map: Option<Lrc<SourceMap>>, fluent_bundle: Option<Lrc<FluentBundle>>, fallback_bundle: LazyFallbackBundle, @@ -33,7 +33,7 @@ pub struct AnnotateSnippetEmitterWriter { macro_backtrace: bool, } -impl Translate for AnnotateSnippetEmitterWriter { +impl Translate for AnnotateSnippetEmitter { fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { self.fluent_bundle.as_ref() } @@ -43,7 +43,7 @@ impl Translate for AnnotateSnippetEmitterWriter { } } -impl Emitter for AnnotateSnippetEmitterWriter { +impl Emitter for AnnotateSnippetEmitter { /// The entry point for the diagnostics generation fn emit_diagnostic(&mut self, diag: &Diagnostic) { let fluent_args = to_fluent_args(diag.args()); @@ -60,7 +60,7 @@ impl Emitter for AnnotateSnippetEmitterWriter { self.emit_messages_default( &diag.level, - &diag.message, + &diag.messages, &fluent_args, &diag.code, &primary_span, @@ -86,9 +86,7 @@ fn source_string(file: Lrc<SourceFile>, line: &Line) -> String { /// Maps `Diagnostic::Level` to `snippet::AnnotationType` fn annotation_type_for_level(level: Level) -> AnnotationType { match level { - Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error { .. } => { - AnnotationType::Error - } + Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error => AnnotationType::Error, Level::Warning(_) => AnnotationType::Warning, Level::Note | Level::OnceNote => AnnotationType::Note, Level::Help | Level::OnceHelp => AnnotationType::Help, @@ -99,7 +97,7 @@ fn annotation_type_for_level(level: Level) -> AnnotationType { } } -impl AnnotateSnippetEmitterWriter { +impl AnnotateSnippetEmitter { pub fn new( source_map: Option<Lrc<SourceMap>>, fluent_bundle: Option<Lrc<FluentBundle>>, @@ -138,7 +136,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 +201,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 470f318eb33..701c1c02ab0 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -3,7 +3,7 @@ use crate::{ CodeSuggestion, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Level, MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, }; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_error_messages::fluent_value_from_str_list_sep_by_and; use rustc_error_messages::FluentValue; use rustc_lint_defs::{Applicability, LintExpectationId}; @@ -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; } @@ -106,7 +103,7 @@ pub struct Diagnostic { // outside of what methods in this crate themselves allow. pub(crate) level: Level, - pub message: Vec<(DiagnosticMessage, Style)>, + pub messages: Vec<(DiagnosticMessage, Style)>, pub code: Option<DiagnosticId>, pub span: MultiSpan, pub children: Vec<SubDiagnostic>, @@ -164,9 +161,8 @@ pub enum DiagnosticId { #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] pub struct SubDiagnostic { pub level: Level, - pub message: Vec<(DiagnosticMessage, Style)>, + pub messages: Vec<(DiagnosticMessage, Style)>, pub span: MultiSpan, - pub render_span: Option<MultiSpan>, } #[derive(Debug, PartialEq, Eq)] @@ -216,17 +212,20 @@ impl StringPart { } } +// Note: most of these methods are setters that return `&mut Self`. The small +// number of simple getter functions all have `get_` prefixes to distinguish +// them from the setters. impl Diagnostic { #[track_caller] pub fn new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self { - Diagnostic::new_with_code(level, None, message) + Diagnostic::new_with_messages(level, vec![(message.into(), Style::NoStyle)]) } #[track_caller] pub fn new_with_messages(level: Level, messages: Vec<(DiagnosticMessage, Style)>) -> Self { Diagnostic { level, - message: messages, + messages, code: None, span: MultiSpan::new(), children: vec![], @@ -238,26 +237,6 @@ impl Diagnostic { } } - #[track_caller] - pub fn new_with_code<M: Into<DiagnosticMessage>>( - level: Level, - code: Option<DiagnosticId>, - message: M, - ) -> Self { - Diagnostic { - level, - message: vec![(message.into(), Style::NoStyle)], - code, - span: MultiSpan::new(), - children: vec![], - suggestions: Ok(vec![]), - args: Default::default(), - sort_span: DUMMY_SP, - is_lint: false, - emitted_at: DiagnosticLocation::caller(), - } - } - #[inline(always)] pub fn level(&self) -> Level { self.level @@ -265,11 +244,9 @@ impl Diagnostic { pub fn is_error(&self) -> bool { match self.level { - Level::Bug - | Level::DelayedBug - | Level::Fatal - | Level::Error { .. } - | Level::FailureNote => true, + Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error | Level::FailureNote => { + true + } Level::Warning(_) | Level::Note @@ -281,9 +258,9 @@ impl Diagnostic { } } - pub fn update_unstable_expectation_id( + pub(crate) fn update_unstable_expectation_id( &mut self, - unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>, + unstable_to_stable: &FxIndexMap<LintExpectationId, LintExpectationId>, ) { if let Level::Expect(expectation_id) | Level::Warning(Some(expectation_id)) = &mut self.level @@ -307,14 +284,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, @@ -332,25 +309,27 @@ impl Diagnostic { /// In the meantime, though, callsites are required to deal with the "bug" /// locally in whichever way makes the most sense. #[track_caller] - pub fn downgrade_to_delayed_bug(&mut self) -> &mut Self { + pub fn downgrade_to_delayed_bug(&mut self) { assert!( self.is_error(), "downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error", self.level ); self.level = Level::DelayedBug; - - self } - /// Adds a span/label to be included in the resulting snippet. + /// Appends a labeled span to the diagnostic. /// - /// This is pushed onto the [`MultiSpan`] that was created when the diagnostic - /// was first built. That means it will be shown together with the original - /// span/label, *not* a span added by one of the `span_{note,warn,help,suggestions}` methods. + /// Labels are used to convey additional context for the diagnostic's primary span. They will + /// be shown together with the original diagnostic's span, *not* with spans added by + /// `span_note`, `span_help`, etc. Therefore, if the primary span is not displayable (because + /// the span is `DUMMY_SP` or the source code isn't found), labels will not be displayed + /// either. /// - /// This span is *not* considered a ["primary span"][`MultiSpan`]; only - /// the `Span` supplied when creating the diagnostic is primary. + /// Implementation-wise, the label span is pushed onto the [`MultiSpan`] that was created when + /// the diagnostic was constructed. However, the label span is *not* considered a + /// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is + /// primary. #[rustc_lint_diagnostics] pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self { self.span.push_span_label(span, self.subdiagnostic_message_to_diagnostic_message(label)); @@ -368,7 +347,7 @@ impl Diagnostic { pub fn replace_span_with(&mut self, after: Span, keep_label: bool) -> &mut Self { let before = self.span.clone(); - self.set_span(after); + self.span(after); for span_label in before.span_labels() { if let Some(label) = span_label.label { if span_label.is_primary && keep_label { @@ -391,29 +370,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, @@ -471,22 +427,22 @@ impl Diagnostic { /// Add a note attached to this diagnostic. #[rustc_lint_diagnostics] pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { - self.sub(Level::Note, msg, MultiSpan::new(), None); + self.sub(Level::Note, msg, MultiSpan::new()); self } - pub fn highlighted_note<M: Into<SubdiagnosticMessage>>( + fn highlighted_note<M: Into<SubdiagnosticMessage>>( &mut self, msg: Vec<(M, Style)>, ) -> &mut Self { - self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None); + self.sub_with_highlights(Level::Note, msg, MultiSpan::new()); self } /// Prints the span with a note above it. /// This is like [`Diagnostic::note()`], but it gets its own span. pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { - self.sub(Level::OnceNote, msg, MultiSpan::new(), None); + self.sub(Level::OnceNote, msg, MultiSpan::new()); self } @@ -498,7 +454,7 @@ impl Diagnostic { sp: S, msg: impl Into<SubdiagnosticMessage>, ) -> &mut Self { - self.sub(Level::Note, msg, sp.into(), None); + self.sub(Level::Note, msg, sp.into()); self } @@ -509,14 +465,14 @@ impl Diagnostic { sp: S, msg: impl Into<SubdiagnosticMessage>, ) -> &mut Self { - self.sub(Level::OnceNote, msg, sp.into(), None); + self.sub(Level::OnceNote, msg, sp.into()); self } /// Add a warning attached to this diagnostic. #[rustc_lint_diagnostics] pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { - self.sub(Level::Warning(None), msg, MultiSpan::new(), None); + self.sub(Level::Warning(None), msg, MultiSpan::new()); self } @@ -528,27 +484,27 @@ impl Diagnostic { sp: S, msg: impl Into<SubdiagnosticMessage>, ) -> &mut Self { - self.sub(Level::Warning(None), msg, sp.into(), None); + self.sub(Level::Warning(None), msg, sp.into()); self } /// Add a help message attached to this diagnostic. #[rustc_lint_diagnostics] pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { - self.sub(Level::Help, msg, MultiSpan::new(), None); + self.sub(Level::Help, msg, MultiSpan::new()); self } /// Prints the span with a help above it. /// This is like [`Diagnostic::help()`], but it gets its own span. pub fn help_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self { - self.sub(Level::OnceHelp, msg, MultiSpan::new(), None); + self.sub(Level::OnceHelp, msg, MultiSpan::new()); self } /// Add a help message attached to this diagnostic with a customizable highlighted message. pub fn highlighted_help(&mut self, msg: Vec<(String, Style)>) -> &mut Self { - self.sub_with_highlights(Level::Help, msg, MultiSpan::new(), None); + self.sub_with_highlights(Level::Help, msg, MultiSpan::new()); self } @@ -560,7 +516,7 @@ impl Diagnostic { sp: S, msg: impl Into<SubdiagnosticMessage>, ) -> &mut Self { - self.sub(Level::Help, msg, sp.into(), None); + self.sub(Level::Help, msg, sp.into()); self } @@ -572,14 +528,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 +570,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 +726,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,18 +868,18 @@ 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 } - pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self { + pub fn span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self { self.span = sp.into(); if let Some(span) = self.span.primary_span() { self.sort_span = span; @@ -940,7 +887,7 @@ impl Diagnostic { self } - pub fn set_is_lint(&mut self) -> &mut Self { + pub fn is_lint(&mut self) -> &mut Self { self.is_lint = true; self } @@ -959,8 +906,8 @@ impl Diagnostic { self.code.clone() } - pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { - self.message[0] = (msg.into(), Style::NoStyle); + pub fn primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { + self.messages[0] = (msg.into(), Style::NoStyle); self } @@ -971,7 +918,7 @@ impl Diagnostic { self.args.iter() } - pub fn set_arg( + pub fn arg( &mut self, name: impl Into<Cow<'static, str>>, arg: impl IntoDiagnosticArg, @@ -987,19 +934,19 @@ impl Diagnostic { self.args = args; } - pub fn styled_message(&self) -> &[(DiagnosticMessage, Style)] { - &self.message + pub fn messages(&self) -> &[(DiagnosticMessage, Style)] { + &self.messages } /// 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 { let msg = - self.message.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages"); + self.messages.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages"); msg.with_subdiagnostic_message(attr.into()) } @@ -1007,21 +954,14 @@ impl Diagnostic { /// public methods above. /// /// Used by `proc_macro_server` for implementing `server::Diagnostic`. - pub fn sub( - &mut self, - level: Level, - message: impl Into<SubdiagnosticMessage>, - span: MultiSpan, - render_span: Option<MultiSpan>, - ) { + pub fn sub(&mut self, level: Level, message: impl Into<SubdiagnosticMessage>, span: MultiSpan) { let sub = SubDiagnostic { level, - message: vec![( + messages: vec![( self.subdiagnostic_message_to_diagnostic_message(message), Style::NoStyle, )], span, - render_span, }; self.children.push(sub); } @@ -1031,15 +971,14 @@ impl Diagnostic { fn sub_with_highlights<M: Into<SubdiagnosticMessage>>( &mut self, level: Level, - message: Vec<(M, Style)>, + messages: Vec<(M, Style)>, span: MultiSpan, - render_span: Option<MultiSpan>, ) { - let message = message + let messages = messages .into_iter() .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1)) .collect(); - let sub = SubDiagnostic { level, message, span, render_span }; + let sub = SubDiagnostic { level, messages, span }; self.children.push(sub); } @@ -1057,7 +996,7 @@ impl Diagnostic { ) { ( &self.level, - &self.message, + &self.messages, self.args().collect(), &self.code, &self.span, diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 85acf8ab5aa..3789cdaf354 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; @@ -15,455 +15,184 @@ use std::ops::{Deref, DerefMut}; use std::panic; use std::thread::panicking; -/// Trait implemented by error types. This should not be implemented manually. Instead, use +/// Trait implemented by error types. This is rarely 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, level: Level) -> 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); - diag.set_span(self.span); - diag + fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> { + self.node.into_diagnostic(dcx, level).span_mv(self.span) } } /// Used for emitting structured error messages and other diagnostic information. +/// Each constructed `DiagnosticBuilder` must be consumed by a function such as +/// `emit`, `cancel`, `delay_as_bug`, or `into_diagnostic`. A panic occurrs if a +/// `DiagnosticBuilder` is dropped without being consumed by one of these +/// functions. /// /// 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> { - inner: DiagnosticBuilderInner<'a>, - _marker: PhantomData<G>, -} +pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> { + pub dcx: &'a DiagCtxt, -/// This type exists only for `DiagnosticBuilder::forget_guarantee`, because it: -/// 1. lacks the `G` parameter and therefore `DiagnosticBuilder<G1>` can be -/// converted into `DiagnosticBuilder<G2>` while reusing the `inner` field -/// 2. can implement the `Drop` "bomb" instead of `DiagnosticBuilder`, as it -/// contains all of the data (`state` + `diagnostic`) of `DiagnosticBuilder` -/// -/// The `diagnostic` field is not `Copy` and can't be moved out of whichever -/// type implements the `Drop` "bomb", but because of the above two facts, that -/// never needs to happen - instead, the whole `inner: DiagnosticBuilderInner` -/// can be moved out of a `DiagnosticBuilder` and into another. -#[must_use] -#[derive(Clone)] -struct DiagnosticBuilderInner<'a> { - state: DiagnosticBuilderState<'a>, - - /// `Diagnostic` is a large type, and `DiagnosticBuilder` is often used as a - /// return value, especially within the frequently-used `PResult` type. - /// In theory, return value optimization (RVO) should avoid unnecessary - /// copying. In practice, it does not (at the time of writing). - diagnostic: Box<Diagnostic>, -} - -#[derive(Clone)] -enum DiagnosticBuilderState<'a> { - /// Initial state of a `DiagnosticBuilder`, before `.emit()` or `.cancel()`. + /// Why the `Option`? It is always `Some` until the `DiagnosticBuilder` is + /// consumed via `emit`, `cancel`, etc. At that point it is consumed and + /// replaced with `None`. Then `drop` checks that it is `None`; if not, it + /// panics because a diagnostic was built but not used. /// - /// The `Diagnostic` will be emitted through this `Handler`. - Emittable(&'a Handler), + /// Why the Box? `Diagnostic` is a large type, and `DiagnosticBuilder` is + /// often used as a return value, especially within the frequently-used + /// `PResult` type. In theory, return value optimization (RVO) should avoid + /// unnecessary copying. In practice, it does not (at the time of writing). + diag: Option<Box<Diagnostic>>, - /// State of a `DiagnosticBuilder`, after `.emit()` or *during* `.cancel()`. - /// - /// The `Diagnostic` will be ignored when calling `.emit()`, and it can be - /// assumed that `.emit()` was previously called, to end up in this state. - /// - /// While this is also used by `.cancel()`, this state is only observed by - /// the `Drop` `impl` of `DiagnosticBuilderInner`, as `.cancel()` takes - /// `self` by-value specifically to prevent any attempts to `.emit()`. - /// - // FIXME(eddyb) currently this doesn't prevent extending the `Diagnostic`, - // despite that being potentially lossy, if important information is added - // *after* the original `.emit()` call. - AlreadyEmittedOrDuringCancellation, + _marker: PhantomData<G>, } -// `DiagnosticBuilderState` should be pointer-sized. +// Cloning a `DiagnosticBuilder` is a recipe for a diagnostic being emitted +// twice, which would be bad. +impl<G> !Clone for DiagnosticBuilder<'_, G> {} + rustc_data_structures::static_assert_size!( - DiagnosticBuilderState<'_>, - std::mem::size_of::<&Handler>() + DiagnosticBuilder<'_, ()>, + 2 * std::mem::size_of::<usize>() ); /// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee" /// (or "proof") token that the emission happened. pub trait EmissionGuarantee: Sized { + /// This exists so that bugs and fatal errors can both result in `!` (an + /// abort) when emitted, but have different aborting behaviour. + type EmitResult = Self; + /// Implementation of `DiagnosticBuilder::emit`, fully controlled by each /// `impl` of `EmissionGuarantee`, to make it impossible to create a value - /// of `Self` without actually performing the emission. - #[track_caller] - fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self; - - /// Creates a new `DiagnosticBuilder` that will return this type of guarantee. - #[track_caller] - fn make_diagnostic_builder( - handler: &Handler, - 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`]. + /// of `Self::EmitResult` without actually performing the emission. #[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. - pub fn forget_guarantee(self) -> DiagnosticBuilder<'a, ()> { - DiagnosticBuilder { inner: self.inner, _marker: PhantomData } - } -} - -// FIXME(eddyb) make `ErrorGuaranteed` impossible to create outside `.emit()`. -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) => { - db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; - - let guar = handler.emit_diagnostic(&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` - // can be overwritten with a new one, thanks to `DerefMut`. - assert!( - db.inner.diagnostic.is_error(), - "emitted non-error ({:?}) diagnostic \ - from `DiagnosticBuilder<ErrorGuaranteed>`", - db.inner.diagnostic.level, - ); - guar.unwrap() - } - // `.emit()` was previously called, disallowed from repeating it, - // but can take advantage of the previous `.emit()`'s guarantee - // still being applicable (i.e. as a form of idempotency). - DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => { - // Only allow a guarantee if the `level` wasn't switched to a - // non-error - the field isn't `pub`, but the whole `Diagnostic` - // can be overwritten with a new one, thanks to `DerefMut`. - assert!( - db.inner.diagnostic.is_error(), - "`DiagnosticBuilder<ErrorGuaranteed>`'s diagnostic \ - became non-error ({:?}), after original `.emit()`", - db.inner.diagnostic.level, - ); - #[allow(deprecated)] - ErrorGuaranteed::unchecked_claim_error_was_emitted() - } - } - } - - #[track_caller] - fn make_diagnostic_builder( - handler: &Handler, - msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_, Self> { - DiagnosticBuilder::new_guaranteeing_error(handler, msg) - } + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult; } -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) +impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { + /// Takes the diagnostic. For use by methods that consume the + /// DiagnosticBuilder: `emit`, `cancel`, etc. Afterwards, `drop` is the + /// only code that will be run on `self`. + fn take_diag(&mut self) -> Diagnostic { + Box::into_inner(self.diag.take().unwrap()) } - /// 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, - } + /// Most `emit_producing_guarantee` functions use this as a starting point. + fn emit_producing_nothing(mut self) { + let diag = self.take_diag(); + self.dcx.emit_diagnostic(diag); } -} -// FIXME(eddyb) should there be a `Option<ErrorGuaranteed>` impl as well? -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) => { - db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; + /// `ErrorGuaranteed::emit_producing_guarantee` uses this. + // FIXME(eddyb) make `ErrorGuaranteed` impossible to create outside `.emit()`. + fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed { + let diag = self.take_diag(); - handler.emit_diagnostic(&mut db.inner.diagnostic); - } - // `.emit()` was previously called, disallowed from repeating it. - DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} - } - } + // Only allow a guarantee if the `level` wasn't switched to a + // non-error. The field isn't `pub`, but the whole `Diagnostic` can be + // overwritten with a new one, thanks to `DerefMut`. + assert!( + diag.is_error(), + "emitted non-error ({:?}) diagnostic from `DiagnosticBuilder<ErrorGuaranteed>`", + diag.level, + ); - fn make_diagnostic_builder( - handler: &Handler, - msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_, Self> { - DiagnosticBuilder::new(handler, Level::Warning(None), msg) + let guar = self.dcx.emit_diagnostic(diag); + guar.unwrap() } } -/// Marker type which enables implementation of `create_note` and `emit_note` functions for -/// note-without-error struct diagnostics. -#[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 ErrorGuaranteed { + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_error_guaranteed() } } -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) => { - db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; - handler.emit_diagnostic(&mut db.inner.diagnostic); - } - // `.emit()` was previously called, disallowed from repeating it. - DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} - } - - Noted - } - - fn make_diagnostic_builder( - handler: &Handler, - msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_, Self> { - DiagnosticBuilder::new_note(handler, msg) +impl EmissionGuarantee for () { + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_nothing(); } } /// Marker type which enables implementation of `create_bug` and `emit_bug` functions for -/// bug struct diagnostics. +/// bug diagnostics. #[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) - } +pub struct BugAbort; - /// 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) => { - db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; +impl EmissionGuarantee for BugAbort { + type EmitResult = !; - handler.emit_diagnostic(&mut db.inner.diagnostic); - } - // `.emit()` was previously called, disallowed from repeating it. - DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} - } - // Then panic. No need to return the marker type. + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_nothing(); panic::panic_any(ExplicitBug); } - - fn make_diagnostic_builder( - handler: &Handler, - 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, - } - } -} +/// Marker type which enables implementation of `create_fatal` and `emit_fatal` functions for +/// fatal diagnostics. +#[derive(Copy, Clone)] +pub struct FatalAbort; -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) => { - db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; +impl EmissionGuarantee for FatalAbort { + type EmitResult = !; - handler.emit_diagnostic(&mut db.inner.diagnostic); - } - // `.emit()` was previously called, disallowed from repeating it. - DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} - } - // Then fatally error, returning `!` + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_nothing(); crate::FatalError.raise() } - - fn make_diagnostic_builder( - handler: &Handler, - 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, - } - } } 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) => { - db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; - - handler.emit_diagnostic(&mut db.inner.diagnostic); - } - // `.emit()` was previously called, disallowed from repeating it. - DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} - } - // Then fatally error.. + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_nothing(); rustc_span::fatal_error::FatalError } - - fn make_diagnostic_builder( - handler: &Handler, - msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_, Self> { - DiagnosticBuilder::new_almost_fatal(handler, msg) - } } -/// In general, the `DiagnosticBuilder` uses deref to allow access to -/// the fields and methods of the embedded `diagnostic` in a -/// transparent way. *However,* many of the methods are intended to -/// be used in a chained way, and hence ought to return `self`. In -/// that case, we can't just naively forward to the method on the -/// `diagnostic`, because the return type would be a `&Diagnostic` -/// instead of a `&DiagnosticBuilder<'a>`. This `forward!` macro makes -/// it easy to declare such methods on the builder. +/// `DiagnosticBuilder` impls `DerefMut`, which allows access to the fields and +/// methods of the embedded `Diagnostic`. However, that doesn't allow method +/// chaining at the `DiagnosticBuilder` level. Each use of this macro defines +/// two builder methods at that level, both of which wrap the equivalent method +/// in `Diagnostic`. +/// - A `&mut self -> &mut Self` method, with the same name as the underlying +/// `Diagnostic` method. It is mostly to modify existing diagnostics, either +/// in a standalone fashion, e.g. `err.code(code)`, or in a chained fashion +/// to make multiple modifications, e.g. `err.code(code).span(span)`. +/// - A `self -> Self` method, with `_mv` suffix added (short for "move"). +/// It is mostly used in a chained fashion when producing a new diagnostic, +/// e.g. `let err = struct_err(msg).code_mv(code)`, or when emitting a new +/// diagnostic , e.g. `struct_err(msg).code_mv(code).emit()`. +/// +/// Although the latter method can be used to modify an existing diagnostic, +/// e.g. `err = err.code_mv(code)`, this should be avoided because the former +/// method give shorter code, e.g. `err.code(code)`. macro_rules! forward { - // Forward pattern for &mut self -> &mut Self ( - $(#[$attrs:meta])* - pub fn $n:ident(&mut self, $($name:ident: $ty:ty),* $(,)?) -> &mut Self + ($n:ident, $n_mv:ident)($($name:ident: $ty:ty),* $(,)?) ) => { - $(#[$attrs])* #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] pub fn $n(&mut self, $($name: $ty),*) -> &mut Self { - self.inner.diagnostic.$n($($name),*); + self.diag.as_mut().unwrap().$n($($name),*); + self + } + #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] + pub fn $n_mv(mut self, $($name: $ty),*) -> Self { + self.diag.as_mut().unwrap().$n($($name),*); self } }; @@ -473,21 +202,35 @@ impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> { type Target = Diagnostic; fn deref(&self) -> &Diagnostic { - &self.inner.diagnostic + self.diag.as_ref().unwrap() } } impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> { fn deref_mut(&mut self) -> &mut Diagnostic { - &mut self.inner.diagnostic + self.diag.as_mut().unwrap() } } impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { - /// Emit the diagnostic. + #[rustc_lint_diagnostics] + #[track_caller] + pub fn new<M: Into<DiagnosticMessage>>(dcx: &'a DiagCtxt, level: Level, message: M) -> Self { + Self::new_diagnostic(dcx, Diagnostic::new(level, message)) + } + + /// Creates a new `DiagnosticBuilder` with an already constructed + /// diagnostic. + #[track_caller] + pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diag: Diagnostic) -> Self { + debug!("Created new diagnostic"); + Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData } + } + + /// Emit and consume the diagnostic. #[track_caller] - pub fn emit(&mut self) -> G { - G::diagnostic_builder_emit_producing_guarantee(self) + pub fn emit(self) -> G::EmitResult { + G::emit_producing_guarantee(self) } /// Emit the diagnostic unless `delay` is true, @@ -495,78 +238,50 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { /// /// See `emit` and `delay_as_bug` for details. #[track_caller] - pub fn emit_unless(&mut self, delay: bool) -> G { + pub fn emit_unless(mut self, delay: bool) -> G::EmitResult { if delay { self.downgrade_to_delayed_bug(); } self.emit() } - /// Cancel the diagnostic (a structured diagnostic must either be emitted or + /// Cancel and consume the diagnostic. (A diagnostic must either be emitted or /// cancelled or it will panic when dropped). - /// - /// This method takes `self` by-value to disallow calling `.emit()` on it, - /// which may be expected to *guarantee* the emission of an error, either - /// at the time of the call, or through a prior `.emit()` call. pub fn cancel(mut self) { - self.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; + self.diag = None; drop(self); } /// 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. + /// As with `buffer`, this is unless the dcx has disabled such buffering. pub fn stash(self, span: Span, key: StashKey) { - if let Some((diag, handler)) = self.into_diagnostic() { - handler.stash_diagnostic(span, key, diag); + if let Some((diag, dcx)) = self.into_diagnostic() { + dcx.stash_diagnostic(span, key, diag); } } /// 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, - // `.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() - { + /// unless dcx has disabled such buffering. + pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a DiagCtxt)> { + if self.dcx.inner.lock().flags.treat_err_as_bug.is_some() { self.emit(); return None; } - // Take the `Diagnostic` by replacing it with a dummy. - let dummy = Diagnostic::new(Level::Allow, DiagnosticMessage::from("")); - let diagnostic = std::mem::replace(&mut *self.inner.diagnostic, dummy); - - // Disable the ICE on `Drop`. - self.cancel(); + let diag = self.take_diag(); // Logging here is useful to help track down where in logs an error was // actually emitted. - debug!("buffer: diagnostic={:?}", diagnostic); - - Some((diagnostic, handler)) - } + debug!("buffer: diag={:?}", diag); - /// Retrieves the [`Handler`] if available - pub fn handler(&self) -> Option<&Handler> { - match self.inner.state { - DiagnosticBuilderState::Emittable(handler) => Some(handler), - DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => None, - } + Some((diag, self.dcx)) } /// Buffers the diagnostic for later emission, - /// unless handler has disabled such buffering. + /// unless dcx has disabled such buffering. pub fn buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>) { buffered_diagnostics.extend(self.into_diagnostic().map(|(diag, _)| diag)); } @@ -582,214 +297,169 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { /// In the meantime, though, callsites are required to deal with the "bug" /// locally in whichever way makes the most sense. #[track_caller] - pub fn delay_as_bug(&mut self) -> G { + pub fn delay_as_bug(mut self) -> G::EmitResult { self.downgrade_to_delayed_bug(); self.emit() } - forward!( - #[track_caller] - pub fn downgrade_to_delayed_bug(&mut self,) -> &mut Self - ); - - forward!( - /// Appends a labeled span to the diagnostic. - /// - /// Labels are used to convey additional context for the diagnostic's primary span. They will - /// be shown together with the original diagnostic's span, *not* with spans added by - /// `span_note`, `span_help`, etc. Therefore, if the primary span is not displayable (because - /// the span is `DUMMY_SP` or the source code isn't found), labels will not be displayed - /// either. - /// - /// Implementation-wise, the label span is pushed onto the [`MultiSpan`] that was created when - /// the diagnostic was constructed. However, the label span is *not* considered a - /// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is - /// primary. - pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self); - - forward!( - /// Labels all the given spans with the provided label. - /// See [`Diagnostic::span_label()`] for more information. - pub fn span_labels( - &mut self, + forward!((span_label, span_label_mv)( + span: Span, + label: impl Into<SubdiagnosticMessage>, + )); + forward!((span_labels, span_labels_mv)( spans: impl IntoIterator<Item = Span>, label: &str, - ) -> &mut Self); - - forward!(pub fn note_expected_found( - &mut self, + )); + forward!((note_expected_found, note_expected_found_mv)( expected_label: &dyn fmt::Display, expected: DiagnosticStyledString, found_label: &dyn fmt::Display, found: DiagnosticStyledString, - ) -> &mut Self); - - forward!(pub fn note_expected_found_extra( - &mut self, + )); + forward!((note_expected_found_extra, note_expected_found_extra_mv)( expected_label: &dyn fmt::Display, expected: DiagnosticStyledString, found_label: &dyn fmt::Display, found: DiagnosticStyledString, expected_extra: &dyn fmt::Display, 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( - &mut self, + )); + forward!((note, note_mv)( + msg: impl Into<SubdiagnosticMessage>, + )); + forward!((note_once, note_once_mv)( + msg: impl Into<SubdiagnosticMessage>, + )); + forward!((span_note, span_note_mv)( sp: impl Into<MultiSpan>, msg: impl Into<SubdiagnosticMessage>, - ) -> &mut Self); - forward!(pub fn span_note_once( - &mut self, + )); + forward!((span_note_once, span_note_once_mv)( sp: impl Into<MultiSpan>, msg: impl Into<SubdiagnosticMessage>, - ) -> &mut Self); - forward!(pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self); - forward!(pub fn span_warn( - &mut self, + )); + forward!((warn, warn_mv)( + msg: impl Into<SubdiagnosticMessage>, + )); + forward!((span_warn, span_warn_mv)( sp: impl Into<MultiSpan>, msg: impl Into<SubdiagnosticMessage>, - ) -> &mut Self); - forward!(pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self); - forward!(pub fn help_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self); - forward!(pub fn span_help( - &mut self, + )); + forward!((help, help_mv)( + msg: impl Into<SubdiagnosticMessage>, + )); + forward!((help_once, help_once_mv)( + msg: impl Into<SubdiagnosticMessage>, + )); + forward!((span_help, span_help_once_mv)( sp: impl Into<MultiSpan>, msg: impl Into<SubdiagnosticMessage>, - ) -> &mut Self); - 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, + )); + forward!((multipart_suggestion, multipart_suggestion_mv)( msg: impl Into<SubdiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn multipart_suggestion_verbose( - &mut self, + )); + forward!((multipart_suggestion_verbose, multipart_suggestion_verbose_mv)( msg: impl Into<SubdiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn tool_only_multipart_suggestion( - &mut self, + )); + forward!((tool_only_multipart_suggestion, tool_only_multipart_suggestion_mv)( msg: impl Into<SubdiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn span_suggestion( - &mut self, + )); + forward!((span_suggestion, span_suggestion_mv)( sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn span_suggestions( - &mut self, + )); + forward!((span_suggestions, span_suggestions_mv)( sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestions: impl IntoIterator<Item = String>, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn multipart_suggestions( - &mut self, + )); + forward!((multipart_suggestions, multipart_suggestions_mv)( msg: impl Into<SubdiagnosticMessage>, suggestions: impl IntoIterator<Item = Vec<(Span, String)>>, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn span_suggestion_short( - &mut self, + )); + forward!((span_suggestion_short, span_suggestion_short_mv)( sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn span_suggestion_verbose( - &mut self, + )); + forward!((span_suggestion_verbose, span_suggestion_verbose_mv)( sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn span_suggestion_hidden( - &mut self, + )); + forward!((span_suggestion_hidden, span_suggestion_hidden_mv)( sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, - ) -> &mut Self); - forward!(pub fn tool_only_span_suggestion( - &mut self, + )); + forward!((tool_only_span_suggestion, tool_only_span_suggestion_mv)( sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, - ) -> &mut Self); - - forward!(pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self); - forward!(pub fn set_span(&mut self, sp: impl Into<MultiSpan>) -> &mut Self); - forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self); - forward!(pub fn set_arg( - &mut self, - name: impl Into<Cow<'static, str>>, - arg: impl IntoDiagnosticArg, - ) -> &mut Self); - - forward!(pub fn subdiagnostic( - &mut self, - subdiagnostic: impl crate::AddToDiagnostic - ) -> &mut Self); + )); + forward!((primary_message, primary_message_mv)( + msg: impl Into<DiagnosticMessage>, + )); + forward!((span, span_mv)( + sp: impl Into<MultiSpan>, + )); + forward!((code, code_mv)( + s: DiagnosticId, + )); + forward!((arg, arg_mv)( + name: impl Into<Cow<'static, str>>, arg: impl IntoDiagnosticArg, + )); + forward!((subdiagnostic, subdiagnostic_mv)( + subdiagnostic: impl crate::AddToDiagnostic, + )); } impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.inner.diagnostic.fmt(f) + self.diag.fmt(f) } } -/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or cancelled -/// or we emit a bug. -impl Drop for DiagnosticBuilderInner<'_> { +/// Destructor bomb: every `DiagnosticBuilder` must be consumed (emitted, +/// cancelled, etc.) or we emit a bug. +impl<G: EmissionGuarantee> Drop for DiagnosticBuilder<'_, G> { fn drop(&mut self) { - match self.state { - // No `.emit()` or `.cancel()` calls. - DiagnosticBuilderState::Emittable(handler) => { - if !panicking() { - handler.emit_diagnostic(&mut Diagnostic::new( - Level::Bug, - DiagnosticMessage::from( - "the following error was constructed but not emitted", - ), - )); - handler.emit_diagnostic(&mut self.diagnostic); - panic!("error was constructed but not emitted"); - } + match self.diag.take() { + Some(diag) if !panicking() => { + self.dcx.emit_diagnostic(Diagnostic::new( + Level::Bug, + DiagnosticMessage::from("the following error was constructed but not emitted"), + )); + self.dcx.emit_diagnostic(*diag); + panic!("error was constructed but not emitted"); } - // `.emit()` was previously called, or maybe we're during `.cancel()`. - DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} + _ => {} } } } #[macro_export] macro_rules! struct_span_err { - ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({ - $session.struct_span_err_with_code( + ($dcx:expr, $span:expr, $code:ident, $($message:tt)*) => ({ + $dcx.struct_span_err( $span, format!($($message)*), - $crate::error_code!($code), ) + .code_mv($crate::error_code!($code)) }) } diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 4f77f09b26e..58d4d2caf2e 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -1,10 +1,12 @@ use crate::diagnostic::DiagnosticLocation; use crate::{fluent_generated as fluent, AddToDiagnostic}; -use crate::{DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg}; +use crate::{ + DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic, + IntoDiagnosticArg, Level, +}; use rustc_ast as ast; use rustc_ast_pretty::pprust; use rustc_hir as hir; -use rustc_lint_defs::Level; use rustc_span::edition::Edition; use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol}; use rustc_span::Span; @@ -216,7 +218,7 @@ impl IntoDiagnosticArg for ast::Visibility { } } -impl IntoDiagnosticArg for Level { +impl IntoDiagnosticArg for rustc_lint_defs::Level { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { DiagnosticArgValue::Str(Cow::Borrowed(self.to_cmd_flag())) } @@ -245,53 +247,45 @@ impl<Id> IntoDiagnosticArg for hir::def::Res<Id> { } } -impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> { - fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> { - let mut diag; +impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for TargetDataLayoutErrors<'_> { + fn into_diagnostic(self, dcx: &DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> { match self { TargetDataLayoutErrors::InvalidAddressSpace { addr_space, err, cause } => { - diag = handler.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 + DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_address_space) + .arg_mv("addr_space", addr_space) + .arg_mv("cause", cause) + .arg_mv("err", err) } TargetDataLayoutErrors::InvalidBits { kind, bit, cause, err } => { - diag = handler.struct_fatal(fluent::errors_target_invalid_bits); - diag.set_arg("kind", kind); - diag.set_arg("bit", bit); - diag.set_arg("cause", cause); - diag.set_arg("err", err); - diag + DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_bits) + .arg_mv("kind", kind) + .arg_mv("bit", bit) + .arg_mv("cause", cause) + .arg_mv("err", err) } TargetDataLayoutErrors::MissingAlignment { cause } => { - diag = handler.struct_fatal(fluent::errors_target_missing_alignment); - diag.set_arg("cause", cause); - diag + DiagnosticBuilder::new(dcx, level, fluent::errors_target_missing_alignment) + .arg_mv("cause", cause) } TargetDataLayoutErrors::InvalidAlignment { cause, err } => { - diag = handler.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 + DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_alignment) + .arg_mv("cause", cause) + .arg_mv("err_kind", err.diag_ident()) + .arg_mv("align", err.align()) } TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => { - diag = handler.struct_fatal(fluent::errors_target_inconsistent_architecture); - diag.set_arg("dl", dl); - diag.set_arg("target", target); - diag + DiagnosticBuilder::new(dcx, level, fluent::errors_target_inconsistent_architecture) + .arg_mv("dl", dl) + .arg_mv("target", target) } TargetDataLayoutErrors::InconsistentTargetPointerWidth { pointer_size, target } => { - diag = handler.struct_fatal(fluent::errors_target_inconsistent_pointer_width); - diag.set_arg("pointer_size", pointer_size); - diag.set_arg("target", target); - diag + DiagnosticBuilder::new(dcx, level, fluent::errors_target_inconsistent_pointer_width) + .arg_mv("pointer_size", pointer_size) + .arg_mv("target", target) } TargetDataLayoutErrors::InvalidBitsSize { err } => { - diag = handler.struct_fatal(fluent::errors_target_invalid_bits_size); - diag.set_arg("err", err); - diag + DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_bits_size) + .arg_mv("err", err) } } } @@ -301,25 +295,13 @@ impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> { pub struct SingleLabelManySpans { pub spans: Vec<Span>, pub label: &'static str, - pub kind: LabelKind, } impl AddToDiagnostic for SingleLabelManySpans { fn add_to_diagnostic_with<F>(self, diag: &mut crate::Diagnostic, _: F) { - match self.kind { - LabelKind::Note => diag.span_note(self.spans, self.label), - LabelKind::Label => diag.span_labels(self.spans, self.label), - LabelKind::Help => diag.span_help(self.spans, self.label), - }; + diag.span_labels(self.spans, self.label); } } -/// The kind of label to attach when using [`SingleLabelManySpans`] -pub enum LabelKind { - Note, - Label, - Help, -} - #[derive(Subdiagnostic)] #[label(errors_expected_lifetime_parameter)] pub struct ExpectedLifetimeParameter { @@ -362,9 +344,9 @@ impl IntoDiagnosticArg for Backtrace { pub struct InvalidFlushedDelayedDiagnosticLevel { #[primary_span] pub span: Span, - pub level: rustc_errors::Level, + pub level: Level, } -impl IntoDiagnosticArg for rustc_errors::Level { +impl IntoDiagnosticArg for Level { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { DiagnosticArgValue::Str(Cow::from(self.to_string())) } @@ -378,3 +360,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 922846775f6..987832e6937 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -16,14 +16,14 @@ 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; use derive_setters::Setters; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::sync::{DynSend, IntoDynSyncSend, Lrc}; use rustc_error_messages::{FluentArgs, SpanLabel}; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -61,13 +61,13 @@ impl HumanReadableErrorType { self, mut dst: Box<dyn WriteColor + Send>, fallback_bundle: LazyFallbackBundle, - ) -> EmitterWriter { + ) -> HumanEmitter { let (short, color_config) = self.unzip(); let color = color_config.suggests_using_colors(); if !dst.supports_color() && color { dst = Box::new(Ansi::new(dst)); } - EmitterWriter::new(dst, fallback_bundle).short_message(short) + HumanEmitter::new(dst, fallback_bundle).short_message(short) } } @@ -196,13 +196,15 @@ pub trait Emitter: Translate { fn emit_diagnostic(&mut self, diag: &Diagnostic); /// Emit a notification that an artifact has been output. - /// This is currently only supported for the JSON format, - /// other formats can, and will, simply ignore it. + /// Currently only supported for the JSON format. fn emit_artifact_notification(&mut self, _path: &Path, _artifact_type: &str) {} + /// Emit a report about future breakage. + /// Currently only supported for the JSON format. fn emit_future_breakage_report(&mut self, _diags: Vec<Diagnostic>) {} - /// Emit list of unused externs + /// Emit list of unused externs. + /// Currently only supported for the JSON format. fn emit_unused_externs( &mut self, _lint_level: rustc_lint_defs::Level, @@ -350,9 +352,8 @@ pub trait Emitter: Translate { children.push(SubDiagnostic { level: Level::Note, - message: vec![(DiagnosticMessage::from(msg), Style::NoStyle)], + messages: vec![(DiagnosticMessage::from(msg), Style::NoStyle)], span: MultiSpan::new(), - render_span: None, }); } } @@ -370,7 +371,7 @@ pub trait Emitter: Translate { } fn render_multispan_macro_backtrace(&self, span: &mut MultiSpan, always_backtrace: bool) { - let mut new_labels: Vec<(Span, String)> = vec![]; + let mut new_labels = FxIndexSet::default(); for &sp in span.primary_spans() { if sp.is_dummy() { @@ -387,7 +388,7 @@ pub trait Emitter: Translate { } if always_backtrace { - new_labels.push(( + new_labels.insert(( trace.def_site, format!( "in this expansion of `{}`{}", @@ -431,7 +432,7 @@ pub trait Emitter: Translate { format!("this {} desugaring", kind.descr()).into() } }; - new_labels.push(( + new_labels.insert(( trace.call_site, format!( "in {}{}", @@ -502,7 +503,7 @@ pub trait Emitter: Translate { } } -impl Translate for EmitterWriter { +impl Translate for HumanEmitter { fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { self.fluent_bundle.as_ref() } @@ -512,7 +513,7 @@ impl Translate for EmitterWriter { } } -impl Emitter for EmitterWriter { +impl Emitter for HumanEmitter { fn source_map(&self) -> Option<&Lrc<SourceMap>> { self.sm.as_ref() } @@ -533,7 +534,7 @@ impl Emitter for EmitterWriter { self.emit_messages_default( &diag.level, - &diag.message, + &diag.messages, &fluent_args, &diag.code, &primary_span, @@ -553,10 +554,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 +582,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); } } } @@ -623,7 +624,7 @@ impl ColorConfig { /// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short` #[derive(Setters)] -pub struct EmitterWriter { +pub struct HumanEmitter { #[setters(skip)] dst: IntoDynSyncSend<Destination>, sm: Option<Lrc<SourceMap>>, @@ -648,14 +649,14 @@ pub struct FileWithAnnotatedLines { multiline_depth: usize, } -impl EmitterWriter { - pub fn stderr(color_config: ColorConfig, fallback_bundle: LazyFallbackBundle) -> EmitterWriter { +impl HumanEmitter { + pub fn stderr(color_config: ColorConfig, fallback_bundle: LazyFallbackBundle) -> HumanEmitter { let dst = from_stderr(color_config); Self::create(dst, fallback_bundle) } - fn create(dst: Destination, fallback_bundle: LazyFallbackBundle) -> EmitterWriter { - EmitterWriter { + fn create(dst: Destination, fallback_bundle: LazyFallbackBundle) -> HumanEmitter { + HumanEmitter { dst: IntoDynSyncSend(dst), sm: None, fluent_bundle: None, @@ -674,7 +675,7 @@ impl EmitterWriter { pub fn new( dst: Box<dyn WriteColor + Send>, fallback_bundle: LazyFallbackBundle, - ) -> EmitterWriter { + ) -> HumanEmitter { Self::create(dst, fallback_bundle) } @@ -1228,10 +1229,10 @@ impl EmitterWriter { /// Adds a left margin to every line but the first, given a padding length and the label being /// displayed, keeping the provided highlighting. - fn msg_to_buffer( + fn msgs_to_buffer( &self, buffer: &mut StyledBuffer, - msg: &[(DiagnosticMessage, Style)], + msgs: &[(DiagnosticMessage, Style)], args: &FluentArgs<'_>, padding: usize, label: &str, @@ -1267,7 +1268,7 @@ impl EmitterWriter { // Provided the following diagnostic message: // - // let msg = vec![ + // let msgs = vec![ // (" // ("highlighted multiline\nstring to\nsee how it ", Style::NoStyle), // ("looks", Style::Highlight), @@ -1284,7 +1285,7 @@ impl EmitterWriter { // see how it *looks* with // very *weird* formats // see? - for (text, style) in msg.iter() { + for (text, style) in msgs.iter() { let text = self.translate_message(text, args).map_err(Report::new).unwrap(); let text = &normalize_whitespace(&text); let lines = text.split('\n').collect::<Vec<_>>(); @@ -1297,16 +1298,16 @@ 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)); } } } #[instrument(level = "trace", skip(self, args), ret)] - fn emit_message_default( + fn emit_messages_default_inner( &mut self, msp: &MultiSpan, - msg: &[(DiagnosticMessage, Style)], + msgs: &[(DiagnosticMessage, Style)], args: &FluentArgs<'_>, code: &Option<DiagnosticId>, level: &Level, @@ -1327,7 +1328,7 @@ impl EmitterWriter { buffer.append(0, level.to_str(), Style::MainHeaderMsg); buffer.append(0, ": ", Style::NoStyle); } - self.msg_to_buffer(&mut buffer, msg, args, max_line_num_len, "note", None); + self.msgs_to_buffer(&mut buffer, msgs, args, max_line_num_len, "note", None); } else { let mut label_width = 0; // The failure note level itself does not provide any useful diagnostic information @@ -1348,12 +1349,19 @@ impl EmitterWriter { buffer.append(0, "]", Style::Level(*level)); label_width += 2 + code.len(); } - let header_style = if is_secondary { Style::HeaderMsg } else { Style::MainHeaderMsg }; + let header_style = if is_secondary { + Style::HeaderMsg + } else if self.short_message { + // For short messages avoid bolding the message, as it doesn't look great (#63835). + Style::NoStyle + } else { + Style::MainHeaderMsg + }; if *level != Level::FailureNote { buffer.append(0, ": ", header_style); label_width += 2; } - for (text, _) in msg.iter() { + for (text, _) in msgs.iter() { let text = self.translate_message(text, args).map_err(Report::new).unwrap(); // Account for newlines to align output to its label. for (line, text) in normalize_whitespace(&text).lines().enumerate() { @@ -1740,7 +1748,7 @@ impl EmitterWriter { buffer.append(0, level.to_str(), Style::Level(*level)); buffer.append(0, ": ", Style::HeaderMsg); - self.msg_to_buffer( + self.msgs_to_buffer( &mut buffer, &[(suggestion.msg.to_owned(), Style::NoStyle)], args, @@ -1924,7 +1932,7 @@ impl EmitterWriter { self.draw_code_line( &mut buffer, &mut row_num, - &highlight_parts, + highlight_parts, line_pos + line_start, line, show_code_change, @@ -2067,7 +2075,7 @@ impl EmitterWriter { fn emit_messages_default( &mut self, level: &Level, - message: &[(DiagnosticMessage, Style)], + messages: &[(DiagnosticMessage, Style)], args: &FluentArgs<'_>, code: &Option<DiagnosticId>, span: &MultiSpan, @@ -2082,9 +2090,9 @@ impl EmitterWriter { num_decimal_digits(n) }; - match self.emit_message_default( + match self.emit_messages_default_inner( span, - message, + messages, args, code, level, @@ -2111,10 +2119,10 @@ impl EmitterWriter { } if !self.short_message { for child in children { - let span = child.render_span.as_ref().unwrap_or(&child.span); - if let Err(err) = self.emit_message_default( + let span = &child.span; + if let Err(err) = self.emit_messages_default_inner( span, - &child.message, + &child.messages, args, &None, &child.level, @@ -2131,7 +2139,7 @@ impl EmitterWriter { // do not display this suggestion, it is meant only for tools } SuggestionStyle::HideCodeAlways => { - if let Err(e) = self.emit_message_default( + if let Err(e) = self.emit_messages_default_inner( &MultiSpan::new(), &[(sugg.msg.to_owned(), Style::HeaderMsg)], args, @@ -2331,7 +2339,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. @@ -2355,11 +2363,7 @@ impl FileWithAnnotatedLines { let label = label.as_ref().map(|m| { normalize_whitespace( - &emitter - .translate_message(m, &args) - .map_err(Report::new) - .unwrap() - .to_string(), + &emitter.translate_message(m, args).map_err(Report::new).unwrap(), ) }); @@ -2671,6 +2675,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(); @@ -2685,11 +2694,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 => { @@ -2704,11 +2709,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) => { @@ -2716,7 +2717,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 0cb75c71b73..52fcb50e9fb 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 @@ -393,7 +398,7 @@ impl Diagnostic { let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap(); let output = String::from_utf8(output).unwrap(); - let translated_message = je.translate_messages(&diag.message, &args); + let translated_message = je.translate_messages(&diag.messages, &args); Diagnostic { message: translated_message.to_string(), code: DiagnosticCode::map_opt_string(diag.code.clone(), je), @@ -414,16 +419,12 @@ impl Diagnostic { args: &FluentArgs<'_>, je: &JsonEmitter, ) -> Diagnostic { - let translated_message = je.translate_messages(&diag.message, args); + let translated_message = je.translate_messages(&diag.messages, args); Diagnostic { message: translated_message.to_string(), code: None, level: diag.level.to_str(), - spans: diag - .render_span - .as_ref() - .map(|sp| DiagnosticSpan::from_multispan(sp, args, je)) - .unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, args, je)), + spans: DiagnosticSpan::from_multispan(&diag.span, args, je), children: vec![], rendered: None, } diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index 1f9a2981e02..303de0a93f6 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 da74ee6391a..76b7e0d79a9 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -3,14 +3,16 @@ //! 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(associated_type_defaults)] +#![feature(box_into_inner)] #![feature(extract_if)] #![feature(if_let_guard)] #![feature(let_chains)] +#![feature(negative_impls)] #![feature(never_type)] -#![feature(result_option_inspect)] #![feature(rustc_attrs)] #![feature(yeet_expr)] #![feature(try_blocks)] @@ -27,27 +29,42 @@ extern crate tracing; extern crate self as rustc_errors; +pub use diagnostic::{ + AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId, + DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic, +}; +pub use diagnostic_builder::{ + BugAbort, DiagnosticBuilder, EmissionGuarantee, FatalAbort, IntoDiagnostic, +}; +pub use diagnostic_impls::{ + DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter, + IndicateAnonymousLifetime, InvalidFlushedDelayedDiagnosticLevel, SingleLabelManySpans, +}; pub use emitter::ColorConfig; +pub use rustc_error_messages::{ + fallback_fluent_bundle, fluent_bundle, DelayDm, DiagnosticMessage, FluentBundle, + LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagnosticMessage, +}; +pub use rustc_lint_defs::{pluralize, Applicability}; +pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker}; +pub use rustc_span::ErrorGuaranteed; +pub use snippet::Style; -use rustc_lint_defs::LintExpectationId; -use Level::*; +// Used by external projects such as `rust-gpu`. +// See https://github.com/rust-lang/rust/pull/115393. +pub use termcolor::{Color, ColorSpec, WriteColor}; -use emitter::{is_case_difference, DynEmitter, Emitter, EmitterWriter}; +use crate::diagnostic_impls::{DelayedAtWithNewline, DelayedAtWithoutNewline}; +use emitter::{is_case_difference, DynEmitter, Emitter, HumanEmitter}; use registry::Registry; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::{Hash128, StableHasher}; use rustc_data_structures::sync::{Lock, Lrc}; use rustc_data_structures::AtomicRef; -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_lint_defs::LintExpectationId; use rustc_span::source_map::SourceMap; -pub use rustc_span::ErrorGuaranteed; use rustc_span::{Loc, Span, DUMMY_SP}; - +use std::backtrace::{Backtrace, BacktraceStatus}; use std::borrow::Cow; use std::error::Report; use std::fmt; @@ -57,9 +74,7 @@ use std::num::NonZeroUsize; use std::panic; use std::path::{Path, PathBuf}; -// Used by external projects such as `rust-gpu`. -// See https://github.com/rust-lang/rust/pull/115393. -pub use termcolor::{Color, ColorSpec, WriteColor}; +use Level::*; pub mod annotate_snippet_emitter_writer; mod diagnostic; @@ -77,13 +92,10 @@ mod styled_buffer; mod tests; pub mod translation; -pub use diagnostic_builder::IntoDiagnostic; -pub use snippet::Style; - -pub type PErr<'a> = DiagnosticBuilder<'a, ErrorGuaranteed>; +pub type PErr<'a> = DiagnosticBuilder<'a>; 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.) @@ -164,7 +176,7 @@ pub struct SubstitutionPart { /// Used to translate between `Span`s and byte positions within a single output line in highlighted /// code of structured suggestions. #[derive(Debug, Clone, Copy)] -pub struct SubstitutionHighlight { +pub(crate) struct SubstitutionHighlight { start: usize, end: usize, } @@ -191,7 +203,7 @@ impl SubstitutionPart { impl CodeSuggestion { /// Returns the assembled code suggestions, whether they should be shown with an underline /// and whether the substitution only differs in capitalization. - pub fn splice_lines( + pub(crate) fn splice_lines( &self, sm: &SourceMap, ) -> Vec<(String, Vec<SubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, bool)> { @@ -388,8 +400,6 @@ impl CodeSuggestion { } } -pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker}; - /// Signifies that the compiler died with an explicit call to `.bug` /// or `.span_bug` rather than a failed assertion, etc. pub struct ExplicitBug; @@ -398,31 +408,18 @@ pub struct ExplicitBug; /// rather than a failed assertion, etc. pub struct DelayedBugPanic; -use crate::diagnostic_impls::{DelayedAtWithNewline, DelayedAtWithoutNewline}; -pub use diagnostic::{ - AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId, - DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic, -}; -pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted}; -pub use diagnostic_impls::{ - DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter, - IndicateAnonymousLifetime, InvalidFlushedDelayedDiagnosticLevel, LabelKind, - SingleLabelManySpans, -}; -use std::backtrace::{Backtrace, BacktraceStatus}; - -/// A handler deals with errors and other compiler output. +/// A `DiagCtxt` 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 +430,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 @@ -448,7 +445,7 @@ struct HandlerInner { emitted_diagnostic_codes: FxIndexSet<DiagnosticId>, /// This set contains a hash of every diagnostic that has been emitted by - /// this handler. These hashes is used to avoid emitting the same error + /// this `DiagCtxt`. These hashes is used to avoid emitting the same error /// twice. emitted_diagnostics: FxHashSet<Hash128>, @@ -508,29 +505,25 @@ pub enum StashKey { TraitMissingMethod, OpaqueHiddenTypeMismatch, MaybeForgetReturn, + /// Query cycle detected, stashing in favor of a better error. + Cycle, } -fn default_track_diagnostic(d: &mut Diagnostic, f: &mut dyn FnMut(&mut Diagnostic)) { - (*f)(d) +fn default_track_diagnostic(diag: Diagnostic, f: &mut dyn FnMut(Diagnostic)) { + (*f)(diag) } -pub static TRACK_DIAGNOSTICS: AtomicRef<fn(&mut Diagnostic, &mut dyn FnMut(&mut Diagnostic))> = +pub static TRACK_DIAGNOSTICS: AtomicRef<fn(Diagnostic, &mut dyn FnMut(Diagnostic))> = 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, /// If Some, the Nth error-level diagnostic is upgraded to bug-level. /// (rustc: see `-Z treat-err-as-bug`) pub treat_err_as_bug: Option<NonZeroUsize>, - /// 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`. - /// (rustc: see `-Z report-delayed-bugs`) - pub report_delayed_bugs: bool, /// Show macro backtraces. /// (rustc: see `-Z macro-backtrace`) pub macro_backtrace: bool, @@ -540,25 +533,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 { - 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", ); } @@ -571,12 +565,12 @@ impl Drop for HandlerInner { } } -impl Handler { +impl DiagCtxt { pub fn with_tty_emitter( sm: Option<Lrc<SourceMap>>, fallback_bundle: LazyFallbackBundle, ) -> Self { - let emitter = Box::new(EmitterWriter::stderr(ColorConfig::Auto, fallback_bundle).sm(sm)); + let emitter = Box::new(HumanEmitter::stderr(ColorConfig::Auto, fallback_bundle).sm(sm)); Self::with_emitter(emitter) } pub fn disable_warnings(mut self) -> Self { @@ -584,12 +578,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 } @@ -601,16 +590,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(), @@ -648,7 +637,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. @@ -664,8 +653,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(); @@ -676,15 +665,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 diag.level == Error && diag.is_lint { + 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 diag.level == Error && diag.is_lint { + 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 { @@ -696,16 +715,6 @@ impl Handler { self.inner.borrow_mut().emit_stashed_diagnostics() } - /// Construct a builder with the `msg` at the level appropriate for the specific `EmissionGuarantee`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_diagnostic<G: EmissionGuarantee>( - &self, - msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_, G> { - G::make_diagnostic_builder(self, msg) - } - /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. /// /// Attempting to `.emit()` the builder will only emit if either: @@ -718,55 +727,7 @@ impl Handler { span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ()> { - let mut result = self.struct_warn(msg); - result.set_span(span); - result - } - - /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. - /// The `id` is used for lint emissions which should also fulfill a lint expectation. - /// - /// Attempting to `.emit()` the builder will only emit if either: - /// * `can_emit_warnings` is `true` - /// * `is_force_warn` was set in `DiagnosticId::Lint` - #[track_caller] - pub fn struct_span_warn_with_expectation( - &self, - span: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, - id: LintExpectationId, - ) -> DiagnosticBuilder<'_, ()> { - let mut result = self.struct_warn_with_expectation(msg, id); - result.set_span(span); - result - } - - /// Construct a builder at the `Allow` level at the given `span` and with the `msg`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_span_allow( - &self, - span: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_, ()> { - let mut result = self.struct_allow(msg); - result.set_span(span); - result - } - - /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. - /// Also include a code. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_span_warn_with_code( - &self, - span: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, - code: DiagnosticId, - ) -> DiagnosticBuilder<'_, ()> { - let mut result = self.struct_span_warn(span, msg); - result.code(code); - result + self.struct_warn(msg).span_mv(span) } /// Construct a builder at the `Warning` level with the `msg`. @@ -777,29 +738,14 @@ impl Handler { #[rustc_lint_diagnostics] #[track_caller] pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { - DiagnosticBuilder::new(self, Level::Warning(None), msg) - } - - /// Construct a builder at the `Warning` level with the `msg`. The `id` is used for - /// lint emissions which should also fulfill a lint expectation. - /// - /// Attempting to `.emit()` the builder will only emit if either: - /// * `can_emit_warnings` is `true` - /// * `is_force_warn` was set in `DiagnosticId::Lint` - #[track_caller] - pub fn struct_warn_with_expectation( - &self, - msg: impl Into<DiagnosticMessage>, - id: LintExpectationId, - ) -> DiagnosticBuilder<'_, ()> { - DiagnosticBuilder::new(self, Level::Warning(Some(id)), msg) + DiagnosticBuilder::new(self, Warning(None), msg) } /// Construct a builder at the `Allow` level with the `msg`. #[rustc_lint_diagnostics] #[track_caller] pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { - DiagnosticBuilder::new(self, Level::Allow, msg) + DiagnosticBuilder::new(self, Allow, msg) } /// Construct a builder at the `Expect` level with the `msg`. @@ -810,7 +756,7 @@ impl Handler { msg: impl Into<DiagnosticMessage>, id: LintExpectationId, ) -> DiagnosticBuilder<'_, ()> { - DiagnosticBuilder::new(self, Level::Expect(id), msg) + DiagnosticBuilder::new(self, Expect(id), msg) } /// Construct a builder at the `Error` level at the given `span` and with the `msg`. @@ -820,68 +766,16 @@ impl Handler { &self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { - let mut result = self.struct_err(msg); - result.set_span(span); - result - } - - /// Construct a builder at the `Error` level at the given `span`, with the `msg`, and `code`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_span_err_with_code( - &self, - span: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, - code: DiagnosticId, - ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { - let mut result = self.struct_span_err(span, msg); - result.code(code); - result + ) -> DiagnosticBuilder<'_> { + self.struct_err(msg).span_mv(span) } /// Construct a builder at the `Error` level with the `msg`. // FIXME: This method should be removed (every error should have an associated error code). #[rustc_lint_diagnostics] #[track_caller] - pub fn struct_err( - &self, - msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { - DiagnosticBuilder::new_guaranteeing_error(self, msg) - } - - /// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors. - #[doc(hidden)] - #[track_caller] - pub fn struct_err_lint(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { - DiagnosticBuilder::new(self, Level::Error { lint: true }, msg) - } - - /// Construct a builder at the `Error` level with the `msg` and the `code`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_err_with_code( - &self, - msg: impl Into<DiagnosticMessage>, - code: DiagnosticId, - ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { - let mut result = self.struct_err(msg); - result.code(code); - result - } - - /// Construct a builder at the `Warn` level with the `msg` and the `code`. - #[rustc_lint_diagnostics] - #[track_caller] - pub fn struct_warn_with_code( - &self, - msg: impl Into<DiagnosticMessage>, - code: DiagnosticId, - ) -> DiagnosticBuilder<'_, ()> { - let mut result = self.struct_warn(msg); - result.code(code); - result + pub fn struct_err(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_> { + DiagnosticBuilder::new(self, Error, msg) } /// Construct a builder at the `Fatal` level at the given `span` and with the `msg`. @@ -891,185 +785,161 @@ impl Handler { &self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>, - ) -> DiagnosticBuilder<'_, !> { - let mut result = self.struct_fatal(msg); - result.set_span(span); - result + ) -> DiagnosticBuilder<'_, FatalAbort> { + self.struct_fatal(msg).span_mv(span) } - /// Construct a builder at the `Fatal` level at the given `span`, with the `msg`, and `code`. + /// Construct a builder at the `Fatal` level with the `msg`. #[rustc_lint_diagnostics] #[track_caller] - pub fn struct_span_fatal_with_code( + pub fn struct_fatal( &self, - span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>, - code: DiagnosticId, - ) -> DiagnosticBuilder<'_, !> { - let mut result = self.struct_span_fatal(span, msg); - result.code(code); - result - } - - /// Construct a builder at the `Error` 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<'_, FatalAbort> { + DiagnosticBuilder::new(self, Fatal, msg) } /// Construct a builder at the `Help` level with the `msg`. #[rustc_lint_diagnostics] pub fn struct_help(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { - DiagnosticBuilder::new(self, Level::Help, msg) + DiagnosticBuilder::new(self, Help, msg) } /// 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<'_, ()> { - DiagnosticBuilder::new(self, Level::Note, msg) + pub fn struct_note(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Note, msg) } + /// Construct a builder at the `Bug` level with the `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() + pub fn struct_bug(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, BugAbort> { + DiagnosticBuilder::new(self, Bug, msg) } + /// Construct a builder at the `Bug` level at the given `span` with the `msg`. #[rustc_lint_diagnostics] #[track_caller] - pub fn span_fatal_with_code( + pub fn struct_span_bug( &self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>, - code: DiagnosticId, - ) -> ! { - self.emit_diag_at_span(Diagnostic::new_with_code(Fatal, Some(code), msg), span); - FatalError.raise() + ) -> DiagnosticBuilder<'_, BugAbort> { + self.struct_bug(msg).span_mv(span) } #[rustc_lint_diagnostics] #[track_caller] - pub fn span_err( - &self, - span: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, - ) -> ErrorGuaranteed { - self.emit_diag_at_span(Diagnostic::new(Error { lint: false }, msg), span).unwrap() + pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { + self.struct_span_fatal(span, msg).emit() } #[rustc_lint_diagnostics] #[track_caller] - pub fn span_err_with_code( + pub fn span_err( &self, 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(span, msg).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); - } - - #[rustc_lint_diagnostics] - #[track_caller] - pub fn span_warn_with_code( - &self, - span: impl Into<MultiSpan>, - 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(span, msg).emit() } - pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<String>) -> ! { - self.inner.borrow_mut().span_bug(span, msg) + pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { + self.struct_span_bug(span, msg).emit() } - /// For documentation on this, see `Session::delay_span_bug`. + /// Ensures that compilation cannot succeed. + /// + /// If this function has been called but no errors have been emitted and + /// compilation succeeds, it will cause an internal compiler error (ICE). + /// + /// This can be used in code paths that should never run on successful compilations. + /// For example, it can be used to create an [`ErrorGuaranteed`] + /// (but you should prefer threading through the [`ErrorGuaranteed`] from an error emission + /// directly). + /// + /// If no span is available, use [`DUMMY_SP`]. + /// + /// [`DUMMY_SP`]: rustc_span::DUMMY_SP + /// + /// Note: this function used to be called `delay_span_bug`. It was renamed + /// to match similar functions like `span_err`, `span_warn`, 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 treat_next_err_as_bug = self.inner.borrow().treat_next_err_as_bug(); + if treat_next_err_as_bug { + self.span_bug(sp, msg); + } + let mut diagnostic = Diagnostic::new(DelayedBug, msg); + diagnostic.span(sp); + self.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) - } - - #[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); + pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) { + let mut inner = self.inner.borrow_mut(); + let diagnostic = Diagnostic::new(DelayedBug, msg); + let backtrace = std::backtrace::Backtrace::capture(); + inner.good_path_delayed_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace)); } #[track_caller] #[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); - db.set_span(span); - db + DiagnosticBuilder::new(self, Note, msg).span_mv(span) } - // 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() } + #[rustc_lint_diagnostics] pub fn bug(&self, msg: impl Into<DiagnosticMessage>) -> ! { - self.inner.borrow_mut().bug(msg) + self.struct_bug(msg).emit() } #[inline] pub fn err_count(&self) -> usize { - self.inner.borrow().err_count() + self.inner.borrow().err_count } pub fn has_errors(&self) -> Option<ErrorGuaranteed> { @@ -1080,26 +950,100 @@ 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(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> { @@ -1107,7 +1051,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. @@ -1116,46 +1064,49 @@ 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, diagnostic: &mut Diagnostic) -> Option<ErrorGuaranteed> { + pub fn emit_diagnostic(&self, diagnostic: Diagnostic) -> Option<ErrorGuaranteed> { self.inner.borrow_mut().emit_diagnostic(diagnostic) } + #[track_caller] pub fn emit_err<'a>(&'a self, err: impl IntoDiagnostic<'a>) -> ErrorGuaranteed { self.create_err(err).emit() } - pub fn create_err<'a>( - &'a self, - err: impl IntoDiagnostic<'a>, - ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { - err.into_diagnostic(self) + #[track_caller] + pub fn create_err<'a>(&'a self, err: impl IntoDiagnostic<'a>) -> DiagnosticBuilder<'a> { + err.into_diagnostic(self, Error) } + #[track_caller] pub fn create_warning<'a>( &'a self, warning: impl IntoDiagnostic<'a, ()>, ) -> DiagnosticBuilder<'a, ()> { - warning.into_diagnostic(self) + warning.into_diagnostic(self, Warning(None)) } + #[track_caller] pub fn emit_warning<'a>(&'a self, warning: impl IntoDiagnostic<'a, ()>) { self.create_warning(warning).emit() } + #[track_caller] pub fn create_almost_fatal<'a>( &'a self, fatal: impl IntoDiagnostic<'a, FatalError>, ) -> DiagnosticBuilder<'a, FatalError> { - fatal.into_diagnostic(self) + fatal.into_diagnostic(self, Fatal) } + #[track_caller] pub fn emit_almost_fatal<'a>( &'a self, fatal: impl IntoDiagnostic<'a, FatalError>, @@ -1163,53 +1114,47 @@ impl Handler { self.create_almost_fatal(fatal).emit() } + #[track_caller] pub fn create_fatal<'a>( &'a self, - fatal: impl IntoDiagnostic<'a, !>, - ) -> DiagnosticBuilder<'a, !> { - fatal.into_diagnostic(self) + fatal: impl IntoDiagnostic<'a, FatalAbort>, + ) -> DiagnosticBuilder<'a, FatalAbort> { + fatal.into_diagnostic(self, Fatal) } - pub fn emit_fatal<'a>(&'a self, fatal: impl IntoDiagnostic<'a, !>) -> ! { + #[track_caller] + pub fn emit_fatal<'a>(&'a self, fatal: impl IntoDiagnostic<'a, FatalAbort>) -> ! { self.create_fatal(fatal).emit() } + #[track_caller] pub fn create_bug<'a>( &'a self, - bug: impl IntoDiagnostic<'a, diagnostic_builder::Bug>, - ) -> DiagnosticBuilder<'a, diagnostic_builder::Bug> { - bug.into_diagnostic(self) + bug: impl IntoDiagnostic<'a, BugAbort>, + ) -> DiagnosticBuilder<'a, BugAbort> { + bug.into_diagnostic(self, Bug) } - pub fn emit_bug<'a>( - &'a self, - bug: impl IntoDiagnostic<'a, diagnostic_builder::Bug>, - ) -> diagnostic_builder::Bug { + #[track_caller] + pub fn emit_bug<'a>(&'a self, bug: impl IntoDiagnostic<'a, diagnostic_builder::BugAbort>) -> ! { self.create_bug(bug).emit() } - pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, Noted>) -> Noted { + #[track_caller] + pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, ()>) { self.create_note(note).emit() } + #[track_caller] pub fn create_note<'a>( &'a self, - note: impl IntoDiagnostic<'a, Noted>, - ) -> DiagnosticBuilder<'a, Noted> { - 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)) + note: impl IntoDiagnostic<'a, ()>, + ) -> DiagnosticBuilder<'a, ()> { + note.into_diagnostic(self, Note) } 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>) { @@ -1228,12 +1173,12 @@ 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( &self, - unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>, + unstable_to_stable: &FxIndexMap<LintExpectationId, LintExpectationId>, ) { let mut inner = self.inner.borrow_mut(); let diags = std::mem::take(&mut inner.unstable_expect_diagnostics); @@ -1247,7 +1192,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); } } @@ -1262,41 +1207,37 @@ 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 }) { + if diag.level == Error && diag.is_lint { self.lint_err_count -= 1; } else { self.err_count -= 1; @@ -1313,14 +1254,17 @@ 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> { + if matches!(diagnostic.level, Error | Fatal) && self.treat_err_as_bug() { + diagnostic.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 @@ -1330,23 +1274,21 @@ impl HandlerInner { return None; } - if diagnostic.level == Level::DelayedBug { + if diagnostic.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 { - #[allow(deprecated)] - return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); - } + #[allow(deprecated)] + return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); } if diagnostic.has_future_breakage() { - // Future breakages aren't emitted if they're Level::Allowed, + // Future breakages aren't emitted if they're Level::Allow, // but they still need to be constructed and stashed below, // so they'll trigger the good-path bug check. self.suppressed_expected_diag = true; @@ -1368,13 +1310,13 @@ impl HandlerInner { return None; } - if matches!(diagnostic.level, Level::Expect(_) | Level::Allow) { + if matches!(diagnostic.level, Expect(_) | Allow) { (*TRACK_DIAGNOSTICS)(diagnostic, &mut |_| {}); return None; } let mut guaranteed = None; - (*TRACK_DIAGNOSTICS)(diagnostic, &mut |diagnostic| { + (*TRACK_DIAGNOSTICS)(diagnostic, &mut |mut diagnostic| { if let Some(ref code) = diagnostic.code { self.emitted_diagnostic_codes.insert(code.clone()); } @@ -1393,7 +1335,7 @@ impl HandlerInner { debug!(?self.emitted_diagnostics); let already_emitted_sub = |sub: &mut SubDiagnostic| { debug!(?sub); - if sub.level != Level::OnceNote && sub.level != Level::OnceHelp { + if sub.level != OnceNote && sub.level != OnceHelp { return false; } let mut hasher = StableHasher::new(); @@ -1410,7 +1352,7 @@ impl HandlerInner { ); } - self.emitter.emit_diagnostic(diagnostic); + self.emitter.emit_diagnostic(&diagnostic); if diagnostic.is_error() { self.deduplicated_err_count += 1; } else if let Warning(_) = diagnostic.level { @@ -1418,7 +1360,7 @@ impl HandlerInner { } } if diagnostic.is_error() { - if matches!(diagnostic.level, Level::Error { lint: true }) { + if diagnostic.level == Error && diagnostic.is_lint { self.bump_lint_err_count(); } else { self.bump_err_count(); @@ -1436,238 +1378,43 @@ 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) + // Use this one before incrementing `err_count`. + fn treat_next_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() + 1 >= c.get() + }) } - #[inline] - fn err_count(&self) -> usize { - self.err_count + fn delayed_bug_count(&self) -> usize { + 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(); - } - } - - #[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); - 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)); + self.err_count > 0 } - 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( &mut self, - bugs: impl IntoIterator<Item = DelayedDiagnostic>, + bugs: Vec<DelayedDiagnostic>, explanation: impl Into<DiagnosticMessage> + Copy, ) { - let mut no_bugs = true; + if bugs.is_empty() { + return; + } + // If backtraces are enabled, also print the query stack let backtrace = std::env::var_os("RUST_BACKTRACE").map_or(true, |x| &x != "0"); - for bug in bugs { + for (i, bug) in bugs.into_iter().enumerate() { if let Some(file) = self.ice_file.as_ref() && let Ok(mut out) = std::fs::File::options().create(true).append(true).open(file) { @@ -1675,25 +1422,25 @@ impl HandlerInner { &mut out, "delayed span bug: {}\n{}\n", bug.inner - .styled_message() + .messages() .iter() .filter_map(|(msg, _)| msg.as_str()) .collect::<String>(), &bug.note ); } - let mut bug = - if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner }; - if no_bugs { + if i == 0 { // 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)); - no_bugs = false; + self.emit_diagnostic(Diagnostic::new(Bug, explanation)); } + let mut bug = + if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner }; + // "Undelay" the `DelayedBug`s (into plain `Bug`s). - if bug.level != Level::DelayedBug { + if bug.level != DelayedBug { // NOTE(eddyb) not panicking here because we're already producing // an ICE, and the more information the merrier. bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel { @@ -1701,15 +1448,13 @@ impl HandlerInner { level: bug.level, }); } - bug.level = Level::Bug; + bug.level = Bug; - self.emit_diagnostic(&mut bug); + self.emit_diagnostic(bug); } // Panic with `DelayedBugPanic` to avoid "unexpected panic" messages. - if !no_bugs { - panic::panic_any(DelayedBugPanic); - } + panic::panic_any(DelayedBugPanic); } fn bump_lint_err_count(&mut self) { @@ -1729,7 +1474,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(), ) { @@ -1787,25 +1532,76 @@ impl DelayedDiagnostic { #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)] pub enum Level { + /// For bugs in the compiler. Manifests as an ICE (internal compiler error) panic. + /// + /// Its `EmissionGuarantee` is `BugAbort`. Bug, + + /// This is a strange one: lets you register an error without emitting it. If compilation ends + /// without any other errors occurring, this will be emitted as a bug. Otherwise, it will be + /// silently dropped. I.e. "expect other errors are emitted" semantics. Useful on code paths + /// that should only be reached when compiling erroneous code. + /// + /// Its `EmissionGuarantee` is `ErrorGuaranteed`. DelayedBug, + + /// An error that causes an immediate abort. Used for things like configuration errors, + /// internal overflows, some file operation errors. + /// + /// Its `EmissionGuarantee` is `FatalAbort`, except in the non-aborting "almost fatal" case + /// that is occasionally used, where it is `FatalError`. Fatal, - Error { - /// If this error comes from a lint, don't abort compilation even when abort_if_errors() is called. - lint: bool, - }, + + /// An error in the code being compiled, which prevents compilation from finishing. This is the + /// most common case. + /// + /// Its `EmissionGuarantee` is `ErrorGuaranteed`. + Error, + + /// A warning about the code being compiled. Does not prevent compilation from finishing. + /// /// This [`LintExpectationId`] is used for expected lint diagnostics, which should /// also emit a warning due to the `force-warn` flag. In all other cases this should /// be `None`. + /// + /// Its `EmissionGuarantee` is `()`. Warning(Option<LintExpectationId>), + + /// A message giving additional context. Rare, because notes are more commonly attached to other + /// diagnostics such as errors. + /// + /// Its `EmissionGuarantee` is `()`. Note, - /// A note that is only emitted once. + + /// A note that is only emitted once. Rare, mostly used in circumstances relating to lints. + /// + /// Its `EmissionGuarantee` is `()`. OnceNote, + + /// A message suggesting how to fix something. Rare, because help messages are more commonly + /// attached to other diagnostics such as errors. + /// + /// Its `EmissionGuarantee` is `()`. Help, - /// A help that is only emitted once. + + /// A help that is only emitted once. Rare. + /// + /// Its `EmissionGuarantee` is `()`. OnceHelp, + + /// Similar to `Note`, but used in cases where compilation has failed. Rare. + /// + /// Its `EmissionGuarantee` is `()`. FailureNote, + + /// Only used for lints. + /// + /// Its `EmissionGuarantee` is `()`. Allow, + + /// Only used for lints. + /// + /// Its `EmissionGuarantee` is `()`. Expect(LintExpectationId), } @@ -1819,7 +1615,7 @@ impl Level { fn color(self) -> ColorSpec { let mut spec = ColorSpec::new(); match self { - Bug | DelayedBug | Fatal | Error { .. } => { + Bug | DelayedBug | Fatal | Error => { spec.set_fg(Some(Color::Red)).set_intense(true); } Warning(_) => { @@ -1840,13 +1636,12 @@ impl Level { pub fn to_str(self) -> &'static str { match self { Bug | DelayedBug => "error: internal compiler error", - Fatal | Error { .. } => "error", + Fatal | Error => "error", Warning(_) => "warning", Note | OnceNote => "note", Help | OnceHelp => "help", FailureNote => "failure-note", - Allow => panic!("Shouldn't call on allowed error"), - Expect(_) => panic!("Shouldn't call on expected error"), + Allow | Expect(_) => unreachable!(), } } @@ -1856,7 +1651,7 @@ impl Level { pub fn get_expectation_id(&self) -> Option<LintExpectationId> { match self { - Level::Expect(id) | Level::Warning(Some(id)) => Some(*id), + Expect(id) | Warning(Some(id)) => Some(*id), _ => None, } } diff --git a/compiler/rustc_errors/src/markdown/parse.rs b/compiler/rustc_errors/src/markdown/parse.rs index d3a08da6283..67e4963fddf 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 6f68fb25a58..a0d956bf0cd 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"]; |
