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

@ -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
}
}