diff options
Diffstat (limited to 'compiler/rustc_errors/src')
| -rw-r--r-- | compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs | 9 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic.rs | 88 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic_builder.rs | 355 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/emitter.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/json.rs | 10 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/json/tests.rs | 27 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/lib.rs | 271 |
7 files changed, 539 insertions, 225 deletions
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 9db8f751390..c380455012d 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -66,13 +66,16 @@ 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::Fatal | Level::Error { .. } => AnnotationType::Error, + Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error { .. } => { + AnnotationType::Error + } Level::Warning => AnnotationType::Warning, Level::Note => AnnotationType::Note, Level::Help => AnnotationType::Help, - // FIXME(#59346): Not sure how to map these two levels - Level::Cancelled | Level::FailureNote => AnnotationType::Error, + // FIXME(#59346): Not sure how to map this level + Level::FailureNote => AnnotationType::Error, Level::Allow => panic!("Should not call with Allow"), + Level::Expect(_) => panic!("Should not call with Expect"), } } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 8cfecafd20c..39ebd57b4b2 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -7,6 +7,7 @@ use crate::SuggestionStyle; use crate::ToolMetadata; use rustc_lint_defs::Applicability; use rustc_serialize::json::Json; +use rustc_span::edition::LATEST_STABLE_EDITION; use rustc_span::{MultiSpan, Span, DUMMY_SP}; use std::fmt; use std::hash::{Hash, Hasher}; @@ -19,7 +20,10 @@ pub struct SuggestionsDisabled; #[must_use] #[derive(Clone, Debug, Encodable, Decodable)] pub struct Diagnostic { - pub level: Level, + // NOTE(eddyb) this is private to disallow arbitrary after-the-fact changes, + // outside of what methods in this crate themselves allow. + crate level: Level, + pub message: Vec<(String, Style)>, pub code: Option<DiagnosticId>, pub span: MultiSpan, @@ -117,11 +121,20 @@ impl Diagnostic { } } + #[inline(always)] + pub fn level(&self) -> Level { + self.level + } + pub fn is_error(&self) -> bool { match self.level { - Level::Bug | Level::Fatal | Level::Error { .. } | Level::FailureNote => true, + Level::Bug + | Level::DelayedBug + | Level::Fatal + | Level::Error { .. } + | Level::FailureNote => true, - Level::Warning | Level::Note | Level::Help | Level::Cancelled | Level::Allow => false, + Level::Warning | Level::Note | Level::Help | Level::Allow | Level::Expect(_) => false, } } @@ -139,15 +152,26 @@ impl Diagnostic { } } - /// Cancel the diagnostic (a structured diagnostic must either be emitted or - /// canceled or it will panic when dropped). - pub fn cancel(&mut self) { - self.level = Level::Cancelled; - } + /// Delay emission of this diagnostic as a bug. + /// + /// This can be useful in contexts where an error indicates a bug but + /// typically this only happens when other compilation errors have already + /// happened. In those cases this can be used to defer emission of this + /// diagnostic as a bug in the compiler only if no other errors have been + /// emitted. + /// + /// 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 { + assert!( + self.is_error(), + "downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error", + self.level + ); + self.level = Level::DelayedBug; - /// Check if this diagnostic [was cancelled][Self::cancel()]. - pub fn cancelled(&self) -> bool { - self.level == Level::Cancelled + self } /// Adds a span/label to be included in the resulting snippet. @@ -163,6 +187,20 @@ impl Diagnostic { self } + /// Labels all the given spans with the provided label. + /// See [`Self::span_label()`] for more information. + pub fn span_labels( + &mut self, + spans: impl IntoIterator<Item = Span>, + label: impl AsRef<str>, + ) -> &mut Self { + let label = label.as_ref(); + for span in spans { + self.span_label(span, label); + } + self + } + pub fn replace_span_with(&mut self, after: Span) -> &mut Self { let before = self.span.clone(); self.set_span(after); @@ -174,7 +212,7 @@ impl Diagnostic { self } - crate fn note_expected_found( + pub fn note_expected_found( &mut self, expected_label: &dyn fmt::Display, expected: DiagnosticStyledString, @@ -184,7 +222,7 @@ impl Diagnostic { self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"") } - crate fn note_unsuccessful_coercion( + pub fn note_unsuccessful_coercion( &mut self, expected: DiagnosticStyledString, found: DiagnosticStyledString, @@ -274,37 +312,49 @@ impl Diagnostic { /// Prints the span with a note above it. /// This is like [`Diagnostic::note()`], but it gets its own span. - crate fn span_note<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self { + pub fn span_note<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self { self.sub(Level::Note, msg, sp.into(), None); self } /// Add a warning attached to this diagnostic. - crate fn warn(&mut self, msg: &str) -> &mut Self { + pub fn warn(&mut self, msg: &str) -> &mut Self { self.sub(Level::Warning, msg, MultiSpan::new(), None); self } /// Prints the span with a warning above it. /// This is like [`Diagnostic::warn()`], but it gets its own span. - crate fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self { + pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self { self.sub(Level::Warning, msg, sp.into(), None); self } /// Add a help message attached to this diagnostic. - crate fn help(&mut self, msg: &str) -> &mut Self { + pub fn help(&mut self, msg: &str) -> &mut Self { self.sub(Level::Help, msg, MultiSpan::new(), None); self } /// Prints the span with some help above it. /// This is like [`Diagnostic::help()`], but it gets its own span. - crate fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self { + pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self { self.sub(Level::Help, msg, sp.into(), None); self } + /// Help the user upgrade to the latest edition. + /// This is factored out to make sure it does the right thing with `Cargo.toml`. + pub fn help_use_latest_edition(&mut self) -> &mut Self { + if std::env::var_os("CARGO").is_some() { + self.help(&format!("set `edition = \"{}\"` in `Cargo.toml`", LATEST_STABLE_EDITION)); + } else { + self.help(&format!("pass `--edition {}` to `rustc`", LATEST_STABLE_EDITION)); + } + self.note("for more on editions, read https://doc.rust-lang.org/edition-guide"); + self + } + /// Disallow attaching suggestions this diagnostic. /// Any suggestions attached e.g. with the `span_suggestion_*` methods /// (before and after the call to `disable_suggestions`) will be ignored. @@ -634,7 +684,7 @@ impl Diagnostic { self.code.clone() } - crate fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self { + pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self { self.message[0] = (msg.into(), Style::NoStyle); self } diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 3c8751a7a35..98b8b2a569e 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -1,9 +1,10 @@ -use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString}; +use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString, ErrorGuaranteed}; use crate::{Handler, Level, StashKey}; use rustc_lint_defs::Applicability; use rustc_span::{MultiSpan, Span}; use std::fmt::{self, Debug}; +use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::thread::panicking; use tracing::debug; @@ -15,8 +16,25 @@ use tracing::debug; /// extending `HandlerFlags`, accessed via `self.handler.flags`. #[must_use] #[derive(Clone)] -pub struct DiagnosticBuilder<'a> { - handler: &'a Handler, +pub struct DiagnosticBuilder<'a, G: EmissionGuarantee> { + inner: DiagnosticBuilderInner<'a>, + _marker: PhantomData<G>, +} + +/// 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. @@ -25,6 +43,161 @@ pub struct DiagnosticBuilder<'a> { diagnostic: Box<Diagnostic>, } +#[derive(Clone)] +enum DiagnosticBuilderState<'a> { + /// Initial state of a `DiagnosticBuilder`, before `.emit()` or `.cancel()`. + /// + /// The `Diagnostic` will be emitted through this `Handler`. + Emittable(&'a Handler), + + /// 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, +} + +// `DiagnosticBuilderState` should be pointer-sized. +rustc_data_structures::static_assert_size!( + DiagnosticBuilderState<'_>, + std::mem::size_of::<&Handler>() +); + +/// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee" +/// (or "proof") token that the emission happened. +pub trait EmissionGuarantee: Sized { + /// 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; +} + +/// Private module for sealing the `IsError` helper trait. +mod sealed_level_is_error { + use crate::Level; + + /// Sealed helper trait for statically checking that a `Level` is an error. + crate trait IsError<const L: Level> {} + + impl IsError<{ Level::Bug }> for () {} + impl IsError<{ Level::DelayedBug }> for () {} + impl IsError<{ Level::Fatal }> for () {} + // NOTE(eddyb) `Level::Error { lint: true }` is also an error, but lints + // don't need error guarantees, as their levels are always dynamic. + impl IsError<{ Level::Error { lint: false } }> for () {} +} + +impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> { + /// Convenience function for internal use, clients should use one of the + /// `struct_*` methods on [`Handler`]. + crate fn new_guaranteeing_error<const L: Level>(handler: &'a Handler, message: &str) -> Self + where + (): sealed_level_is_error::IsError<L>, + { + Self { + inner: DiagnosticBuilderInner { + state: DiagnosticBuilderState::Emittable(handler), + diagnostic: Box::new(Diagnostic::new_with_code(L, 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; + + handler.emit_diagnostic(&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, + ); + ErrorGuaranteed + } + // `.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, + ); + ErrorGuaranteed + } + } + } +} + +impl<'a> DiagnosticBuilder<'a, ()> { + /// Convenience function for internal use, clients should use one of the + /// `struct_*` methods on [`Handler`]. + crate fn new(handler: &'a Handler, level: Level, message: &str) -> Self { + let diagnostic = Diagnostic::new_with_code(level, None, message); + Self::new_diagnostic(handler, diagnostic) + } + + /// Creates a new `DiagnosticBuilder` with an already constructed + /// diagnostic. + 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, + } + } +} + +// 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; + + handler.emit_diagnostic(&db.inner.diagnostic); + } + // `.emit()` was previously called, disallowed from repeating it. + DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} + } + } +} + /// 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 @@ -55,60 +228,54 @@ macro_rules! forward { $(#[$attrs])* #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] pub fn $n(&mut self, $($name: $ty),*) -> &mut Self { - self.diagnostic.$n($($name),*); - self - } - }; - - // Forward pattern for &mut self -> &mut Self, with generic parameters. - ( - $(#[$attrs:meta])* - pub fn $n:ident<$($generic:ident: $bound:path),*>( - &mut self, - $($name:ident: $ty:ty),* - $(,)? - ) -> &mut Self - ) => { - $(#[$attrs])* - #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] - pub fn $n<$($generic: $bound),*>(&mut self, $($name: $ty),*) -> &mut Self { - self.diagnostic.$n($($name),*); + self.inner.diagnostic.$n($($name),*); self } }; } -impl<'a> Deref for DiagnosticBuilder<'a> { +impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> { type Target = Diagnostic; fn deref(&self) -> &Diagnostic { - &self.diagnostic + &self.inner.diagnostic } } -impl<'a> DerefMut for DiagnosticBuilder<'a> { +impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> { fn deref_mut(&mut self) -> &mut Diagnostic { - &mut self.diagnostic + &mut self.inner.diagnostic } } -impl<'a> DiagnosticBuilder<'a> { +impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { /// Emit the diagnostic. - pub fn emit(&mut self) { - self.handler.emit_diagnostic(&self); - self.cancel(); + #[track_caller] + pub fn emit(&mut self) -> G { + G::diagnostic_builder_emit_producing_guarantee(self) } /// Emit the diagnostic unless `delay` is true, /// in which case the emission will be delayed as a bug. /// /// See `emit` and `delay_as_bug` for details. - pub fn emit_unless(&mut self, delay: bool) { + #[track_caller] + pub fn emit_unless(&mut self, delay: bool) -> G { if delay { - self.delay_as_bug(); - } else { - self.emit(); + self.downgrade_to_delayed_bug(); } + self.emit() + } + + /// Cancel the diagnostic (a structured 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; + drop(self); } /// Stashes diagnostic for possible later improvement in a different, @@ -123,21 +290,28 @@ impl<'a> DiagnosticBuilder<'a> { } /// Converts the builder to a `Diagnostic` for later emission, - /// unless handler has disabled such buffering. + /// unless handler has disabled such buffering, or `.emit()` was called. pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> { - if self.handler.flags.dont_buffer_diagnostics - || self.handler.flags.treat_err_as_bug.is_some() - { + 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.flags.dont_buffer_diagnostics || handler.flags.treat_err_as_bug.is_some() { self.emit(); return None; } - let handler = self.handler; + // Take the `Diagnostic` by replacing it with a dummy. + let dummy = Diagnostic::new(Level::Allow, ""); + let diagnostic = std::mem::replace(&mut *self.inner.diagnostic, dummy); - // We must use `Level::Cancelled` for `dummy` to avoid an ICE about an - // unused diagnostic. - let dummy = Diagnostic::new(Level::Cancelled, ""); - let diagnostic = std::mem::replace(&mut *self.diagnostic, dummy); + // Disable the ICE on `Drop`. + self.cancel(); // Logging here is useful to help track down where in logs an error was // actually emitted. @@ -162,12 +336,18 @@ impl<'a> DiagnosticBuilder<'a> { /// /// 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) { - self.level = Level::Bug; - self.handler.delay_as_bug((*self.diagnostic).clone()); - self.cancel(); + 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 @@ -180,24 +360,16 @@ impl<'a> DiagnosticBuilder<'a> { /// 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<String>) -> &mut Self { - self.diagnostic.span_label(span, label); - self - } + pub fn span_label(&mut self, span: Span, label: impl Into<String>) -> &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, spans: impl IntoIterator<Item = Span>, label: impl AsRef<str>, - ) -> &mut Self { - let label = label.as_ref(); - for span in spans { - self.diagnostic.span_label(span, label); - } - self - } + ) -> &mut Self); forward!(pub fn note_expected_found( &mut self, @@ -224,19 +396,20 @@ impl<'a> DiagnosticBuilder<'a> { ) -> &mut Self); forward!(pub fn note(&mut self, msg: &str) -> &mut Self); - forward!(pub fn span_note<S: Into<MultiSpan>>( + forward!(pub fn span_note( &mut self, - sp: S, + sp: impl Into<MultiSpan>, msg: &str, ) -> &mut Self); forward!(pub fn warn(&mut self, msg: &str) -> &mut Self); - forward!(pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self); + forward!(pub fn span_warn(&mut self, sp: impl Into<MultiSpan>, msg: &str) -> &mut Self); forward!(pub fn help(&mut self, msg: &str) -> &mut Self); - forward!(pub fn span_help<S: Into<MultiSpan>>( + forward!(pub fn span_help( &mut self, - sp: S, + sp: impl Into<MultiSpan>, msg: &str, ) -> &mut Self); + forward!(pub fn help_use_latest_edition(&mut self,) -> &mut Self); forward!(pub fn set_is_lint(&mut self,) -> &mut Self); forward!(pub fn disable_suggestions(&mut self,) -> &mut Self); @@ -308,55 +481,35 @@ impl<'a> DiagnosticBuilder<'a> { applicability: Applicability, ) -> &mut Self); - forward!(pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self); - forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self); + forward!(pub fn set_primary_message(&mut self, msg: impl Into<String>) -> &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); - - /// Convenience function for internal use, clients should use one of the - /// `struct_*` methods on [`Handler`]. - crate fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> { - DiagnosticBuilder::new_with_code(handler, level, None, message) - } - - /// Convenience function for internal use, clients should use one of the - /// `struct_*` methods on [`Handler`]. - crate fn new_with_code( - handler: &'a Handler, - level: Level, - code: Option<DiagnosticId>, - message: &str, - ) -> DiagnosticBuilder<'a> { - let diagnostic = Diagnostic::new_with_code(level, code, message); - DiagnosticBuilder::new_diagnostic(handler, diagnostic) - } - - /// Creates a new `DiagnosticBuilder` with an already constructed - /// diagnostic. - crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> { - debug!("Created new diagnostic"); - DiagnosticBuilder { handler, diagnostic: Box::new(diagnostic) } - } } -impl<'a> Debug for DiagnosticBuilder<'a> { +impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.diagnostic.fmt(f) + self.inner.diagnostic.fmt(f) } } -/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or canceled +/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or cancelled /// or we emit a bug. -impl<'a> Drop for DiagnosticBuilder<'a> { +impl Drop for DiagnosticBuilderInner<'_> { fn drop(&mut self) { - if !panicking() && !self.cancelled() { - let mut db = DiagnosticBuilder::new( - self.handler, - Level::Bug, - "the following error was constructed but not emitted", - ); - db.emit(); - self.emit(); - panic!(); + match self.state { + // No `.emit()` or `.cancel()` calls. + DiagnosticBuilderState::Emittable(handler) => { + if !panicking() { + handler.emit_diagnostic(&Diagnostic::new( + Level::Bug, + "the following error was constructed but not emitted", + )); + handler.emit_diagnostic(&self.diagnostic); + panic!(); + } + } + // `.emit()` was previously called, or maybe we're during `.cancel()`. + DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} } } } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index bfed9211cec..52c44231d8f 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -408,8 +408,8 @@ pub trait Emitter { "this derive macro expansion".into() } ExpnKind::Macro(MacroKind::Bang, _) => "this macro invocation".into(), - ExpnKind::Inlined => "the inlined copy of this code".into(), - ExpnKind::Root => "in the crate root".into(), + ExpnKind::Inlined => "this inlined function call".into(), + ExpnKind::Root => "the crate root".into(), ExpnKind::AstPass(kind) => kind.descr().into(), ExpnKind::Desugaring(kind) => { format!("this {} desugaring", kind.descr()).into() diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index ff3478073d9..dc28d1bb452 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -454,8 +454,14 @@ impl DiagnosticSpan { let end = je.sm.lookup_char_pos(span.hi()); let backtrace_step = backtrace.next().map(|bt| { let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je); - let def_site_span = - Self::from_span_full(bt.def_site, false, None, None, [].into_iter(), je); + let def_site_span = Self::from_span_full( + je.sm.guess_head_span(bt.def_site), + false, + None, + None, + [].into_iter(), + je, + ); Box::new(DiagnosticSpanMacroExpansion { span: call_site, macro_decl_name: bt.kind.descr(), diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index c5b3d204407..ed01afe6e30 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -5,17 +5,12 @@ use rustc_span::source_map::{FilePathMapping, SourceMap}; use crate::emitter::{ColorConfig, HumanReadableErrorType}; use crate::Handler; -use rustc_serialize::json::decode; +use rustc_serialize::json; use rustc_span::{BytePos, Span}; use std::str; -#[derive(Decodable, Debug, PartialEq, Eq)] -struct TestData { - spans: Vec<SpanTestData>, -} - -#[derive(Decodable, Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] struct SpanTestData { pub byte_start: u32, pub byte_end: u32, @@ -41,8 +36,6 @@ impl<T: Write> Write for Shared<T> { /// Test the span yields correct positions in JSON. fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { - let expected_output = TestData { spans: vec![expected_output] }; - rustc_span::create_default_session_globals_then(|| { let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned()); @@ -64,9 +57,19 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { let bytes = output.lock().unwrap(); let actual_output = str::from_utf8(&bytes).unwrap(); - let actual_output: TestData = decode(actual_output); - - assert_eq!(expected_output, actual_output) + let actual_output = json::from_str(&actual_output).unwrap(); + let spans = actual_output["spans"].as_array().unwrap(); + assert_eq!(spans.len(), 1); + let obj = &spans[0]; + let actual_output = SpanTestData { + byte_start: obj["byte_start"].as_u64().unwrap() as u32, + byte_end: obj["byte_end"].as_u64().unwrap() as u32, + line_start: obj["line_start"].as_u64().unwrap() as u32, + line_end: obj["line_end"].as_u64().unwrap() as u32, + column_start: obj["column_start"].as_u64().unwrap() as u32, + column_end: obj["column_end"].as_u64().unwrap() as u32, + }; + assert_eq!(expected_output, actual_output); }) } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index a5c954cca13..217d3ec2c24 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -8,7 +8,9 @@ #![feature(if_let_guard)] #![feature(let_else)] #![feature(nll)] -#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))] +#![feature(adt_const_params)] +#![allow(incomplete_features)] +#![allow(rustc::potential_query_instability)] #[macro_use] extern crate rustc_macros; @@ -18,11 +20,12 @@ extern crate tracing; pub use emitter::ColorConfig; +use rustc_lint_defs::LintExpectationId; use Level::*; use emitter::{is_case_difference, Emitter, EmitterWriter}; use registry::Registry; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{self, Lock, Lrc}; use rustc_data_structures::AtomicRef; @@ -52,7 +55,7 @@ mod snippet; mod styled_buffer; pub use snippet::Style; -pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>; +pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a, ErrorGuaranteed>>; // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger. // (See also the comment on `DiagnosticBuilder`'s `diagnostic` field.) @@ -448,6 +451,22 @@ struct HandlerInner { deduplicated_warn_count: usize, future_breakage_diagnostics: Vec<Diagnostic>, + + /// Expected [`Diagnostic`]s store a [`LintExpectationId`] as part of + /// the lint level. [`LintExpectationId`]s created early during the compilation + /// (before `HirId`s have been defined) are not stable and can therefore not be + /// stored on disk. This buffer stores these diagnostics until the ID has been + /// replaced by a stable [`LintExpectationId`]. The [`Diagnostic`]s are the + /// submitted for storage and added to the list of fulfilled expectations. + unstable_expect_diagnostics: Vec<Diagnostic>, + + /// expected diagnostic will have the level `Expect` which additionally + /// carries the [`LintExpectationId`] of the expectation that can be + /// marked as fulfilled. This is a collection of all [`LintExpectationId`]s + /// that have been marked as fulfilled this way. + /// + /// [RFC-2383]: https://rust-lang.github.io/rfcs/2383-lint-reasons.html + fulfilled_expectations: FxHashSet<LintExpectationId>, } /// A key denoting where from a diagnostic was stashed. @@ -491,10 +510,15 @@ impl Drop for HandlerInner { self.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued"); } + // FIXME(eddyb) this explains what `delayed_good_path_bugs` are! + // They're `delayed_span_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() { let bugs = std::mem::replace(&mut self.delayed_good_path_bugs, Vec::new()); self.flush_delayed( - bugs.into_iter().map(DelayedDiagnostic::decorate).collect(), + bugs.into_iter().map(DelayedDiagnostic::decorate), "no warnings or errors encountered even though `delayed_good_path_bugs` issued", ); } @@ -563,6 +587,8 @@ impl Handler { emitted_diagnostics: Default::default(), stashed_diagnostics: Default::default(), future_breakage_diagnostics: Vec::new(), + unstable_expect_diagnostics: Vec::new(), + fulfilled_expectations: Default::default(), }), } } @@ -604,7 +630,7 @@ impl Handler { } /// 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<'_>> { + pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_, ()>> { self.inner .borrow_mut() .stashed_diagnostics @@ -617,33 +643,17 @@ impl Handler { self.inner.borrow_mut().emit_stashed_diagnostics(); } - /// Construct a dummy builder with `Level::Cancelled`. - /// - /// Using this will neither report anything to the user (e.g. a warning), - /// nor will compilation cancel as a result. - pub fn struct_dummy(&self) -> DiagnosticBuilder<'_> { - DiagnosticBuilder::new(self, Level::Cancelled, "") - } - - /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. - /// - /// The builder will be canceled if warnings cannot be emitted. - pub fn struct_span_warn(&self, span: impl Into<MultiSpan>, msg: &str) -> 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`. /// - /// This will "force" the warning meaning it will not be canceled even - /// if warnings cannot be emitted. - pub fn struct_span_force_warn( + /// Attempting to `.emit()` the builder will only emit if either: + /// * `can_emit_warnings` is `true` + /// * `is_force_warn` was set in `DiagnosticId::Lint` + pub fn struct_span_warn( &self, span: impl Into<MultiSpan>, msg: &str, - ) -> DiagnosticBuilder<'_> { - let mut result = self.struct_force_warn(msg); + ) -> DiagnosticBuilder<'_, ()> { + let mut result = self.struct_warn(msg); result.set_span(span); result } @@ -653,7 +663,7 @@ impl Handler { &self, span: impl Into<MultiSpan>, msg: &str, - ) -> DiagnosticBuilder<'_> { + ) -> DiagnosticBuilder<'_, ()> { let mut result = self.struct_allow(msg); result.set_span(span); result @@ -666,7 +676,7 @@ impl Handler { span: impl Into<MultiSpan>, msg: &str, code: DiagnosticId, - ) -> DiagnosticBuilder<'_> { + ) -> DiagnosticBuilder<'_, ()> { let mut result = self.struct_span_warn(span, msg); result.code(code); result @@ -674,30 +684,29 @@ impl Handler { /// Construct a builder at the `Warning` level with the `msg`. /// - /// The builder will be canceled if warnings cannot be emitted. - pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_> { - let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); - if !self.flags.can_emit_warnings { - result.cancel(); - } - result - } - - /// Construct a builder at the `Warning` level with the `msg`. - /// - /// This will "force" a warning meaning it will not be canceled even - /// if warnings cannot be emitted. - pub fn struct_force_warn(&self, msg: &str) -> DiagnosticBuilder<'_> { + /// Attempting to `.emit()` the builder will only emit if either: + /// * `can_emit_warnings` is `true` + /// * `is_force_warn` was set in `DiagnosticId::Lint` + pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Warning, msg) } /// Construct a builder at the `Allow` level with the `msg`. - pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_> { + pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Allow, msg) } + /// Construct a builder at the `Expect` level with the `msg`. + pub fn struct_expect(&self, msg: &str, id: LintExpectationId) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Level::Expect(id), msg) + } + /// Construct a builder at the `Error` level at the given `span` and with the `msg`. - pub fn struct_span_err(&self, span: impl Into<MultiSpan>, msg: &str) -> DiagnosticBuilder<'_> { + pub fn struct_span_err( + &self, + span: impl Into<MultiSpan>, + msg: &str, + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { let mut result = self.struct_err(msg); result.set_span(span); result @@ -709,7 +718,7 @@ impl Handler { span: impl Into<MultiSpan>, msg: &str, code: DiagnosticId, - ) -> DiagnosticBuilder<'_> { + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { let mut result = self.struct_span_err(span, msg); result.code(code); result @@ -717,18 +726,22 @@ impl Handler { /// Construct a builder at the `Error` level with the `msg`. // FIXME: This method should be removed (every error should have an associated error code). - pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_> { - DiagnosticBuilder::new(self, Level::Error { lint: false }, msg) + pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + DiagnosticBuilder::new_guaranteeing_error::<{ Level::Error { lint: false } }>(self, msg) } /// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors. #[doc(hidden)] - pub fn struct_err_lint(&self, msg: &str) -> DiagnosticBuilder<'_> { + pub fn struct_err_lint(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Error { lint: true }, msg) } /// Construct a builder at the `Error` level with the `msg` and the `code`. - pub fn struct_err_with_code(&self, msg: &str, code: DiagnosticId) -> DiagnosticBuilder<'_> { + pub fn struct_err_with_code( + &self, + msg: &str, + code: DiagnosticId, + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { let mut result = self.struct_err(msg); result.code(code); result @@ -739,7 +752,7 @@ impl Handler { &self, span: impl Into<MultiSpan>, msg: &str, - ) -> DiagnosticBuilder<'_> { + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { let mut result = self.struct_fatal(msg); result.set_span(span); result @@ -751,24 +764,24 @@ impl Handler { span: impl Into<MultiSpan>, msg: &str, code: DiagnosticId, - ) -> DiagnosticBuilder<'_> { + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { let mut result = self.struct_span_fatal(span, msg); result.code(code); result } /// Construct a builder at the `Error` level with the `msg`. - pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_> { - DiagnosticBuilder::new(self, Level::Fatal, msg) + pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + DiagnosticBuilder::new_guaranteeing_error::<{ Level::Fatal }>(self, msg) } /// Construct a builder at the `Help` level with the `msg`. - pub fn struct_help(&self, msg: &str) -> DiagnosticBuilder<'_> { + pub fn struct_help(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Help, msg) } /// Construct a builder at the `Note` level with the `msg`. - pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_> { + pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Note, msg) } @@ -815,6 +828,8 @@ impl Handler { self.inner.borrow_mut().delay_span_bug(span, msg) } + // 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). pub fn delay_good_path_bug(&self, msg: &str) { self.inner.borrow_mut().delay_good_path_bug(msg) } @@ -827,7 +842,7 @@ impl Handler { self.emit_diag_at_span(Diagnostic::new(Note, msg), span); } - pub fn span_note_diag(&self, span: Span, msg: &str) -> DiagnosticBuilder<'_> { + pub fn span_note_diag(&self, span: Span, msg: &str) -> DiagnosticBuilder<'_, ()> { let mut db = DiagnosticBuilder::new(self, Note, msg); db.set_span(span); db @@ -916,8 +931,46 @@ impl Handler { self.inner.borrow_mut().emit_unused_externs(lint_level, unused_externs) } - pub fn delay_as_bug(&self, diagnostic: Diagnostic) { - self.inner.borrow_mut().delay_as_bug(diagnostic) + pub fn update_unstable_expectation_id( + &self, + unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>, + ) { + let diags = std::mem::take(&mut self.inner.borrow_mut().unstable_expect_diagnostics); + if diags.is_empty() { + return; + } + + let mut inner = self.inner.borrow_mut(); + for mut diag in diags.into_iter() { + let mut unstable_id = diag + .level + .get_expectation_id() + .expect("all diagnostics inside `unstable_expect_diagnostics` must have a `LintExpectationId`"); + + // The unstable to stable map only maps the unstable `AttrId` to a stable `HirId` with an attribute index. + // The lint index inside the attribute is manually transferred here. + let lint_index = unstable_id.get_lint_index(); + unstable_id.set_lint_index(None); + let mut stable_id = *unstable_to_stable + .get(&unstable_id) + .expect("each unstable `LintExpectationId` must have a matching stable id"); + + stable_id.set_lint_index(lint_index); + diag.level = Level::Expect(stable_id); + inner.fulfilled_expectations.insert(stable_id); + + (*TRACK_DIAGNOSTICS)(&diag); + } + } + + /// This methods steals all [`LintExpectationId`]s that are stored inside + /// [`HandlerInner`] and indicate that the linked expectation has been fulfilled. + 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", + ); + std::mem::take(&mut self.inner.borrow_mut().fulfilled_expectations) } } @@ -936,9 +989,18 @@ impl HandlerInner { diags.iter().for_each(|diag| self.emit_diagnostic(diag)); } + // FIXME(eddyb) this should ideally take `diagnostic` by value. fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) { - if diagnostic.cancelled() { - return; + if diagnostic.level == Level::DelayedBug { + // FIXME(eddyb) this should check for `has_errors` and stop pushing + // once *any* errors were emitted (and truncate `delayed_span_bugs` + // when an error is first emitted, also), but maybe there's a case + // in which that's not sound? otherwise this is really inefficient. + self.delayed_span_bugs.push(diagnostic.clone()); + + if !self.flags.report_delayed_bugs { + return; + } } if diagnostic.has_future_breakage() { @@ -955,9 +1017,21 @@ impl HandlerInner { return; } + // 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 + // a stable one by the `LintLevelsBuilder`. + if let Level::Expect(LintExpectationId::Unstable { .. }) = diagnostic.level { + self.unstable_expect_diagnostics.push(diagnostic.clone()); + return; + } + (*TRACK_DIAGNOSTICS)(diagnostic); - if diagnostic.level == Allow { + if let Level::Expect(expectation_id) = diagnostic.level { + self.fulfilled_expectations.insert(expectation_id); + return; + } else if diagnostic.level == Allow { return; } @@ -1119,14 +1193,16 @@ impl HandlerInner { // FIXME: don't abort here if report_delayed_bugs is off self.span_bug(sp, msg); } - let mut diagnostic = Diagnostic::new(Level::Bug, msg); + let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg); diagnostic.set_span(sp.into()); diagnostic.note(&format!("delayed at {}", std::panic::Location::caller())); - self.delay_as_bug(diagnostic) + self.emit_diagnostic(&diagnostic) } + // 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: &str) { - let diagnostic = Diagnostic::new(Level::Bug, msg); + let diagnostic = Diagnostic::new(Level::DelayedBug, msg); if self.flags.report_delayed_bugs { self.emit_diagnostic(&diagnostic); } @@ -1160,20 +1236,34 @@ impl HandlerInner { panic::panic_any(ExplicitBug); } - fn delay_as_bug(&mut self, diagnostic: Diagnostic) { - if self.flags.report_delayed_bugs { - self.emit_diagnostic(&diagnostic); - } - self.delayed_span_bugs.push(diagnostic); - } + fn flush_delayed(&mut self, bugs: impl IntoIterator<Item = Diagnostic>, explanation: &str) { + let mut no_bugs = true; + for mut bug in bugs { + if no_bugs { + // Put the overall explanation before the `DelayedBug`s, to + // frame them better (e.g. separate warnings from them). + self.emit_diagnostic(&Diagnostic::new(Bug, explanation)); + no_bugs = false; + } + + // "Undelay" the `DelayedBug`s (into plain `Bug`s). + if bug.level != Level::DelayedBug { + // NOTE(eddyb) not panicking here because we're already producing + // an ICE, and the more information the merrier. + bug.note(&format!( + "`flushed_delayed` got diagnostic with level {:?}, \ + instead of the expected `DelayedBug`", + bug.level, + )); + } + bug.level = Level::Bug; - fn flush_delayed(&mut self, bugs: Vec<Diagnostic>, explanation: &str) { - let has_bugs = !bugs.is_empty(); - for bug in bugs { self.emit_diagnostic(&bug); } - if has_bugs { - panic!("{}", explanation); + + // Panic with `ExplicitBug` to avoid "unexpected panic" messages. + if !no_bugs { + panic::panic_any(ExplicitBug); } } @@ -1224,9 +1314,10 @@ impl DelayedDiagnostic { } } -#[derive(Copy, PartialEq, Clone, Hash, Debug, Encodable, Decodable)] +#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)] pub enum Level { Bug, + DelayedBug, Fatal, Error { /// If this error comes from a lint, don't abort compilation even when abort_if_errors() is called. @@ -1235,9 +1326,9 @@ pub enum Level { Warning, Note, Help, - Cancelled, FailureNote, Allow, + Expect(LintExpectationId), } impl fmt::Display for Level { @@ -1250,7 +1341,7 @@ impl Level { fn color(self) -> ColorSpec { let mut spec = ColorSpec::new(); match self { - Bug | Fatal | Error { .. } => { + Bug | DelayedBug | Fatal | Error { .. } => { spec.set_fg(Some(Color::Red)).set_intense(true); } Warning => { @@ -1263,32 +1354,40 @@ impl Level { spec.set_fg(Some(Color::Cyan)).set_intense(true); } FailureNote => {} - Allow | Cancelled => unreachable!(), + Allow | Expect(_) => unreachable!(), } spec } pub fn to_str(self) -> &'static str { match self { - Bug => "error: internal compiler error", + Bug | DelayedBug => "error: internal compiler error", Fatal | Error { .. } => "error", Warning => "warning", Note => "note", Help => "help", FailureNote => "failure-note", - Cancelled => panic!("Shouldn't call on cancelled error"), Allow => panic!("Shouldn't call on allowed error"), + Expect(_) => panic!("Shouldn't call on expected error"), } } pub fn is_failure_note(&self) -> bool { matches!(*self, FailureNote) } + + pub fn get_expectation_id(&self) -> Option<LintExpectationId> { + match self { + Level::Expect(id) => Some(*id), + _ => None, + } + } } +// FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite. pub fn add_elided_lifetime_in_path_suggestion( source_map: &SourceMap, - db: &mut DiagnosticBuilder<'_>, + diag: &mut Diagnostic, n: usize, path_span: Span, incl_angl_brckt: bool, @@ -1320,7 +1419,7 @@ pub fn add_elided_lifetime_in_path_suggestion( (insertion_span, anon_lts) } }; - db.span_suggestion( + diag.span_suggestion( replace_span, &format!("indicate the anonymous lifetime{}", pluralize!(n)), suggestion, @@ -1331,6 +1430,6 @@ pub fn add_elided_lifetime_in_path_suggestion( // Useful type to use with `Result<>` indicate that an error has already // been reported to the user, so no need to continue checking. #[derive(Clone, Copy, Debug, Encodable, Decodable, Hash, PartialEq, Eq)] -pub struct ErrorReported; +pub struct ErrorGuaranteed; -rustc_data_structures::impl_stable_hash_via_hash!(ErrorReported); +rustc_data_structures::impl_stable_hash_via_hash!(ErrorGuaranteed); |
