From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_errors/src/diagnostic_builder.rs | 616 ++++++++++++++++++++++++ 1 file changed, 616 insertions(+) create mode 100644 compiler/rustc_errors/src/diagnostic_builder.rs (limited to 'compiler/rustc_errors/src/diagnostic_builder.rs') diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs new file mode 100644 index 000000000..9e68ee282 --- /dev/null +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -0,0 +1,616 @@ +use crate::diagnostic::IntoDiagnosticArg; +use crate::{ + Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed, + SubdiagnosticMessage, +}; +use crate::{Handler, Level, MultiSpan, StashKey}; +use rustc_lint_defs::Applicability; + +use rustc_span::Span; +use std::borrow::Cow; +use std::fmt::{self, Debug}; +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; +use std::thread::panicking; +use tracing::debug; + +/// Used for emitting structured error messages and other diagnostic information. +/// +/// 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`. +#[must_use] +#[derive(Clone)] +pub struct DiagnosticBuilder<'a, G: EmissionGuarantee> { + inner: DiagnosticBuilderInner<'a>, + _marker: PhantomData, +} + +/// This type exists only for `DiagnosticBuilder::forget_guarantee`, because it: +/// 1. lacks the `G` parameter and therefore `DiagnosticBuilder` can be +/// converted into `DiagnosticBuilder` 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, +} + +#[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. + pub(crate) trait IsError {} + + 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`]. + pub(crate) fn new_guaranteeing_error, const L: Level>( + handler: &'a Handler, + message: M, + ) -> Self + where + (): sealed_level_is_error::IsError, + { + 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; + + 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`", + 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`'s diagnostic \ + became non-error ({:?}), after original `.emit()`", + db.inner.diagnostic.level, + ); + ErrorGuaranteed::unchecked_claim_error_was_emitted() + } + } + } +} + +impl<'a> DiagnosticBuilder<'a, ()> { + /// Convenience function for internal use, clients should use one of the + /// `struct_*` methods on [`Handler`]. + pub(crate) fn new>( + handler: &'a Handler, + level: Level, + message: M, + ) -> Self { + let diagnostic = Diagnostic::new_with_code(level, None, message); + Self::new_diagnostic(handler, diagnostic) + } + + /// Creates a new `DiagnosticBuilder` with an already constructed + /// diagnostic. + 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, + } + } +} + +// FIXME(eddyb) should there be a `Option` 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(&mut db.inner.diagnostic); + } + // `.emit()` was previously called, disallowed from repeating it. + DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} + } + } +} + +impl<'a> DiagnosticBuilder<'a, !> { + /// Convenience function for internal use, clients should use one of the + /// `struct_*` methods on [`Handler`]. + pub(crate) fn new_fatal(handler: &'a Handler, message: impl Into) -> 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, + } + } +} + +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(&mut db.inner.diagnostic); + } + // `.emit()` was previously called, disallowed from repeating it. + DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} + } + // Then fatally error, returning `!` + crate::FatalError.raise() + } +} + +/// 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. +macro_rules! forward { + // Forward pattern for &mut self -> &mut Self + ( + $(#[$attrs:meta])* + pub fn $n:ident(&mut self, $($name:ident: $ty:ty),* $(,)?) -> &mut Self + ) => { + $(#[$attrs])* + #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")] + pub fn $n(&mut self, $($name: $ty),*) -> &mut Self { + self.inner.diagnostic.$n($($name),*); + self + } + }; +} + +impl Deref for DiagnosticBuilder<'_, G> { + type Target = Diagnostic; + + fn deref(&self) -> &Diagnostic { + &self.inner.diagnostic + } +} + +impl DerefMut for DiagnosticBuilder<'_, G> { + fn deref_mut(&mut self) -> &mut Diagnostic { + &mut self.inner.diagnostic + } +} + +impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { + /// Emit the diagnostic. + #[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. + #[track_caller] + pub fn emit_unless(&mut self, delay: bool) -> G { + if delay { + 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, + /// later stage of the compiler. The diagnostic can be accessed with + /// the provided `span` and `key` through [`Handler::steal_diagnostic()`]. + /// + /// As with `buffer`, this is unless the handler 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); + } + } + + /// 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.flags.dont_buffer_diagnostics || handler.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::Str("".to_string())); + let diagnostic = std::mem::replace(&mut *self.inner.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. + debug!("buffer: diagnostic={:?}", diagnostic); + + Some((diagnostic, handler)) + } + + /// Buffers the diagnostic for later emission, + /// unless handler has disabled such buffering. + pub fn buffer(self, buffered_diagnostics: &mut Vec) { + buffered_diagnostics.extend(self.into_diagnostic().map(|(diag, _)| diag)); + } + + /// 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 delay_as_bug(&mut self) { + 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) -> &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, + label: impl AsRef, + ) -> &mut Self); + + forward!(pub fn note_expected_found( + &mut self, + 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, + 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) -> &mut Self); + forward!(pub fn note_once(&mut self, msg: impl Into) -> &mut Self); + forward!(pub fn span_note( + &mut self, + sp: impl Into, + msg: impl Into, + ) -> &mut Self); + forward!(pub fn span_note_once( + &mut self, + sp: impl Into, + msg: impl Into, + ) -> &mut Self); + forward!(pub fn warn(&mut self, msg: impl Into) -> &mut Self); + forward!(pub fn span_warn( + &mut self, + sp: impl Into, + msg: impl Into, + ) -> &mut Self); + forward!(pub fn help(&mut self, msg: impl Into) -> &mut Self); + forward!(pub fn span_help( + &mut self, + sp: impl Into, + msg: impl Into, + ) -> &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); + forward!(pub fn clear_suggestions(&mut self,) -> &mut Self); + + forward!(pub fn multipart_suggestion( + &mut self, + msg: impl Into, + suggestion: Vec<(Span, String)>, + applicability: Applicability, + ) -> &mut Self); + forward!(pub fn multipart_suggestion_verbose( + &mut self, + msg: impl Into, + suggestion: Vec<(Span, String)>, + applicability: Applicability, + ) -> &mut Self); + forward!(pub fn tool_only_multipart_suggestion( + &mut self, + msg: impl Into, + suggestion: Vec<(Span, String)>, + applicability: Applicability, + ) -> &mut Self); + forward!(pub fn span_suggestion( + &mut self, + sp: Span, + msg: impl Into, + suggestion: impl ToString, + applicability: Applicability, + ) -> &mut Self); + forward!(pub fn span_suggestions( + &mut self, + sp: Span, + msg: impl Into, + suggestions: impl Iterator, + applicability: Applicability, + ) -> &mut Self); + forward!(pub fn multipart_suggestions( + &mut self, + msg: impl Into, + suggestions: impl Iterator>, + applicability: Applicability, + ) -> &mut Self); + forward!(pub fn span_suggestion_short( + &mut self, + sp: Span, + msg: impl Into, + suggestion: impl ToString, + applicability: Applicability, + ) -> &mut Self); + forward!(pub fn span_suggestion_verbose( + &mut self, + sp: Span, + msg: impl Into, + suggestion: impl ToString, + applicability: Applicability, + ) -> &mut Self); + forward!(pub fn span_suggestion_hidden( + &mut self, + sp: Span, + msg: impl Into, + suggestion: impl ToString, + applicability: Applicability, + ) -> &mut Self); + forward!(pub fn tool_only_span_suggestion( + &mut self, + sp: Span, + msg: impl Into, + suggestion: impl ToString, + applicability: Applicability, + ) -> &mut Self); + + forward!(pub fn set_primary_message(&mut self, msg: impl Into) -> &mut Self); + forward!(pub fn set_span(&mut self, sp: impl Into) -> &mut Self); + forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self); + forward!(pub fn set_arg( + &mut self, + name: impl Into>, + arg: impl IntoDiagnosticArg, + ) -> &mut Self); + + forward!(pub fn subdiagnostic( + &mut self, + subdiagnostic: impl crate::AddSubdiagnostic + ) -> &mut Self); +} + +impl Debug for DiagnosticBuilder<'_, G> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.diagnostic.fmt(f) + } +} + +/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or cancelled +/// or we emit a bug. +impl Drop for DiagnosticBuilderInner<'_> { + 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::Str( + "the following error was constructed but not emitted".to_string(), + ), + )); + handler.emit_diagnostic(&mut self.diagnostic); + panic!(); + } + } + // `.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( + $span, + &format!($($message)*), + $crate::error_code!($code), + ) + }) +} + +#[macro_export] +macro_rules! error_code { + ($code:ident) => {{ $crate::DiagnosticId::Error(stringify!($code).to_owned()) }}; +} + +/// Wrapper around a `DiagnosticBuilder` for creating lints. +pub struct LintDiagnosticBuilder<'a, G: EmissionGuarantee>(DiagnosticBuilder<'a, G>); + +impl<'a, G: EmissionGuarantee> LintDiagnosticBuilder<'a, G> { + #[rustc_lint_diagnostics] + /// Return the inner `DiagnosticBuilder`, first setting the primary message to `msg`. + pub fn build(mut self, msg: impl Into) -> DiagnosticBuilder<'a, G> { + self.0.set_primary_message(msg); + self.0.set_is_lint(); + self.0 + } + + /// Create a `LintDiagnosticBuilder` from some existing `DiagnosticBuilder`. + pub fn new(err: DiagnosticBuilder<'a, G>) -> LintDiagnosticBuilder<'a, G> { + LintDiagnosticBuilder(err) + } +} + +impl<'a> LintDiagnosticBuilder<'a, ErrorGuaranteed> { + pub fn forget_guarantee(self) -> LintDiagnosticBuilder<'a, ()> { + LintDiagnosticBuilder(self.0.forget_guarantee()) + } +} -- cgit v1.2.3