errors: only eagerly translate subdiagnostics

Subdiagnostics don't need to be lazily translated, they can always be
eagerly translated. Eager translation is slightly more complex as we need
to have a `DiagCtxt` available to perform the translation, which involves
slightly more threading of that context.

This slight increase in complexity should enable later simplifications -
like passing `DiagCtxt` into `AddToDiagnostic` and moving Fluent messages
into the diagnostic structs rather than having them in separate files
(working on that was what led to this change).

Signed-off-by: David Wood <david@davidtw.co>
This commit is contained in:
David Wood 2024-02-14 14:17:27 +00:00
parent bb89df6903
commit b80fc5d4e8
No known key found for this signature in database
43 changed files with 532 additions and 388 deletions

View file

@ -851,18 +851,11 @@ impl Diagnostic {
self
}
/// Add a subdiagnostic from a type that implements `Subdiagnostic` (see
/// [rustc_macros::Subdiagnostic]).
pub fn subdiagnostic(&mut self, subdiagnostic: impl AddToDiagnostic) -> &mut Self {
subdiagnostic.add_to_diagnostic(self);
self
}
/// Add a subdiagnostic from a type that implements `Subdiagnostic` (see
/// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages
/// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of
/// interpolated variables).
pub fn eager_subdiagnostic(
pub fn subdiagnostic(
&mut self,
dcx: &crate::DiagCtxt,
subdiagnostic: impl AddToDiagnostic,
@ -921,7 +914,7 @@ impl Diagnostic {
/// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by
/// combining it with the primary message of the diagnostic (if translatable, otherwise it just
/// passes the user's string along).
fn subdiagnostic_message_to_diagnostic_message(
pub(crate) fn subdiagnostic_message_to_diagnostic_message(
&self,
attr: impl Into<SubdiagnosticMessage>,
) -> DiagnosticMessage {

View file

@ -404,9 +404,6 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
name: impl Into<Cow<'static, str>>, arg: impl IntoDiagnosticArg,
));
forward!((subdiagnostic, with_subdiagnostic)(
subdiagnostic: impl crate::AddToDiagnostic,
));
forward!((eager_subdiagnostic, with_eager_subdiagnostic)(
dcx: &DiagCtxt,
subdiagnostic: impl crate::AddToDiagnostic,
));

View file

@ -10,6 +10,7 @@
use rustc_span::source_map::SourceMap;
use rustc_span::{FileLines, FileName, SourceFile, Span};
use crate::error::TranslateError;
use crate::snippet::{
Annotation, AnnotationColumn, AnnotationType, Line, MultilineAnnotation, Style, StyledString,
};
@ -559,6 +560,18 @@ pub struct SilentEmitter {
pub fatal_note: String,
}
pub fn silent_translate<'a>(
message: &'a DiagnosticMessage,
) -> Result<Cow<'_, str>, TranslateError<'_>> {
match message {
DiagnosticMessage::Str(msg) | DiagnosticMessage::Translated(msg) => Ok(Cow::Borrowed(msg)),
DiagnosticMessage::FluentIdentifier(identifier, _) => {
// Any value works here.
Ok(identifier.clone())
}
}
}
impl Translate for SilentEmitter {
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
None
@ -567,6 +580,16 @@ impl Translate for SilentEmitter {
fn fallback_fluent_bundle(&self) -> &FluentBundle {
panic!("silent emitter attempted to translate message")
}
// Override `translate_message` for the silent emitter because eager translation of
// subdiagnostics result in a call to this.
fn translate_message<'a>(
&'a self,
message: &'a DiagnosticMessage,
_: &'a FluentArgs<'_>,
) -> Result<Cow<'_, str>, TranslateError<'_>> {
silent_translate(message)
}
}
impl Emitter for SilentEmitter {

View file

@ -640,7 +640,8 @@ impl DiagCtxt {
message: DiagnosticMessage,
args: impl Iterator<Item = DiagnosticArg<'a>>,
) -> SubdiagnosticMessage {
SubdiagnosticMessage::Eager(Cow::from(self.eagerly_translate_to_string(message, args)))
let inner = self.inner.borrow();
inner.eagerly_translate(message, args)
}
/// Translate `message` eagerly with `args` to `String`.
@ -650,8 +651,7 @@ impl DiagCtxt {
args: impl Iterator<Item = DiagnosticArg<'a>>,
) -> String {
let inner = self.inner.borrow();
let args = crate::translation::to_fluent_args(args);
inner.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string()
inner.eagerly_translate_to_string(message, args)
}
// This is here to not allow mutation of flags;
@ -1446,6 +1446,25 @@ impl DiagCtxtInner {
.or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied())
}
/// Translate `message` eagerly with `args` to `SubdiagnosticMessage::Eager`.
pub fn eagerly_translate<'a>(
&self,
message: DiagnosticMessage,
args: impl Iterator<Item = DiagnosticArg<'a>>,
) -> SubdiagnosticMessage {
SubdiagnosticMessage::Translated(Cow::from(self.eagerly_translate_to_string(message, args)))
}
/// Translate `message` eagerly with `args` to `String`.
pub fn eagerly_translate_to_string<'a>(
&self,
message: DiagnosticMessage,
args: impl Iterator<Item = DiagnosticArg<'a>>,
) -> String {
let args = crate::translation::to_fluent_args(args);
self.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string()
}
fn flush_delayed(&mut self) {
if self.delayed_bugs.is_empty() {
return;
@ -1484,15 +1503,22 @@ impl DiagCtxtInner {
}
let mut bug =
if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner };
if backtrace || self.ice_file.is_none() { bug.decorate(self) } else { bug.inner };
// "Undelay" the delayed bugs (into plain `Bug`s).
if bug.level != DelayedBug {
// NOTE(eddyb) not panicking here because we're already producing
// an ICE, and the more information the merrier.
bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel {
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)
});
}
bug.level = Bug;
@ -1527,25 +1553,35 @@ impl DelayedDiagnostic {
DelayedDiagnostic { inner: diagnostic, note: backtrace }
}
fn decorate(mut self) -> Diagnostic {
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;
self.inner.subdiagnostic(DelayedAtWithNewline {
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);
}
// Avoid the needless newline when no backtrace has been captured,
// the display impl should just be a single line.
_ => {
let inner = &self.inner;
self.inner.subdiagnostic(DelayedAtWithoutNewline {
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);
}
}
@ -1691,15 +1727,15 @@ impl Level {
}
// FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite.
pub fn add_elided_lifetime_in_path_suggestion(
pub fn add_elided_lifetime_in_path_suggestion<E: EmissionGuarantee>(
source_map: &SourceMap,
diag: &mut Diagnostic,
diag: &mut DiagnosticBuilder<'_, E>,
n: usize,
path_span: Span,
incl_angl_brckt: bool,
insertion_span: Span,
) {
diag.subdiagnostic(ExpectedLifetimeParameter { span: path_span, count: n });
diag.subdiagnostic(diag.dcx, ExpectedLifetimeParameter { span: path_span, count: n });
if !source_map.is_span_accessible(insertion_span) {
// Do not try to suggest anything if generated by a proc-macro.
return;
@ -1708,11 +1744,10 @@ pub fn add_elided_lifetime_in_path_suggestion(
let suggestion =
if incl_angl_brckt { format!("<{anon_lts}>") } else { format!("{anon_lts}, ") };
diag.subdiagnostic(IndicateAnonymousLifetime {
span: insertion_span.shrink_to_hi(),
count: n,
suggestion,
});
diag.subdiagnostic(
diag.dcx,
IndicateAnonymousLifetime { span: insertion_span.shrink_to_hi(), count: n, suggestion },
);
}
pub fn report_ambiguity_error<'a, G: EmissionGuarantee>(

View file

@ -2,7 +2,7 @@ use crate::error::{TranslateError, TranslateErrorKind};
use crate::snippet::Style;
use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle};
use rustc_data_structures::sync::Lrc;
use rustc_error_messages::FluentArgs;
pub use rustc_error_messages::FluentArgs;
use std::borrow::Cow;
use std::env;
use std::error::Report;
@ -61,7 +61,7 @@ pub trait Translate {
) -> Result<Cow<'_, str>, TranslateError<'_>> {
trace!(?message, ?args);
let (identifier, attr) = match message {
DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => {
DiagnosticMessage::Str(msg) | DiagnosticMessage::Translated(msg) => {
return Ok(Cow::Borrowed(msg));
}
DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),