Skip to content

Commit 5c38950

Browse files
authored
Rollup merge of rust-lang#102623 - davidtwco:translation-eager, r=compiler-errors
translation: eager translation Part of rust-lang#100717. See [Zulip thread](https://rust-lang.zulipchat.com/#narrow/stream/336883-i18n/topic/.23100717.20lists!/near/295010720) for additional context. - **Store diagnostic arguments in a `HashMap`**: Eager translation will enable subdiagnostics to be translated multiple times with different arguments - this requires the ability to replace the value of one argument with a new value, which is better suited to a `HashMap` than the previous storage, a `Vec`. - **Add `AddToDiagnostic::add_to_diagnostic_with`**: `AddToDiagnostic::add_to_diagnostic_with` is similar to the previous `AddToDiagnostic::add_to_diagnostic` but takes a function that can be used by the caller to modify diagnostic messages originating from the subdiagnostic (such as performing translation eagerly). `add_to_diagnostic` now just calls `add_to_diagnostic_with` with an empty closure. - **Add `DiagnosticMessage::Eager`**: Add variant of `DiagnosticMessage` for eagerly translated messages (messages in the target language which don't need translated by the emitter during emission). Also adds `eager_subdiagnostic` function which is intended to be invoked by the diagnostic derive for subdiagnostic fields which are marked as needing eager translation. - **Support `#[subdiagnostic(eager)]`**: Add support for `eager` argument to the `subdiagnostic` attribute which generates a call to `eager_subdiagnostic`. - **Finish migrating `rustc_query_system`**: Using eager translation, migrate the remaining repeated cycle stack diagnostic. - **Split formatting initialization and use in diagnostic derives**: Diagnostic derives have previously had to take special care when ordering the generated code so that fields were not used after a move. This is unlikely for most fields because a field is either annotated with a subdiagnostic attribute and is thus likely a `Span` and copiable, or is a argument, in which case it is only used once by `set_arg` anyway. However, format strings for code in suggestions can result in fields being used after being moved if not ordered carefully. As a result, the derive currently puts `set_arg` calls last (just before emission), such as: let diag = { /* create diagnostic */ }; diag.span_suggestion_with_style( span, fluent::crate::slug, format!("{}", __binding_0), Applicability::Unknown, SuggestionStyle::ShowAlways ); /* + other subdiagnostic additions */ diag.set_arg("foo", __binding_0); /* + other `set_arg` calls */ diag.emit(); For eager translation, this doesn't work, as the message being translated eagerly can assume that all arguments are available - so arguments _must_ be set first. Format strings for suggestion code are now separated into two parts - an initialization line that performs the formatting into a variable, and a usage in the subdiagnostic addition. By separating these parts, the initialization can happen before arguments are set, preserving the desired order so that code compiles, while still enabling arguments to be set before subdiagnostics are added. let diag = { /* create diagnostic */ }; let __code_0 = format!("{}", __binding_0); /* + other formatting */ diag.set_arg("foo", __binding_0); /* + other `set_arg` calls */ diag.span_suggestion_with_style( span, fluent::crate::slug, __code_0, Applicability::Unknown, SuggestionStyle::ShowAlways ); /* + other subdiagnostic additions */ diag.emit(); - **Remove field ordering logic in diagnostic derive:** Following the approach taken in earlier commits to separate formatting initialization from use in the subdiagnostic derive, simplify the diagnostic derive by removing the field-ordering logic that previously solved this problem. r? ``@compiler-errors``
2 parents e6ce562 + fbac1f2 commit 5c38950

File tree

26 files changed

+539
-235
lines changed

26 files changed

+539
-235
lines changed

compiler/rustc_ast_lowering/src/errors.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgFromDisplay};
1+
use rustc_errors::{
2+
fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgFromDisplay,
3+
SubdiagnosticMessage,
4+
};
25
use rustc_macros::{Diagnostic, Subdiagnostic};
36
use rustc_span::{symbol::Ident, Span, Symbol};
47

@@ -19,7 +22,10 @@ pub struct UseAngleBrackets {
1922
}
2023

2124
impl AddToDiagnostic for UseAngleBrackets {
22-
fn add_to_diagnostic(self, diag: &mut Diagnostic) {
25+
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
26+
where
27+
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
28+
{
2329
diag.multipart_suggestion(
2430
fluent::ast_lowering::use_angle_brackets,
2531
vec![(self.open_param, String::from("<")), (self.close_param, String::from(">"))],
@@ -69,7 +75,10 @@ pub enum AssocTyParenthesesSub {
6975
}
7076

7177
impl AddToDiagnostic for AssocTyParenthesesSub {
72-
fn add_to_diagnostic(self, diag: &mut Diagnostic) {
78+
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
79+
where
80+
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
81+
{
7382
match self {
7483
Self::Empty { parentheses_span } => diag.multipart_suggestion(
7584
fluent::ast_lowering::remove_parentheses,

compiler/rustc_ast_passes/src/errors.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Errors emitted by ast_passes.
22
3-
use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic};
3+
use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic, SubdiagnosticMessage};
44
use rustc_macros::{Diagnostic, Subdiagnostic};
55
use rustc_span::{Span, Symbol};
66

@@ -17,7 +17,10 @@ pub struct ForbiddenLet {
1717
}
1818

1919
impl AddToDiagnostic for ForbiddenLetReason {
20-
fn add_to_diagnostic(self, diag: &mut Diagnostic) {
20+
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
21+
where
22+
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
23+
{
2124
match self {
2225
Self::GenericForbidden => {}
2326
Self::NotSupportedOr(span) => {
@@ -228,7 +231,10 @@ pub struct ExternBlockSuggestion {
228231
}
229232

230233
impl AddToDiagnostic for ExternBlockSuggestion {
231-
fn add_to_diagnostic(self, diag: &mut Diagnostic) {
234+
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
235+
where
236+
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
237+
{
232238
let start_suggestion = if let Some(abi) = self.abi {
233239
format!("extern \"{}\" {{", abi)
234240
} else {

compiler/rustc_codegen_ssa/src/back/write.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ use rustc_data_structures::profiling::TimingGuard;
1515
use rustc_data_structures::profiling::VerboseTimingGuard;
1616
use rustc_data_structures::sync::Lrc;
1717
use rustc_errors::emitter::Emitter;
18-
use rustc_errors::{translation::Translate, DiagnosticId, FatalError, Handler, Level};
18+
use rustc_errors::{
19+
translation::{to_fluent_args, Translate},
20+
DiagnosticId, FatalError, Handler, Level,
21+
};
1922
use rustc_fs_util::link_or_copy;
2023
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
2124
use rustc_incremental::{
@@ -1740,7 +1743,7 @@ impl Translate for SharedEmitter {
17401743

17411744
impl Emitter for SharedEmitter {
17421745
fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) {
1743-
let fluent_args = self.to_fluent_args(diag.args());
1746+
let fluent_args = to_fluent_args(diag.args());
17441747
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
17451748
msg: self.translate_messages(&diag.message, &fluent_args).to_string(),
17461749
code: diag.code.clone(),

compiler/rustc_error_messages/locales/en-US/query_system.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ query_system_cycle_usage = cycle used when {$usage}
1212
1313
query_system_cycle_stack_single = ...which immediately requires {$stack_bottom} again
1414
15+
query_system_cycle_stack_middle = ...which requires {$desc}...
16+
1517
query_system_cycle_stack_multiple = ...which again requires {$stack_bottom}, completing the cycle
1618
1719
query_system_cycle_recursive_ty_alias = type aliases cannot be recursive

compiler/rustc_error_messages/src/lib.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,18 @@ pub enum SubdiagnosticMessage {
277277
/// Non-translatable diagnostic message.
278278
// FIXME(davidtwco): can a `Cow<'static, str>` be used here?
279279
Str(String),
280+
/// Translatable message which has already been translated eagerly.
281+
///
282+
/// Some diagnostics have repeated subdiagnostics where the same interpolated variables would
283+
/// be instantiated multiple times with different values. As translation normally happens
284+
/// immediately prior to emission, after the diagnostic and subdiagnostic derive logic has run,
285+
/// the setting of diagnostic arguments in the derived code will overwrite previous variable
286+
/// values and only the final value will be set when translation occurs - resulting in
287+
/// incorrect diagnostics. Eager translation results in translation for a subdiagnostic
288+
/// happening immediately after the subdiagnostic derive's logic has been run. This variant
289+
/// stores messages which have been translated eagerly.
290+
// FIXME(#100717): can a `Cow<'static, str>` be used here?
291+
Eager(String),
280292
/// Identifier of a Fluent message. Instances of this variant are generated by the
281293
/// `Subdiagnostic` derive.
282294
FluentIdentifier(FluentId),
@@ -304,8 +316,20 @@ impl<S: Into<String>> From<S> for SubdiagnosticMessage {
304316
#[rustc_diagnostic_item = "DiagnosticMessage"]
305317
pub enum DiagnosticMessage {
306318
/// Non-translatable diagnostic message.
307-
// FIXME(davidtwco): can a `Cow<'static, str>` be used here?
319+
// FIXME(#100717): can a `Cow<'static, str>` be used here?
308320
Str(String),
321+
/// Translatable message which has already been translated eagerly.
322+
///
323+
/// Some diagnostics have repeated subdiagnostics where the same interpolated variables would
324+
/// be instantiated multiple times with different values. As translation normally happens
325+
/// immediately prior to emission, after the diagnostic and subdiagnostic derive logic has run,
326+
/// the setting of diagnostic arguments in the derived code will overwrite previous variable
327+
/// values and only the final value will be set when translation occurs - resulting in
328+
/// incorrect diagnostics. Eager translation results in translation for a subdiagnostic
329+
/// happening immediately after the subdiagnostic derive's logic has been run. This variant
330+
/// stores messages which have been translated eagerly.
331+
// FIXME(#100717): can a `Cow<'static, str>` be used here?
332+
Eager(String),
309333
/// Identifier for a Fluent message (with optional attribute) corresponding to the diagnostic
310334
/// message.
311335
///
@@ -324,6 +348,7 @@ impl DiagnosticMessage {
324348
pub fn with_subdiagnostic_message(&self, sub: SubdiagnosticMessage) -> Self {
325349
let attr = match sub {
326350
SubdiagnosticMessage::Str(s) => return DiagnosticMessage::Str(s),
351+
SubdiagnosticMessage::Eager(s) => return DiagnosticMessage::Eager(s),
327352
SubdiagnosticMessage::FluentIdentifier(id) => {
328353
return DiagnosticMessage::FluentIdentifier(id, None);
329354
}
@@ -332,6 +357,7 @@ impl DiagnosticMessage {
332357

333358
match self {
334359
DiagnosticMessage::Str(s) => DiagnosticMessage::Str(s.clone()),
360+
DiagnosticMessage::Eager(s) => DiagnosticMessage::Eager(s.clone()),
335361
DiagnosticMessage::FluentIdentifier(id, _) => {
336362
DiagnosticMessage::FluentIdentifier(id.clone(), Some(attr))
337363
}
@@ -367,6 +393,7 @@ impl Into<SubdiagnosticMessage> for DiagnosticMessage {
367393
fn into(self) -> SubdiagnosticMessage {
368394
match self {
369395
DiagnosticMessage::Str(s) => SubdiagnosticMessage::Str(s),
396+
DiagnosticMessage::Eager(s) => SubdiagnosticMessage::Eager(s),
370397
DiagnosticMessage::FluentIdentifier(id, None) => {
371398
SubdiagnosticMessage::FluentIdentifier(id)
372399
}

compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
88
use crate::emitter::FileWithAnnotatedLines;
99
use crate::snippet::Line;
10-
use crate::translation::Translate;
10+
use crate::translation::{to_fluent_args, Translate};
1111
use crate::{
1212
CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle,
1313
LazyFallbackBundle, Level, MultiSpan, Style, SubDiagnostic,
@@ -46,7 +46,7 @@ impl Translate for AnnotateSnippetEmitterWriter {
4646
impl Emitter for AnnotateSnippetEmitterWriter {
4747
/// The entry point for the diagnostics generation
4848
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
49-
let fluent_args = self.to_fluent_args(diag.args());
49+
let fluent_args = to_fluent_args(diag.args());
5050

5151
let mut children = diag.children.clone();
5252
let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args);

compiler/rustc_errors/src/diagnostic.rs

+46-11
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ pub struct SuggestionsDisabled;
2727
/// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of
2828
/// `DiagnosticArg` are converted to `FluentArgs` (consuming the collection) at the start of
2929
/// diagnostic emission.
30-
pub type DiagnosticArg<'source> = (Cow<'source, str>, DiagnosticArgValue<'source>);
30+
pub type DiagnosticArg<'iter, 'source> =
31+
(&'iter DiagnosticArgName<'source>, &'iter DiagnosticArgValue<'source>);
32+
33+
/// Name of a diagnostic argument.
34+
pub type DiagnosticArgName<'source> = Cow<'source, str>;
3135

3236
/// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted
3337
/// to a `FluentValue` by the emitter to be used in diagnostic translation.
@@ -199,9 +203,20 @@ impl IntoDiagnosticArg for ast::token::TokenKind {
199203
/// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic].
200204
#[cfg_attr(bootstrap, rustc_diagnostic_item = "AddSubdiagnostic")]
201205
#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "AddToDiagnostic")]
202-
pub trait AddToDiagnostic {
206+
pub trait AddToDiagnostic
207+
where
208+
Self: Sized,
209+
{
203210
/// Add a subdiagnostic to an existing diagnostic.
204-
fn add_to_diagnostic(self, diag: &mut Diagnostic);
211+
fn add_to_diagnostic(self, diag: &mut Diagnostic) {
212+
self.add_to_diagnostic_with(diag, |_, m| m);
213+
}
214+
215+
/// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used
216+
/// (to optionally perform eager translation).
217+
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, f: F)
218+
where
219+
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage;
205220
}
206221

207222
/// Trait implemented by lint types. This should not be implemented manually. Instead, use
@@ -229,7 +244,7 @@ pub struct Diagnostic {
229244
pub span: MultiSpan,
230245
pub children: Vec<SubDiagnostic>,
231246
pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
232-
args: Vec<DiagnosticArg<'static>>,
247+
args: FxHashMap<DiagnosticArgName<'static>, DiagnosticArgValue<'static>>,
233248

234249
/// This is not used for highlighting or rendering any error message. Rather, it can be used
235250
/// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
@@ -321,7 +336,7 @@ impl Diagnostic {
321336
span: MultiSpan::new(),
322337
children: vec![],
323338
suggestions: Ok(vec![]),
324-
args: vec![],
339+
args: Default::default(),
325340
sort_span: DUMMY_SP,
326341
is_lint: false,
327342
}
@@ -917,13 +932,30 @@ impl Diagnostic {
917932
self
918933
}
919934

920-
/// Add a subdiagnostic from a type that implements `Subdiagnostic` - see
921-
/// [rustc_macros::Subdiagnostic].
935+
/// Add a subdiagnostic from a type that implements `Subdiagnostic` (see
936+
/// [rustc_macros::Subdiagnostic]).
922937
pub fn subdiagnostic(&mut self, subdiagnostic: impl AddToDiagnostic) -> &mut Self {
923938
subdiagnostic.add_to_diagnostic(self);
924939
self
925940
}
926941

942+
/// Add a subdiagnostic from a type that implements `Subdiagnostic` (see
943+
/// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages
944+
/// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of
945+
/// interpolated variables).
946+
pub fn eager_subdiagnostic(
947+
&mut self,
948+
handler: &crate::Handler,
949+
subdiagnostic: impl AddToDiagnostic,
950+
) -> &mut Self {
951+
subdiagnostic.add_to_diagnostic_with(self, |diag, msg| {
952+
let args = diag.args();
953+
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
954+
handler.eagerly_translate(msg, args)
955+
});
956+
self
957+
}
958+
927959
pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
928960
self.span = sp.into();
929961
if let Some(span) = self.span.primary_span() {
@@ -956,16 +988,19 @@ impl Diagnostic {
956988
self
957989
}
958990

959-
pub fn args(&self) -> &[DiagnosticArg<'static>] {
960-
&self.args
991+
// Exact iteration order of diagnostic arguments shouldn't make a difference to output because
992+
// they're only used in interpolation.
993+
#[allow(rustc::potential_query_instability)]
994+
pub fn args<'a>(&'a self) -> impl Iterator<Item = DiagnosticArg<'a, 'static>> {
995+
self.args.iter()
961996
}
962997

963998
pub fn set_arg(
964999
&mut self,
9651000
name: impl Into<Cow<'static, str>>,
9661001
arg: impl IntoDiagnosticArg,
9671002
) -> &mut Self {
968-
self.args.push((name.into(), arg.into_diagnostic_arg()));
1003+
self.args.insert(name.into(), arg.into_diagnostic_arg());
9691004
self
9701005
}
9711006

@@ -976,7 +1011,7 @@ impl Diagnostic {
9761011
/// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by
9771012
/// combining it with the primary message of the diagnostic (if translatable, otherwise it just
9781013
/// passes the user's string along).
979-
fn subdiagnostic_message_to_diagnostic_message(
1014+
pub(crate) fn subdiagnostic_message_to_diagnostic_message(
9801015
&self,
9811016
attr: impl Into<SubdiagnosticMessage>,
9821017
) -> DiagnosticMessage {

compiler/rustc_errors/src/emitter.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_span::{FileLines, SourceFile, Span};
1414

1515
use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString};
1616
use crate::styled_buffer::StyledBuffer;
17-
use crate::translation::Translate;
17+
use crate::translation::{to_fluent_args, Translate};
1818
use crate::{
1919
CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, FluentBundle, Handler,
2020
LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, SuggestionStyle,
@@ -535,7 +535,7 @@ impl Emitter for EmitterWriter {
535535
}
536536

537537
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
538-
let fluent_args = self.to_fluent_args(diag.args());
538+
let fluent_args = to_fluent_args(diag.args());
539539

540540
let mut children = diag.children.clone();
541541
let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args);

compiler/rustc_errors/src/json.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_span::source_map::{FilePathMapping, SourceMap};
1313

1414
use crate::emitter::{Emitter, HumanReadableErrorType};
1515
use crate::registry::Registry;
16-
use crate::translation::Translate;
16+
use crate::translation::{to_fluent_args, Translate};
1717
use crate::DiagnosticId;
1818
use crate::{
1919
CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic,
@@ -312,7 +312,7 @@ struct UnusedExterns<'a, 'b, 'c> {
312312

313313
impl Diagnostic {
314314
fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
315-
let args = je.to_fluent_args(diag.args());
315+
let args = to_fluent_args(diag.args());
316316
let sugg = diag.suggestions.iter().flatten().map(|sugg| {
317317
let translated_message = je.translate_message(&sugg.msg, &args);
318318
Diagnostic {

compiler/rustc_errors/src/lib.rs

+11
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,17 @@ impl Handler {
598598
}
599599
}
600600

601+
/// Translate `message` eagerly with `args`.
602+
pub fn eagerly_translate<'a>(
603+
&self,
604+
message: DiagnosticMessage,
605+
args: impl Iterator<Item = DiagnosticArg<'a, 'static>>,
606+
) -> SubdiagnosticMessage {
607+
let inner = self.inner.borrow();
608+
let args = crate::translation::to_fluent_args(args);
609+
SubdiagnosticMessage::Eager(inner.emitter.translate_message(&message, &args).to_string())
610+
}
611+
601612
// This is here to not allow mutation of flags;
602613
// as of this writing it's only used in tests in librustc_middle.
603614
pub fn can_emit_warnings(&self) -> bool {

0 commit comments

Comments
 (0)