Reduce capabilities of Diagnostic.

Currently many diagnostic modifier methods are available on both
`Diagnostic` and `DiagnosticBuilder`. This commit removes most of them
from `Diagnostic`. To minimize the diff size, it keeps them within
`diagnostic.rs` but changes the surrounding `impl Diagnostic` block to
`impl DiagnosticBuilder`. (I intend to move things around later, to give
a more sensible code layout.)

`Diagnostic` keeps a few methods that it still needs, like `sub`,
`arg`, and `replace_args`.

The `forward!` macro, which defined two additional methods per call
(e.g. `note` and `with_note`), is replaced by the `with_fn!` macro,
which defines one additional method per call (e.g. `with_note`). It's
now also only used when necessary -- not all modifier methods currently
need a `with_*` form. (New ones can be easily added as necessary.)

All this also requires changing `trait AddToDiagnostic` so its methods
take `DiagnosticBuilder` instead of `Diagnostic`, which leads to many
mechanical changes. `SubdiagnosticMessageOp` gains a type parameter `G`.

There are three subdiagnostics -- `DelayedAtWithoutNewline`,
`DelayedAtWithNewline`, and `InvalidFlushedDelayedDiagnosticLevel` --
that are created within the diagnostics machinery and appended to
external diagnostics. These are handled at the `Diagnostic` level, which
means it's now hard to construct them via `derive(Diagnostic)`, so
instead we construct them by hand. This has no effect on what they look
like when printed.

There are lots of new `allow` markers for `untranslatable_diagnostics`
and `diagnostics_outside_of_impl`. This is because
`#[rustc_lint_diagnostics]` annotations were present on the `Diagnostic`
modifier methods, but missing from the `DiagnosticBuilder` modifier
methods. They're now present.
This commit is contained in:
Nicholas Nethercote 2024-02-06 16:44:30 +11:00
parent b18f3e11fa
commit f6f8779843
40 changed files with 502 additions and 395 deletions

View file

@ -12,6 +12,7 @@ use rustc_span::{Span, DUMMY_SP};
use std::borrow::Cow;
use std::fmt::{self, Debug};
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use std::panic::Location;
/// Error type for `Diagnostic`'s `suggestions` field, indicating that
@ -71,17 +72,21 @@ where
Self: Sized,
{
/// Add a subdiagnostic to an existing diagnostic.
fn add_to_diagnostic(self, diag: &mut Diagnostic) {
fn add_to_diagnostic<G: EmissionGuarantee>(self, diag: &mut DiagnosticBuilder<'_, G>) {
self.add_to_diagnostic_with(diag, |_, m| m);
}
/// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used
/// (to optionally perform eager translation).
fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F);
fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
self,
diag: &mut DiagnosticBuilder<'_, G>,
f: F,
);
}
pub trait SubdiagnosticMessageOp =
Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage;
pub trait SubdiagnosticMessageOp<G> =
Fn(&mut DiagnosticBuilder<'_, G>, SubdiagnosticMessage) -> SubdiagnosticMessage;
/// Trait implemented by lint types. This should not be implemented manually. Instead, use
/// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic].
@ -93,6 +98,10 @@ pub trait DecorateLint<'a, G: EmissionGuarantee> {
fn msg(&self) -> DiagnosticMessage;
}
/// The main part of a diagnostic. Note that `DiagnosticBuilder`, which wraps
/// this type, is used for most operations, and should be used instead whenever
/// possible. This type should only be used when `DiagnosticBuilder`'s lifetime
/// causes difficulties, e.g. when storing diagnostics within `DiagCtxt`.
#[must_use]
#[derive(Clone, Debug, Encodable, Decodable)]
pub struct Diagnostic {
@ -289,6 +298,90 @@ impl Diagnostic {
}
}
// See comment on `DiagnosticBuilder::subdiagnostic_message_to_diagnostic_message`.
pub(crate) fn subdiagnostic_message_to_diagnostic_message(
&self,
attr: impl Into<SubdiagnosticMessage>,
) -> DiagnosticMessage {
let msg =
self.messages.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages");
msg.with_subdiagnostic_message(attr.into())
}
pub(crate) fn sub(
&mut self,
level: Level,
message: impl Into<SubdiagnosticMessage>,
span: MultiSpan,
) {
let sub = SubDiagnostic {
level,
messages: vec![(
self.subdiagnostic_message_to_diagnostic_message(message),
Style::NoStyle,
)],
span,
};
self.children.push(sub);
}
pub(crate) fn arg(&mut self, name: impl Into<DiagnosticArgName>, arg: impl IntoDiagnosticArg) {
self.args.insert(name.into(), arg.into_diagnostic_arg());
}
pub fn args(&self) -> impl Iterator<Item = DiagnosticArg<'_>> {
self.args.iter()
}
pub fn replace_args(&mut self, args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>) {
self.args = args;
}
}
/// `DiagnosticBuilder` impls many `&mut self -> &mut Self` methods. Each one
/// modifies an existing diagnostic, 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);`.
///
/// This macro creates an equivalent `self -> Self` method, with a `with_`
/// prefix. This can be used in a chained fashion when making a new diagnostic,
/// e.g. `let err = struct_err(msg).with_code(code);`, or emitting a new
/// diagnostic, e.g. `struct_err(msg).with_code(code).emit();`.
///
/// Although the latter method can be used to modify an existing diagnostic,
/// e.g. `err = err.with_code(code);`, this should be avoided because the former
/// method gives shorter code, e.g. `err.code(code);`.
///
/// Note: the `with_` methods are added only when needed. If you want to use
/// one and it's not defined, feel free to add it.
///
/// Note: any doc comments must be within the `with_fn!` call.
macro_rules! with_fn {
{
$with_f:ident,
$(#[$attrs:meta])*
pub fn $f:ident(&mut $self:ident, $($name:ident: $ty:ty),* $(,)?) -> &mut Self {
$($body:tt)*
}
} => {
// The original function.
$(#[$attrs])*
#[doc = concat!("See [`DiagnosticBuilder::", stringify!($f), "()`].")]
pub fn $f(&mut $self, $($name: $ty),*) -> &mut Self {
$($body)*
}
// The `with_*` variant.
$(#[$attrs])*
#[doc = concat!("See [`DiagnosticBuilder::", stringify!($f), "()`].")]
pub fn $with_f(mut $self, $($name: $ty),*) -> Self {
$self.$f($($name),*);
$self
}
};
}
impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
/// Delay emission of this diagnostic as a bug.
///
/// This can be useful in contexts where an error indicates a bug but
@ -309,6 +402,7 @@ impl Diagnostic {
self.level = Level::DelayedBug;
}
with_fn! { with_span_label,
/// Appends a labeled span to the diagnostic.
///
/// Labels are used to convey additional context for the diagnostic's primary span. They will
@ -323,10 +417,12 @@ impl Diagnostic {
/// 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));
let msg = self.subdiagnostic_message_to_diagnostic_message(label);
self.span.push_span_label(span, msg);
self
}
} }
with_fn! { with_span_labels,
/// 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: &str) -> &mut Self {
@ -334,7 +430,7 @@ impl Diagnostic {
self.span_label(span, label.to_string());
}
self
}
} }
pub fn replace_span_with(&mut self, after: Span, keep_label: bool) -> &mut Self {
let before = self.span.clone();
@ -412,39 +508,40 @@ impl Diagnostic {
self
}
with_fn! { with_note,
/// 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());
self
}
} }
fn highlighted_note(&mut self, msg: Vec<StringPart>) -> &mut Self {
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.
/// This is like [`DiagnosticBuilder::note()`], but it's only printed once.
pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
self.sub(Level::OnceNote, msg, MultiSpan::new());
self
}
with_fn! { with_span_note,
/// Prints the span with a note above it.
/// This is like [`Diagnostic::note()`], but it gets its own span.
/// This is like [`DiagnosticBuilder::note()`], but it gets its own span.
#[rustc_lint_diagnostics]
pub fn span_note<S: Into<MultiSpan>>(
pub fn span_note(
&mut self,
sp: S,
sp: impl Into<MultiSpan>,
msg: impl Into<SubdiagnosticMessage>,
) -> &mut Self {
self.sub(Level::Note, msg, sp.into());
self
}
} }
/// Prints the span with a note above it.
/// This is like [`Diagnostic::note()`], but it gets its own span.
/// This is like [`DiagnosticBuilder::note_once()`], but it gets its own span.
pub fn span_note_once<S: Into<MultiSpan>>(
&mut self,
sp: S,
@ -454,15 +551,16 @@ impl Diagnostic {
self
}
with_fn! { with_warn,
/// 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, msg, MultiSpan::new());
self
}
} }
/// Prints the span with a warning above it.
/// This is like [`Diagnostic::warn()`], but it gets its own span.
/// This is like [`DiagnosticBuilder::warn()`], but it gets its own span.
#[rustc_lint_diagnostics]
pub fn span_warn<S: Into<MultiSpan>>(
&mut self,
@ -473,15 +571,15 @@ impl Diagnostic {
self
}
with_fn! { with_help,
/// 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());
self
}
} }
/// Prints the span with a help above it.
/// This is like [`Diagnostic::help()`], but it gets its own span.
/// This is like [`DiagnosticBuilder::help()`], but it's only printed once.
pub fn help_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
self.sub(Level::OnceHelp, msg, MultiSpan::new());
self
@ -494,7 +592,7 @@ impl Diagnostic {
}
/// Prints the span with some help above it.
/// This is like [`Diagnostic::help()`], but it gets its own span.
/// This is like [`DiagnosticBuilder::help()`], but it gets its own span.
#[rustc_lint_diagnostics]
pub fn span_help<S: Into<MultiSpan>>(
&mut self,
@ -531,6 +629,7 @@ impl Diagnostic {
}
}
with_fn! { with_multipart_suggestion,
/// Show a suggestion that has multiple parts to it.
/// In other words, multiple changes need to be applied as part of this suggestion.
pub fn multipart_suggestion(
@ -545,7 +644,7 @@ impl Diagnostic {
applicability,
SuggestionStyle::ShowCode,
)
}
} }
/// Show a suggestion that has multiple parts to it, always as it's own subdiagnostic.
/// In other words, multiple changes need to be applied as part of this suggestion.
@ -562,7 +661,8 @@ impl Diagnostic {
SuggestionStyle::ShowAlways,
)
}
/// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`].
/// [`DiagnosticBuilder::multipart_suggestion()`] but you can set the [`SuggestionStyle`].
pub fn multipart_suggestion_with_style(
&mut self,
msg: impl Into<SubdiagnosticMessage>,
@ -619,6 +719,7 @@ impl Diagnostic {
)
}
with_fn! { with_span_suggestion,
/// Prints out a message with a suggested edit of the code.
///
/// In case of short messages and a simple suggestion, rustc displays it as a label:
@ -651,9 +752,9 @@ impl Diagnostic {
SuggestionStyle::ShowCode,
);
self
}
} }
/// [`Diagnostic::span_suggestion()`] but you can set the [`SuggestionStyle`].
/// [`DiagnosticBuilder::span_suggestion()`] but you can set the [`SuggestionStyle`].
pub fn span_suggestion_with_style(
&mut self,
sp: Span,
@ -677,6 +778,7 @@ impl Diagnostic {
self
}
with_fn! { with_span_suggestion_verbose,
/// Always show the suggested change.
pub fn span_suggestion_verbose(
&mut self,
@ -693,10 +795,11 @@ impl Diagnostic {
SuggestionStyle::ShowAlways,
);
self
}
} }
with_fn! { with_span_suggestions,
/// Prints out a message with multiple suggested edits of the code.
/// See also [`Diagnostic::span_suggestion()`].
/// See also [`DiagnosticBuilder::span_suggestion()`].
pub fn span_suggestions(
&mut self,
sp: Span,
@ -711,9 +814,8 @@ impl Diagnostic {
applicability,
SuggestionStyle::ShowCode,
)
}
} }
/// [`Diagnostic::span_suggestions()`] but you can set the [`SuggestionStyle`].
pub fn span_suggestions_with_style(
&mut self,
sp: Span,
@ -743,7 +845,7 @@ impl Diagnostic {
/// Prints out a message with multiple suggested edits of the code, where each edit consists of
/// multiple parts.
/// See also [`Diagnostic::multipart_suggestion()`].
/// See also [`DiagnosticBuilder::multipart_suggestion()`].
pub fn multipart_suggestions(
&mut self,
msg: impl Into<SubdiagnosticMessage>,
@ -785,6 +887,7 @@ impl Diagnostic {
self
}
with_fn! { with_span_suggestion_short,
/// Prints out a message with a suggested edit of the code. If the suggestion is presented
/// inline, it will only show the message and not the suggestion.
///
@ -804,7 +907,7 @@ impl Diagnostic {
SuggestionStyle::HideCodeInline,
);
self
}
} }
/// Prints out a message for a suggestion without showing the suggested code.
///
@ -829,6 +932,7 @@ impl Diagnostic {
self
}
with_fn! { with_tool_only_span_suggestion,
/// Adds a suggestion to the JSON output that will not be shown in the CLI.
///
/// This is intended to be used for suggestions that are *very* obvious in what the changes
@ -849,7 +953,7 @@ impl Diagnostic {
SuggestionStyle::CompletelyHidden,
);
self
}
} }
/// Add a subdiagnostic from a type that implements `Subdiagnostic` (see
/// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages
@ -868,45 +972,45 @@ impl Diagnostic {
self
}
pub fn span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
with_fn! { with_span,
/// Add a span.
pub fn span(&mut self, sp: impl Into<MultiSpan>) -> &mut Self {
self.span = sp.into();
if let Some(span) = self.span.primary_span() {
self.sort_span = span;
}
self
}
} }
pub fn is_lint(&mut self, name: String, has_future_breakage: bool) -> &mut Self {
self.is_lint = Some(IsLint { name, has_future_breakage });
self
}
with_fn! { with_code,
/// Add an error code.
pub fn code(&mut self, code: ErrCode) -> &mut Self {
self.code = Some(code);
self
}
} }
with_fn! { with_primary_message,
/// Add a primary message.
pub fn primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
self.messages[0] = (msg.into(), Style::NoStyle);
self
}
pub fn args(&self) -> impl Iterator<Item = DiagnosticArg<'_>> {
self.args.iter()
}
} }
with_fn! { with_arg,
/// Add an argument.
pub fn arg(
&mut self,
name: impl Into<DiagnosticArgName>,
arg: impl IntoDiagnosticArg,
) -> &mut Self {
self.args.insert(name.into(), arg.into_diagnostic_arg());
self.deref_mut().arg(name, arg);
self
}
pub fn replace_args(&mut self, args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>) {
self.args = args;
}
} }
/// 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
@ -915,9 +1019,7 @@ impl Diagnostic {
&self,
attr: impl Into<SubdiagnosticMessage>,
) -> DiagnosticMessage {
let msg =
self.messages.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages");
msg.with_subdiagnostic_message(attr.into())
self.deref().subdiagnostic_message_to_diagnostic_message(attr)
}
/// Convenience function for internal use, clients should use one of the
@ -925,15 +1027,7 @@ impl Diagnostic {
///
/// Used by `proc_macro_server` for implementing `server::Diagnostic`.
pub fn sub(&mut self, level: Level, message: impl Into<SubdiagnosticMessage>, span: MultiSpan) {
let sub = SubDiagnostic {
level,
messages: vec![(
self.subdiagnostic_message_to_diagnostic_message(message),
Style::NoStyle,
)],
span,
};
self.children.push(sub);
self.deref_mut().sub(level, message, span);
}
/// Convenience function for internal use, clients should use one of the
@ -946,7 +1040,9 @@ impl Diagnostic {
let sub = SubDiagnostic { level, messages, span };
self.children.push(sub);
}
}
impl Diagnostic {
/// Fields used for Hash, and PartialEq trait
fn keys(
&self,

View file

@ -1,14 +1,8 @@
use crate::diagnostic::IntoDiagnosticArg;
use crate::{DiagCtxt, Level, MultiSpan, StashKey};
use crate::{
Diagnostic, DiagnosticMessage, DiagnosticStyledString, ErrCode, ErrorGuaranteed, ExplicitBug,
SubdiagnosticMessage,
DiagCtxt, Diagnostic, DiagnosticMessage, ErrorGuaranteed, ExplicitBug, Level, StashKey,
};
use rustc_lint_defs::Applicability;
use rustc_span::source_map::Spanned;
use rustc_span::Span;
use std::borrow::Cow;
use std::fmt::{self, Debug};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
@ -35,6 +29,11 @@ where
}
/// Used for emitting structured error messages and other diagnostic information.
/// Wraps a `Diagnostic`, adding some useful things.
/// - The `dcx` field, allowing it to (a) emit itself, and (b) do a drop check
/// that it has been emitted or cancelled.
/// - The `EmissionGuarantee`, which determines the type returned from `emit`.
///
/// 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
@ -56,9 +55,11 @@ pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> {
/// 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>>,
// FIXME(nnethercote) Make private once this moves to diagnostic.rs.
pub(crate) diag: Option<Box<Diagnostic>>,
_marker: PhantomData<G>,
// FIXME(nnethercote) Make private once this moves to diagnostic.rs.
pub(crate) _marker: PhantomData<G>,
}
// Cloning a `DiagnosticBuilder` is a recipe for a diagnostic being emitted
@ -88,18 +89,21 @@ 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 {
// FIXME(nnethercote) Make private once this moves to diagnostic.rs.
pub(crate) fn take_diag(&mut self) -> Diagnostic {
Box::into_inner(self.diag.take().unwrap())
}
/// Most `emit_producing_guarantee` functions use this as a starting point.
fn emit_producing_nothing(mut self) {
// FIXME(nnethercote) Make private once this moves to diagnostic.rs.
pub(crate) fn emit_producing_nothing(mut self) {
let diag = self.take_diag();
self.dcx.emit_diagnostic(diag);
}
/// `ErrorGuaranteed::emit_producing_guarantee` uses this.
fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed {
// FIXME(nnethercote) Make private once this moves to diagnostic.rs.
pub(crate) fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed {
let diag = self.take_diag();
// The only error levels that produce `ErrorGuaranteed` are
@ -168,40 +172,6 @@ impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
}
}
/// `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, which has a `with_` prefix added.
/// It is mostly used in a chained fashion when producing a new diagnostic,
/// e.g. `let err = struct_err(msg).with_code(code)`, or when emitting a new
/// diagnostic , e.g. `struct_err(msg).with_code(code).emit()`.
///
/// Although the latter method can be used to modify an existing diagnostic,
/// e.g. `err = err.with_code(code)`, this should be avoided because the former
/// method gives shorter code, e.g. `err.code(code)`.
macro_rules! forward {
(
($f:ident, $with_f:ident)($($name:ident: $ty:ty),* $(,)?)
) => {
#[doc = concat!("See [`Diagnostic::", stringify!($f), "()`].")]
pub fn $f(&mut self, $($name: $ty),*) -> &mut Self {
self.diag.as_mut().unwrap().$f($($name),*);
self
}
#[doc = concat!("See [`Diagnostic::", stringify!($f), "()`].")]
pub fn $with_f(mut self, $($name: $ty),*) -> Self {
self.diag.as_mut().unwrap().$f($($name),*);
self
}
};
}
impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> {
type Target = Diagnostic;
@ -278,135 +248,6 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
self.downgrade_to_delayed_bug();
self.emit()
}
forward!((span_label, with_span_label)(
span: Span,
label: impl Into<SubdiagnosticMessage>,
));
forward!((span_labels, with_span_labels)(
spans: impl IntoIterator<Item = Span>,
label: &str,
));
forward!((note_expected_found, with_note_expected_found)(
expected_label: &dyn fmt::Display,
expected: DiagnosticStyledString,
found_label: &dyn fmt::Display,
found: DiagnosticStyledString,
));
forward!((note_expected_found_extra, with_note_expected_found_extra)(
expected_label: &dyn fmt::Display,
expected: DiagnosticStyledString,
found_label: &dyn fmt::Display,
found: DiagnosticStyledString,
expected_extra: &dyn fmt::Display,
found_extra: &dyn fmt::Display,
));
forward!((note, with_note)(
msg: impl Into<SubdiagnosticMessage>,
));
forward!((note_once, with_note_once)(
msg: impl Into<SubdiagnosticMessage>,
));
forward!((span_note, with_span_note)(
sp: impl Into<MultiSpan>,
msg: impl Into<SubdiagnosticMessage>,
));
forward!((span_note_once, with_span_note_once)(
sp: impl Into<MultiSpan>,
msg: impl Into<SubdiagnosticMessage>,
));
forward!((warn, with_warn)(
msg: impl Into<SubdiagnosticMessage>,
));
forward!((span_warn, with_span_warn)(
sp: impl Into<MultiSpan>,
msg: impl Into<SubdiagnosticMessage>,
));
forward!((help, with_help)(
msg: impl Into<SubdiagnosticMessage>,
));
forward!((help_once, with_help_once)(
msg: impl Into<SubdiagnosticMessage>,
));
forward!((span_help, with_span_help_once)(
sp: impl Into<MultiSpan>,
msg: impl Into<SubdiagnosticMessage>,
));
forward!((multipart_suggestion, with_multipart_suggestion)(
msg: impl Into<SubdiagnosticMessage>,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
));
forward!((multipart_suggestion_verbose, with_multipart_suggestion_verbose)(
msg: impl Into<SubdiagnosticMessage>,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
));
forward!((tool_only_multipart_suggestion, with_tool_only_multipart_suggestion)(
msg: impl Into<SubdiagnosticMessage>,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
));
forward!((span_suggestion, with_span_suggestion)(
sp: Span,
msg: impl Into<SubdiagnosticMessage>,
suggestion: impl ToString,
applicability: Applicability,
));
forward!((span_suggestions, with_span_suggestions)(
sp: Span,
msg: impl Into<SubdiagnosticMessage>,
suggestions: impl IntoIterator<Item = String>,
applicability: Applicability,
));
forward!((multipart_suggestions, with_multipart_suggestions)(
msg: impl Into<SubdiagnosticMessage>,
suggestions: impl IntoIterator<Item = Vec<(Span, String)>>,
applicability: Applicability,
));
forward!((span_suggestion_short, with_span_suggestion_short)(
sp: Span,
msg: impl Into<SubdiagnosticMessage>,
suggestion: impl ToString,
applicability: Applicability,
));
forward!((span_suggestion_verbose, with_span_suggestion_verbose)(
sp: Span,
msg: impl Into<SubdiagnosticMessage>,
suggestion: impl ToString,
applicability: Applicability,
));
forward!((span_suggestion_hidden, with_span_suggestion_hidden)(
sp: Span,
msg: impl Into<SubdiagnosticMessage>,
suggestion: impl ToString,
applicability: Applicability,
));
forward!((tool_only_span_suggestion, with_tool_only_span_suggestion)(
sp: Span,
msg: impl Into<SubdiagnosticMessage>,
suggestion: impl ToString,
applicability: Applicability,
));
forward!((primary_message, with_primary_message)(
msg: impl Into<DiagnosticMessage>,
));
forward!((span, with_span)(
sp: impl Into<MultiSpan>,
));
forward!((is_lint, with_is_lint)(
name: String, has_future_breakage: bool,
));
forward!((code, with_code)(
code: ErrCode,
));
forward!((arg, with_arg)(
name: impl Into<Cow<'static, str>>, arg: impl IntoDiagnosticArg,
));
forward!((subdiagnostic, with_subdiagnostic)(
dcx: &DiagCtxt,
subdiagnostic: impl crate::AddToDiagnostic,
));
}
impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> {

View file

@ -299,7 +299,11 @@ pub struct SingleLabelManySpans {
pub label: &'static str,
}
impl AddToDiagnostic for SingleLabelManySpans {
fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut crate::Diagnostic, _: F) {
fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
self,
diag: &mut DiagnosticBuilder<'_, G>,
_: F,
) {
diag.span_labels(self.spans, self.label);
}
}
@ -312,23 +316,6 @@ pub struct ExpectedLifetimeParameter {
pub count: usize,
}
#[derive(Subdiagnostic)]
#[note(errors_delayed_at_with_newline)]
pub struct DelayedAtWithNewline {
#[primary_span]
pub span: Span,
pub emitted_at: DiagnosticLocation,
pub note: Backtrace,
}
#[derive(Subdiagnostic)]
#[note(errors_delayed_at_without_newline)]
pub struct DelayedAtWithoutNewline {
#[primary_span]
pub span: Span,
pub emitted_at: DiagnosticLocation,
pub note: Backtrace,
}
impl IntoDiagnosticArg for DiagnosticLocation {
fn into_diagnostic_arg(self) -> DiagnosticArgValue {
DiagnosticArgValue::Str(Cow::from(self.to_string()))
@ -341,13 +328,6 @@ impl IntoDiagnosticArg for Backtrace {
}
}
#[derive(Subdiagnostic)]
#[note(errors_invalid_flushed_delayed_diagnostic_level)]
pub struct InvalidFlushedDelayedDiagnosticLevel {
#[primary_span]
pub span: Span,
pub level: Level,
}
impl IntoDiagnosticArg for Level {
fn into_diagnostic_arg(self) -> DiagnosticArgValue {
DiagnosticArgValue::Str(Cow::from(self.to_string()))

View file

@ -599,7 +599,7 @@ impl Emitter for SilentEmitter {
fn emit_diagnostic(&mut self, mut diag: Diagnostic) {
if diag.level == Level::Fatal {
diag.note(self.fatal_note.clone());
diag.sub(Level::Note, self.fatal_note.clone(), MultiSpan::new());
self.fatal_dcx.emit_diagnostic(diag);
}
}

View file

@ -46,7 +46,7 @@ pub use diagnostic_builder::{
};
pub use diagnostic_impls::{
DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter,
IndicateAnonymousLifetime, InvalidFlushedDelayedDiagnosticLevel, SingleLabelManySpans,
IndicateAnonymousLifetime, SingleLabelManySpans,
};
pub use emitter::ColorConfig;
pub use rustc_error_messages::{
@ -62,7 +62,6 @@ pub use snippet::Style;
// See https://github.com/rust-lang/rust/pull/115393.
pub use termcolor::{Color, ColorSpec, WriteColor};
use crate::diagnostic_impls::{DelayedAtWithNewline, DelayedAtWithoutNewline};
use emitter::{is_case_difference, DynEmitter, Emitter, HumanEmitter};
use registry::Registry;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
@ -1395,9 +1394,8 @@ impl DiagCtxtInner {
};
diagnostic.children.extract_if(already_emitted_sub).for_each(|_| {});
if already_emitted {
diagnostic.note(
"duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`",
);
let msg = "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`";
diagnostic.sub(Level::Note, msg, MultiSpan::new());
}
if is_error {
@ -1483,6 +1481,16 @@ impl DiagCtxtInner {
self.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string()
}
fn eagerly_translate_for_subdiag(
&self,
diag: &Diagnostic,
msg: impl Into<SubdiagnosticMessage>,
) -> SubdiagnosticMessage {
let args = diag.args();
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
self.eagerly_translate(msg, args)
}
fn flush_delayed(&mut self) {
if self.delayed_bugs.is_empty() {
return;
@ -1527,17 +1535,14 @@ impl DiagCtxtInner {
if bug.level != DelayedBug {
// NOTE(eddyb) not panicking here because we're already producing
// an ICE, and the more information the merrier.
let subdiag = InvalidFlushedDelayedDiagnosticLevel {
span: bug.span.primary_span().unwrap(),
level: bug.level,
};
// FIXME: Cannot use `Diagnostic::subdiagnostic` which takes `DiagCtxt`, but it
// just uses `DiagCtxtInner` functions.
subdiag.add_to_diagnostic_with(&mut bug, |diag, msg| {
let args = diag.args();
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
self.eagerly_translate(msg, args)
});
//
// We are at the `Diagnostic`/`DiagCtxtInner` level rather than
// the usual `DiagnosticBuilder`/`DiagCtxt` level, so we must
// augment `bug` in a lower-level fashion.
bug.arg("level", bug.level);
let msg = crate::fluent_generated::errors_invalid_flushed_delayed_diagnostic_level;
let msg = self.eagerly_translate_for_subdiag(&bug, msg); // after the `arg` call
bug.sub(Level::Note, msg, bug.span.primary_span().unwrap().into());
}
bug.level = Bug;
@ -1571,39 +1576,22 @@ impl DelayedDiagnostic {
DelayedDiagnostic { inner: diagnostic, note: backtrace }
}
fn decorate(mut self, dcx: &DiagCtxtInner) -> Diagnostic {
// FIXME: Cannot use `Diagnostic::subdiagnostic` which takes `DiagCtxt`, but it
// just uses `DiagCtxtInner` functions.
let subdiag_with = |diag: &mut Diagnostic, msg| {
let args = diag.args();
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
dcx.eagerly_translate(msg, args)
};
match self.note.status() {
BacktraceStatus::Captured => {
let inner = &self.inner;
let subdiag = DelayedAtWithNewline {
span: inner.span.primary_span().unwrap_or(DUMMY_SP),
emitted_at: inner.emitted_at.clone(),
note: self.note,
};
subdiag.add_to_diagnostic_with(&mut self.inner, subdiag_with);
}
fn decorate(self, dcx: &DiagCtxtInner) -> Diagnostic {
// We are at the `Diagnostic`/`DiagCtxtInner` level rather than the
// usual `DiagnosticBuilder`/`DiagCtxt` level, so we must construct
// `diag` in a lower-level fashion.
let mut diag = self.inner;
let msg = match self.note.status() {
BacktraceStatus::Captured => crate::fluent_generated::errors_delayed_at_with_newline,
// Avoid the needless newline when no backtrace has been captured,
// the display impl should just be a single line.
_ => {
let inner = &self.inner;
let subdiag = DelayedAtWithoutNewline {
span: inner.span.primary_span().unwrap_or(DUMMY_SP),
emitted_at: inner.emitted_at.clone(),
note: self.note,
};
subdiag.add_to_diagnostic_with(&mut self.inner, subdiag_with);
}
}
self.inner
_ => crate::fluent_generated::errors_delayed_at_without_newline,
};
diag.arg("emitted_at", diag.emitted_at.clone());
diag.arg("note", self.note);
let msg = dcx.eagerly_translate_for_subdiag(&diag, msg); // after the `arg` calls
diag.sub(Level::Note, msg, diag.span.primary_span().unwrap_or(DUMMY_SP).into());
diag
}
}