Rollup merge of #142650 - camsteffen:refactor-translator, r=petrochenkov

Refactor Translator

My main motivation was to simplify the usage of `SilentEmitter` for users like rustfmt. A few refactoring opportunities arose along the way.

* Replace `Translate` trait with `Translator` struct
* Replace `Emitter: Translate` with `Emitter::translator`
* Split `SilentEmitter` into `FatalOnlyEmitter` and `SilentEmitter`
This commit is contained in:
Trevor Gross
2025-06-20 13:36:01 -04:00
committed by GitHub
19 changed files with 220 additions and 302 deletions

View File

@@ -14,10 +14,10 @@ use rustc_data_structures::jobserver::{self, Acquired};
use rustc_data_structures::memmap::Mmap; use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard}; use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard};
use rustc_errors::emitter::Emitter; use rustc_errors::emitter::Emitter;
use rustc_errors::translation::Translate; use rustc_errors::translation::Translator;
use rustc_errors::{ use rustc_errors::{
Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, FluentBundle, Level, MultiSpan, Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, Level, MultiSpan, Style,
Style, Suggestions, Suggestions,
}; };
use rustc_fs_util::link_or_copy; use rustc_fs_util::link_or_copy;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
@@ -1889,16 +1889,6 @@ impl SharedEmitter {
} }
} }
impl Translate for SharedEmitter {
fn fluent_bundle(&self) -> Option<&FluentBundle> {
None
}
fn fallback_fluent_bundle(&self) -> &FluentBundle {
panic!("shared emitter attempted to translate a diagnostic");
}
}
impl Emitter for SharedEmitter { impl Emitter for SharedEmitter {
fn emit_diagnostic( fn emit_diagnostic(
&mut self, &mut self,
@@ -1932,6 +1922,10 @@ impl Emitter for SharedEmitter {
fn source_map(&self) -> Option<&SourceMap> { fn source_map(&self) -> Option<&SourceMap> {
None None
} }
fn translator(&self) -> &Translator {
panic!("shared emitter attempted to translate a diagnostic");
}
} }
impl SharedEmitterMain { impl SharedEmitterMain {

View File

@@ -38,6 +38,7 @@ use rustc_data_structures::profiling::{
}; };
use rustc_errors::emitter::stderr_destination; use rustc_errors::emitter::stderr_destination;
use rustc_errors::registry::Registry; use rustc_errors::registry::Registry;
use rustc_errors::translation::Translator;
use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown}; use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown};
use rustc_feature::find_gated_cfg; use rustc_feature::find_gated_cfg;
// This avoids a false positive with `-Wunused_crate_dependencies`. // This avoids a false positive with `-Wunused_crate_dependencies`.
@@ -109,6 +110,10 @@ use crate::session_diagnostics::{
rustc_fluent_macro::fluent_messages! { "../messages.ftl" } rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
pub fn default_translator() -> Translator {
Translator::with_fallback_bundle(DEFAULT_LOCALE_RESOURCES.to_vec(), false)
}
pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
// tidy-alphabetical-start // tidy-alphabetical-start
crate::DEFAULT_LOCALE_RESOURCE, crate::DEFAULT_LOCALE_RESOURCE,
@@ -1413,11 +1418,10 @@ fn report_ice(
extra_info: fn(&DiagCtxt), extra_info: fn(&DiagCtxt),
using_internal_features: &AtomicBool, using_internal_features: &AtomicBool,
) { ) {
let fallback_bundle = let translator = default_translator();
rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
let emitter = Box::new(rustc_errors::emitter::HumanEmitter::new( let emitter = Box::new(rustc_errors::emitter::HumanEmitter::new(
stderr_destination(rustc_errors::ColorConfig::Auto), stderr_destination(rustc_errors::ColorConfig::Auto),
fallback_bundle, translator,
)); ));
let dcx = rustc_errors::DiagCtxt::new(emitter); let dcx = rustc_errors::DiagCtxt::new(emitter);
let dcx = dcx.handle(); let dcx = dcx.handle();

View File

@@ -18,7 +18,7 @@ pub use fluent_bundle::{self, FluentArgs, FluentError, FluentValue};
use fluent_syntax::parser::ParserError; use fluent_syntax::parser::ParserError;
use icu_provider_adapters::fallback::{LocaleFallbackProvider, LocaleFallbacker}; use icu_provider_adapters::fallback::{LocaleFallbackProvider, LocaleFallbacker};
use intl_memoizer::concurrent::IntlLangMemoizer; use intl_memoizer::concurrent::IntlLangMemoizer;
use rustc_data_structures::sync::IntoDynSyncSend; use rustc_data_structures::sync::{DynSend, IntoDynSyncSend};
use rustc_macros::{Decodable, Encodable}; use rustc_macros::{Decodable, Encodable};
use rustc_span::Span; use rustc_span::Span;
use smallvec::SmallVec; use smallvec::SmallVec;
@@ -204,16 +204,16 @@ fn register_functions(bundle: &mut FluentBundle) {
/// Type alias for the result of `fallback_fluent_bundle` - a reference-counted pointer to a lazily /// Type alias for the result of `fallback_fluent_bundle` - a reference-counted pointer to a lazily
/// evaluated fluent bundle. /// evaluated fluent bundle.
pub type LazyFallbackBundle = Arc<LazyLock<FluentBundle, impl FnOnce() -> FluentBundle>>; pub type LazyFallbackBundle =
Arc<LazyLock<FluentBundle, Box<dyn FnOnce() -> FluentBundle + DynSend>>>;
/// Return the default `FluentBundle` with standard "en-US" diagnostic messages. /// Return the default `FluentBundle` with standard "en-US" diagnostic messages.
#[instrument(level = "trace", skip(resources))] #[instrument(level = "trace", skip(resources))]
#[define_opaque(LazyFallbackBundle)]
pub fn fallback_fluent_bundle( pub fn fallback_fluent_bundle(
resources: Vec<&'static str>, resources: Vec<&'static str>,
with_directionality_markers: bool, with_directionality_markers: bool,
) -> LazyFallbackBundle { ) -> LazyFallbackBundle {
Arc::new(LazyLock::new(move || { Arc::new(LazyLock::new(Box::new(move || {
let mut fallback_bundle = new_bundle(vec![langid!("en-US")]); let mut fallback_bundle = new_bundle(vec![langid!("en-US")]);
register_functions(&mut fallback_bundle); register_functions(&mut fallback_bundle);
@@ -228,7 +228,7 @@ pub fn fallback_fluent_bundle(
} }
fallback_bundle fallback_bundle
})) })))
} }
/// Identifier for the Fluent message/attribute corresponding to a diagnostic message. /// Identifier for the Fluent message/attribute corresponding to a diagnostic message.

View File

@@ -15,17 +15,15 @@ use rustc_span::source_map::SourceMap;
use crate::emitter::FileWithAnnotatedLines; use crate::emitter::FileWithAnnotatedLines;
use crate::registry::Registry; use crate::registry::Registry;
use crate::snippet::Line; use crate::snippet::Line;
use crate::translation::{Translate, to_fluent_args}; use crate::translation::{Translator, to_fluent_args};
use crate::{ use crate::{
CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, FluentBundle, LazyFallbackBundle, CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, Level, MultiSpan, Style, Subdiag,
Level, MultiSpan, Style, Subdiag,
}; };
/// Generates diagnostics using annotate-snippet /// Generates diagnostics using annotate-snippet
pub struct AnnotateSnippetEmitter { pub struct AnnotateSnippetEmitter {
source_map: Option<Arc<SourceMap>>, source_map: Option<Arc<SourceMap>>,
fluent_bundle: Option<Arc<FluentBundle>>, translator: Translator,
fallback_bundle: LazyFallbackBundle,
/// If true, hides the longer explanation text /// If true, hides the longer explanation text
short_message: bool, short_message: bool,
@@ -35,16 +33,6 @@ pub struct AnnotateSnippetEmitter {
macro_backtrace: bool, macro_backtrace: bool,
} }
impl Translate for AnnotateSnippetEmitter {
fn fluent_bundle(&self) -> Option<&FluentBundle> {
self.fluent_bundle.as_deref()
}
fn fallback_fluent_bundle(&self) -> &FluentBundle {
&self.fallback_bundle
}
}
impl Emitter for AnnotateSnippetEmitter { impl Emitter for AnnotateSnippetEmitter {
/// The entry point for the diagnostics generation /// The entry point for the diagnostics generation
fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) { fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) {
@@ -78,6 +66,10 @@ impl Emitter for AnnotateSnippetEmitter {
fn should_show_explain(&self) -> bool { fn should_show_explain(&self) -> bool {
!self.short_message !self.short_message
} }
fn translator(&self) -> &Translator {
&self.translator
}
} }
/// Provides the source string for the given `line` of `file` /// Provides the source string for the given `line` of `file`
@@ -104,19 +96,11 @@ fn annotation_level_for_level(level: Level) -> annotate_snippets::Level {
impl AnnotateSnippetEmitter { impl AnnotateSnippetEmitter {
pub fn new( pub fn new(
source_map: Option<Arc<SourceMap>>, source_map: Option<Arc<SourceMap>>,
fluent_bundle: Option<Arc<FluentBundle>>, translator: Translator,
fallback_bundle: LazyFallbackBundle,
short_message: bool, short_message: bool,
macro_backtrace: bool, macro_backtrace: bool,
) -> Self { ) -> Self {
Self { Self { source_map, translator, short_message, ui_testing: false, macro_backtrace }
source_map,
fluent_bundle,
fallback_bundle,
short_message,
ui_testing: false,
macro_backtrace,
}
} }
/// Allows to modify `Self` to enable or disable the `ui_testing` flag. /// Allows to modify `Self` to enable or disable the `ui_testing` flag.
@@ -137,7 +121,7 @@ impl AnnotateSnippetEmitter {
_children: &[Subdiag], _children: &[Subdiag],
_suggestions: &[CodeSuggestion], _suggestions: &[CodeSuggestion],
) { ) {
let message = self.translate_messages(messages, args); let message = self.translator.translate_messages(messages, args);
if let Some(source_map) = &self.source_map { if let Some(source_map) = &self.source_map {
// Make sure our primary file comes first // Make sure our primary file comes first
let primary_lo = if let Some(primary_span) = msp.primary_span().as_ref() { let primary_lo = if let Some(primary_span) = msp.primary_span().as_ref() {

View File

@@ -35,10 +35,10 @@ use crate::snippet::{
}; };
use crate::styled_buffer::StyledBuffer; use crate::styled_buffer::StyledBuffer;
use crate::timings::TimingRecord; use crate::timings::TimingRecord;
use crate::translation::{Translate, to_fluent_args}; use crate::translation::{Translator, to_fluent_args};
use crate::{ use crate::{
CodeSuggestion, DiagInner, DiagMessage, ErrCode, FluentBundle, LazyFallbackBundle, Level, CodeSuggestion, DiagInner, DiagMessage, ErrCode, Level, MultiSpan, Subdiag,
MultiSpan, Subdiag, SubstitutionHighlight, SuggestionStyle, TerminalUrl, SubstitutionHighlight, SuggestionStyle, TerminalUrl,
}; };
/// Default column width, used in tests and when terminal dimensions cannot be determined. /// Default column width, used in tests and when terminal dimensions cannot be determined.
@@ -175,7 +175,7 @@ const ANONYMIZED_LINE_NUM: &str = "LL";
pub type DynEmitter = dyn Emitter + DynSend; pub type DynEmitter = dyn Emitter + DynSend;
/// Emitter trait for emitting errors and other structured information. /// Emitter trait for emitting errors and other structured information.
pub trait Emitter: Translate { pub trait Emitter {
/// Emit a structured diagnostic. /// Emit a structured diagnostic.
fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry); fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry);
@@ -212,6 +212,8 @@ pub trait Emitter: Translate {
fn source_map(&self) -> Option<&SourceMap>; fn source_map(&self) -> Option<&SourceMap>;
fn translator(&self) -> &Translator;
/// Formats the substitutions of the primary_span /// Formats the substitutions of the primary_span
/// ///
/// There are a lot of conditions to this method, but in short: /// There are a lot of conditions to this method, but in short:
@@ -224,13 +226,17 @@ pub trait Emitter: Translate {
/// * If the current `DiagInner` has multiple suggestions, /// * If the current `DiagInner` has multiple suggestions,
/// we leave `primary_span` and the suggestions untouched. /// we leave `primary_span` and the suggestions untouched.
fn primary_span_formatted( fn primary_span_formatted(
&mut self, &self,
primary_span: &mut MultiSpan, primary_span: &mut MultiSpan,
suggestions: &mut Vec<CodeSuggestion>, suggestions: &mut Vec<CodeSuggestion>,
fluent_args: &FluentArgs<'_>, fluent_args: &FluentArgs<'_>,
) { ) {
if let Some((sugg, rest)) = suggestions.split_first() { if let Some((sugg, rest)) = suggestions.split_first() {
let msg = self.translate_message(&sugg.msg, fluent_args).map_err(Report::new).unwrap(); let msg = self
.translator()
.translate_message(&sugg.msg, fluent_args)
.map_err(Report::new)
.unwrap();
if rest.is_empty() if rest.is_empty()
// ^ if there is only one suggestion // ^ if there is only one suggestion
// don't display multi-suggestions as labels // don't display multi-suggestions as labels
@@ -491,16 +497,6 @@ pub trait Emitter: Translate {
} }
} }
impl Translate for HumanEmitter {
fn fluent_bundle(&self) -> Option<&FluentBundle> {
self.fluent_bundle.as_deref()
}
fn fallback_fluent_bundle(&self) -> &FluentBundle {
&self.fallback_bundle
}
}
impl Emitter for HumanEmitter { impl Emitter for HumanEmitter {
fn source_map(&self) -> Option<&SourceMap> { fn source_map(&self) -> Option<&SourceMap> {
self.sm.as_deref() self.sm.as_deref()
@@ -538,25 +534,41 @@ impl Emitter for HumanEmitter {
fn supports_color(&self) -> bool { fn supports_color(&self) -> bool {
self.dst.supports_color() self.dst.supports_color()
} }
fn translator(&self) -> &Translator {
&self.translator
}
} }
/// An emitter that does nothing when emitting a non-fatal diagnostic. /// An emitter that does nothing when emitting a non-fatal diagnostic.
/// Fatal diagnostics are forwarded to `fatal_emitter` to avoid silent /// Fatal diagnostics are forwarded to `fatal_emitter` to avoid silent
/// failures of rustc, as witnessed e.g. in issue #89358. /// failures of rustc, as witnessed e.g. in issue #89358.
pub struct SilentEmitter { pub struct FatalOnlyEmitter {
pub fatal_emitter: Box<dyn Emitter + DynSend>, pub fatal_emitter: Box<dyn Emitter + DynSend>,
pub fatal_note: Option<String>, pub fatal_note: Option<String>,
pub emit_fatal_diagnostic: bool,
} }
impl Translate for SilentEmitter { impl Emitter for FatalOnlyEmitter {
fn fluent_bundle(&self) -> Option<&FluentBundle> { fn source_map(&self) -> Option<&SourceMap> {
None None
} }
fn fallback_fluent_bundle(&self) -> &FluentBundle { fn emit_diagnostic(&mut self, mut diag: DiagInner, registry: &Registry) {
self.fatal_emitter.fallback_fluent_bundle() if diag.level == Level::Fatal {
if let Some(fatal_note) = &self.fatal_note {
diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new());
} }
self.fatal_emitter.emit_diagnostic(diag, registry);
}
}
fn translator(&self) -> &Translator {
self.fatal_emitter.translator()
}
}
pub struct SilentEmitter {
pub translator: Translator,
} }
impl Emitter for SilentEmitter { impl Emitter for SilentEmitter {
@@ -564,13 +576,10 @@ impl Emitter for SilentEmitter {
None None
} }
fn emit_diagnostic(&mut self, mut diag: DiagInner, registry: &Registry) { fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {}
if self.emit_fatal_diagnostic && diag.level == Level::Fatal {
if let Some(fatal_note) = &self.fatal_note { fn translator(&self) -> &Translator {
diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new()); &self.translator
}
self.fatal_emitter.emit_diagnostic(diag, registry);
}
} }
} }
@@ -615,9 +624,8 @@ pub struct HumanEmitter {
#[setters(skip)] #[setters(skip)]
dst: IntoDynSyncSend<Destination>, dst: IntoDynSyncSend<Destination>,
sm: Option<Arc<SourceMap>>, sm: Option<Arc<SourceMap>>,
fluent_bundle: Option<Arc<FluentBundle>>,
#[setters(skip)] #[setters(skip)]
fallback_bundle: LazyFallbackBundle, translator: Translator,
short_message: bool, short_message: bool,
ui_testing: bool, ui_testing: bool,
ignored_directories_in_source_blocks: Vec<String>, ignored_directories_in_source_blocks: Vec<String>,
@@ -637,12 +645,11 @@ pub(crate) struct FileWithAnnotatedLines {
} }
impl HumanEmitter { impl HumanEmitter {
pub fn new(dst: Destination, fallback_bundle: LazyFallbackBundle) -> HumanEmitter { pub fn new(dst: Destination, translator: Translator) -> HumanEmitter {
HumanEmitter { HumanEmitter {
dst: IntoDynSyncSend(dst), dst: IntoDynSyncSend(dst),
sm: None, sm: None,
fluent_bundle: None, translator,
fallback_bundle,
short_message: false, short_message: false,
ui_testing: false, ui_testing: false,
ignored_directories_in_source_blocks: Vec::new(), ignored_directories_in_source_blocks: Vec::new(),
@@ -1433,7 +1440,7 @@ impl HumanEmitter {
// very *weird* formats // very *weird* formats
// see? // see?
for (text, style) in msgs.iter() { for (text, style) in msgs.iter() {
let text = self.translate_message(text, args).map_err(Report::new).unwrap(); let text = self.translator.translate_message(text, args).map_err(Report::new).unwrap();
let text = &normalize_whitespace(&text); let text = &normalize_whitespace(&text);
let lines = text.split('\n').collect::<Vec<_>>(); let lines = text.split('\n').collect::<Vec<_>>();
if lines.len() > 1 { if lines.len() > 1 {
@@ -1528,7 +1535,8 @@ impl HumanEmitter {
} }
let mut line = 0; let mut line = 0;
for (text, style) in msgs.iter() { for (text, style) in msgs.iter() {
let text = self.translate_message(text, args).map_err(Report::new).unwrap(); let text =
self.translator.translate_message(text, args).map_err(Report::new).unwrap();
// Account for newlines to align output to its label. // Account for newlines to align output to its label.
for text in normalize_whitespace(&text).lines() { for text in normalize_whitespace(&text).lines() {
buffer.append( buffer.append(
@@ -1560,7 +1568,7 @@ impl HumanEmitter {
.into_iter() .into_iter()
.filter_map(|label| match label.label { .filter_map(|label| match label.label {
Some(msg) if label.is_primary => { Some(msg) if label.is_primary => {
let text = self.translate_message(&msg, args).ok()?; let text = self.translator.translate_message(&msg, args).ok()?;
if !text.trim().is_empty() { Some(text.to_string()) } else { None } if !text.trim().is_empty() { Some(text.to_string()) } else { None }
} }
_ => None, _ => None,
@@ -3104,7 +3112,11 @@ impl FileWithAnnotatedLines {
let label = label.as_ref().map(|m| { let label = label.as_ref().map(|m| {
normalize_whitespace( normalize_whitespace(
&emitter.translate_message(m, args).map_err(Report::new).unwrap(), &emitter
.translator()
.translate_message(m, args)
.map_err(Report::new)
.unwrap(),
) )
}); });

View File

@@ -32,11 +32,8 @@ use crate::emitter::{
}; };
use crate::registry::Registry; use crate::registry::Registry;
use crate::timings::{TimingRecord, TimingSection}; use crate::timings::{TimingRecord, TimingSection};
use crate::translation::{Translate, to_fluent_args}; use crate::translation::{Translator, to_fluent_args};
use crate::{ use crate::{CodeSuggestion, MultiSpan, SpanLabel, Subdiag, Suggestions, TerminalUrl};
CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, Subdiag, Suggestions,
TerminalUrl,
};
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@@ -47,9 +44,8 @@ pub struct JsonEmitter {
dst: IntoDynSyncSend<Box<dyn Write + Send>>, dst: IntoDynSyncSend<Box<dyn Write + Send>>,
#[setters(skip)] #[setters(skip)]
sm: Option<Arc<SourceMap>>, sm: Option<Arc<SourceMap>>,
fluent_bundle: Option<Arc<FluentBundle>>,
#[setters(skip)] #[setters(skip)]
fallback_bundle: LazyFallbackBundle, translator: Translator,
#[setters(skip)] #[setters(skip)]
pretty: bool, pretty: bool,
ui_testing: bool, ui_testing: bool,
@@ -67,7 +63,7 @@ impl JsonEmitter {
pub fn new( pub fn new(
dst: Box<dyn Write + Send>, dst: Box<dyn Write + Send>,
sm: Option<Arc<SourceMap>>, sm: Option<Arc<SourceMap>>,
fallback_bundle: LazyFallbackBundle, translator: Translator,
pretty: bool, pretty: bool,
json_rendered: HumanReadableErrorType, json_rendered: HumanReadableErrorType,
color_config: ColorConfig, color_config: ColorConfig,
@@ -75,8 +71,7 @@ impl JsonEmitter {
JsonEmitter { JsonEmitter {
dst: IntoDynSyncSend(dst), dst: IntoDynSyncSend(dst),
sm, sm,
fluent_bundle: None, translator,
fallback_bundle,
pretty, pretty,
ui_testing: false, ui_testing: false,
ignored_directories_in_source_blocks: Vec::new(), ignored_directories_in_source_blocks: Vec::new(),
@@ -110,16 +105,6 @@ enum EmitTyped<'a> {
UnusedExtern(UnusedExterns<'a>), UnusedExtern(UnusedExterns<'a>),
} }
impl Translate for JsonEmitter {
fn fluent_bundle(&self) -> Option<&FluentBundle> {
self.fluent_bundle.as_deref()
}
fn fallback_fluent_bundle(&self) -> &FluentBundle {
&self.fallback_bundle
}
}
impl Emitter for JsonEmitter { impl Emitter for JsonEmitter {
fn emit_diagnostic(&mut self, diag: crate::DiagInner, registry: &Registry) { fn emit_diagnostic(&mut self, diag: crate::DiagInner, registry: &Registry) {
let data = Diagnostic::from_errors_diagnostic(diag, self, registry); let data = Diagnostic::from_errors_diagnostic(diag, self, registry);
@@ -194,6 +179,10 @@ impl Emitter for JsonEmitter {
fn should_show_explain(&self) -> bool { fn should_show_explain(&self) -> bool {
!self.json_rendered.short() !self.json_rendered.short()
} }
fn translator(&self) -> &Translator {
&self.translator
}
} }
// The following data types are provided just for serialisation. // The following data types are provided just for serialisation.
@@ -324,7 +313,7 @@ impl Diagnostic {
let args = to_fluent_args(diag.args.iter()); let args = to_fluent_args(diag.args.iter());
let sugg_to_diag = |sugg: &CodeSuggestion| { let sugg_to_diag = |sugg: &CodeSuggestion| {
let translated_message = let translated_message =
je.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap(); je.translator.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap();
Diagnostic { Diagnostic {
message: translated_message.to_string(), message: translated_message.to_string(),
code: None, code: None,
@@ -368,7 +357,7 @@ impl Diagnostic {
} }
} }
let translated_message = je.translate_messages(&diag.messages, &args); let translated_message = je.translator.translate_messages(&diag.messages, &args);
let code = if let Some(code) = diag.code { let code = if let Some(code) = diag.code {
Some(DiagnosticCode { Some(DiagnosticCode {
@@ -396,10 +385,9 @@ impl Diagnostic {
ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)), ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)),
ColorConfig::Never => {} ColorConfig::Never => {}
} }
HumanEmitter::new(dst, Arc::clone(&je.fallback_bundle)) HumanEmitter::new(dst, je.translator.clone())
.short_message(short) .short_message(short)
.sm(je.sm.clone()) .sm(je.sm.clone())
.fluent_bundle(je.fluent_bundle.clone())
.diagnostic_width(je.diagnostic_width) .diagnostic_width(je.diagnostic_width)
.macro_backtrace(je.macro_backtrace) .macro_backtrace(je.macro_backtrace)
.track_diagnostics(je.track_diagnostics) .track_diagnostics(je.track_diagnostics)
@@ -430,7 +418,7 @@ impl Diagnostic {
args: &FluentArgs<'_>, args: &FluentArgs<'_>,
je: &JsonEmitter, je: &JsonEmitter,
) -> Diagnostic { ) -> Diagnostic {
let translated_message = je.translate_messages(&subdiag.messages, args); let translated_message = je.translator.translate_messages(&subdiag.messages, args);
Diagnostic { Diagnostic {
message: translated_message.to_string(), message: translated_message.to_string(),
code: None, code: None,
@@ -454,7 +442,7 @@ impl DiagnosticSpan {
span.is_primary, span.is_primary,
span.label span.label
.as_ref() .as_ref()
.map(|m| je.translate_message(m, args).unwrap()) .map(|m| je.translator.translate_message(m, args).unwrap())
.map(|m| m.to_string()), .map(|m| m.to_string()),
suggestion, suggestion,
je, je,

View File

@@ -41,14 +41,14 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
rustc_span::create_default_session_globals_then(|| { rustc_span::create_default_session_globals_then(|| {
let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned()); sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned());
let fallback_bundle = let translator =
crate::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false); Translator::with_fallback_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
let output = Arc::new(Mutex::new(Vec::new())); let output = Arc::new(Mutex::new(Vec::new()));
let je = JsonEmitter::new( let je = JsonEmitter::new(
Box::new(Shared { data: output.clone() }), Box::new(Shared { data: output.clone() }),
Some(sm), Some(sm),
fallback_bundle, translator,
true, // pretty true, // pretty
HumanReadableErrorType::Short, HumanReadableErrorType::Short,
ColorConfig::Never, ColorConfig::Never,

View File

@@ -748,40 +748,10 @@ impl DiagCtxt {
Self { inner: Lock::new(DiagCtxtInner::new(emitter)) } Self { inner: Lock::new(DiagCtxtInner::new(emitter)) }
} }
pub fn make_silent(&self, fatal_note: Option<String>, emit_fatal_diagnostic: bool) { pub fn make_silent(&self) {
// An empty type that implements `Emitter` to temporarily swap in place of the real one,
// which will be used in constructing its replacement.
struct FalseEmitter;
impl Emitter for FalseEmitter {
fn emit_diagnostic(&mut self, _: DiagInner, _: &Registry) {
unimplemented!("false emitter must only used during `make_silent`")
}
fn source_map(&self) -> Option<&SourceMap> {
unimplemented!("false emitter must only used during `make_silent`")
}
}
impl translation::Translate for FalseEmitter {
fn fluent_bundle(&self) -> Option<&FluentBundle> {
unimplemented!("false emitter must only used during `make_silent`")
}
fn fallback_fluent_bundle(&self) -> &FluentBundle {
unimplemented!("false emitter must only used during `make_silent`")
}
}
let mut inner = self.inner.borrow_mut(); let mut inner = self.inner.borrow_mut();
let mut prev_emitter = Box::new(FalseEmitter) as Box<dyn Emitter + DynSend>; let translator = inner.emitter.translator().clone();
std::mem::swap(&mut inner.emitter, &mut prev_emitter); inner.emitter = Box::new(emitter::SilentEmitter { translator });
let new_emitter = Box::new(emitter::SilentEmitter {
fatal_emitter: prev_emitter,
fatal_note,
emit_fatal_diagnostic,
});
inner.emitter = new_emitter;
} }
pub fn set_emitter(&self, emitter: Box<dyn Emitter + DynSend>) { pub fn set_emitter(&self, emitter: Box<dyn Emitter + DynSend>) {
@@ -1771,7 +1741,12 @@ impl DiagCtxtInner {
args: impl Iterator<Item = DiagArg<'a>>, args: impl Iterator<Item = DiagArg<'a>>,
) -> String { ) -> String {
let args = crate::translation::to_fluent_args(args); let args = crate::translation::to_fluent_args(args);
self.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string() self.emitter
.translator()
.translate_message(&message, &args)
.map_err(Report::new)
.unwrap()
.to_string()
} }
fn eagerly_translate_for_subdiag( fn eagerly_translate_for_subdiag(

View File

@@ -1,3 +1,5 @@
use std::sync::{Arc, LazyLock};
use rustc_data_structures::sync::IntoDynSyncSend; use rustc_data_structures::sync::IntoDynSyncSend;
use rustc_error_messages::fluent_bundle::resolver::errors::{ReferenceKind, ResolverError}; use rustc_error_messages::fluent_bundle::resolver::errors::{ReferenceKind, ResolverError};
use rustc_error_messages::{DiagMessage, langid}; use rustc_error_messages::{DiagMessage, langid};
@@ -5,23 +7,9 @@ use rustc_error_messages::{DiagMessage, langid};
use crate::FluentBundle; use crate::FluentBundle;
use crate::error::{TranslateError, TranslateErrorKind}; use crate::error::{TranslateError, TranslateErrorKind};
use crate::fluent_bundle::*; use crate::fluent_bundle::*;
use crate::translation::Translate; use crate::translation::Translator;
struct Dummy { fn make_translator(ftl: &'static str) -> Translator {
bundle: FluentBundle,
}
impl Translate for Dummy {
fn fluent_bundle(&self) -> Option<&FluentBundle> {
None
}
fn fallback_fluent_bundle(&self) -> &FluentBundle {
&self.bundle
}
}
fn make_dummy(ftl: &'static str) -> Dummy {
let resource = FluentResource::try_new(ftl.into()).expect("Failed to parse an FTL string."); let resource = FluentResource::try_new(ftl.into()).expect("Failed to parse an FTL string.");
let langid_en = langid!("en-US"); let langid_en = langid!("en-US");
@@ -33,12 +21,15 @@ fn make_dummy(ftl: &'static str) -> Dummy {
bundle.add_resource(resource).expect("Failed to add FTL resources to the bundle."); bundle.add_resource(resource).expect("Failed to add FTL resources to the bundle.");
Dummy { bundle } Translator {
fluent_bundle: None,
fallback_fluent_bundle: Arc::new(LazyLock::new(Box::new(|| bundle))),
}
} }
#[test] #[test]
fn wellformed_fluent() { fn wellformed_fluent() {
let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value let translator = make_translator("mir_build_borrow_of_moved_value = borrow of moved value
.label = value moved into `{$name}` here .label = value moved into `{$name}` here
.occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement the `Copy` trait .occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement the `Copy` trait
.value_borrowed_label = value borrowed here after move .value_borrowed_label = value borrowed here after move
@@ -54,7 +45,7 @@ fn wellformed_fluent() {
); );
assert_eq!( assert_eq!(
dummy.translate_message(&message, &args).unwrap(), translator.translate_message(&message, &args).unwrap(),
"borrow this binding in the pattern to avoid moving the value" "borrow this binding in the pattern to avoid moving the value"
); );
} }
@@ -66,7 +57,7 @@ fn wellformed_fluent() {
); );
assert_eq!( assert_eq!(
dummy.translate_message(&message, &args).unwrap(), translator.translate_message(&message, &args).unwrap(),
"value borrowed here after move" "value borrowed here after move"
); );
} }
@@ -78,7 +69,7 @@ fn wellformed_fluent() {
); );
assert_eq!( assert_eq!(
dummy.translate_message(&message, &args).unwrap(), translator.translate_message(&message, &args).unwrap(),
"move occurs because `\u{2068}Foo\u{2069}` has type `\u{2068}std::string::String\u{2069}` which does not implement the `Copy` trait" "move occurs because `\u{2068}Foo\u{2069}` has type `\u{2068}std::string::String\u{2069}` which does not implement the `Copy` trait"
); );
@@ -89,7 +80,7 @@ fn wellformed_fluent() {
); );
assert_eq!( assert_eq!(
dummy.translate_message(&message, &args).unwrap(), translator.translate_message(&message, &args).unwrap(),
"value moved into `\u{2068}Foo\u{2069}` here" "value moved into `\u{2068}Foo\u{2069}` here"
); );
} }
@@ -98,7 +89,7 @@ fn wellformed_fluent() {
#[test] #[test]
fn misformed_fluent() { fn misformed_fluent() {
let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value let translator = make_translator("mir_build_borrow_of_moved_value = borrow of moved value
.label = value moved into `{name}` here .label = value moved into `{name}` here
.occurs_because_label = move occurs because `{$oops}` has type `{$ty}` which does not implement the `Copy` trait .occurs_because_label = move occurs because `{$oops}` has type `{$ty}` which does not implement the `Copy` trait
.suggestion = borrow this binding in the pattern to avoid moving the value"); .suggestion = borrow this binding in the pattern to avoid moving the value");
@@ -112,7 +103,7 @@ fn misformed_fluent() {
Some("value_borrowed_label".into()), Some("value_borrowed_label".into()),
); );
let err = dummy.translate_message(&message, &args).unwrap_err(); let err = translator.translate_message(&message, &args).unwrap_err();
assert!( assert!(
matches!( matches!(
&err, &err,
@@ -141,7 +132,7 @@ fn misformed_fluent() {
Some("label".into()), Some("label".into()),
); );
let err = dummy.translate_message(&message, &args).unwrap_err(); let err = translator.translate_message(&message, &args).unwrap_err();
if let TranslateError::Two { if let TranslateError::Two {
primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. }, primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. },
fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. }, fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. },
@@ -168,7 +159,7 @@ fn misformed_fluent() {
Some("occurs_because_label".into()), Some("occurs_because_label".into()),
); );
let err = dummy.translate_message(&message, &args).unwrap_err(); let err = translator.translate_message(&message, &args).unwrap_err();
if let TranslateError::Two { if let TranslateError::Two {
primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. }, primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. },
fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. }, fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. },

View File

@@ -1,8 +1,9 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::env; use std::env;
use std::error::Report; use std::error::Report;
use std::sync::Arc;
pub use rustc_error_messages::FluentArgs; pub use rustc_error_messages::{FluentArgs, LazyFallbackBundle};
use tracing::{debug, trace}; use tracing::{debug, trace};
use crate::error::{TranslateError, TranslateErrorKind}; use crate::error::{TranslateError, TranslateErrorKind};
@@ -28,19 +29,33 @@ pub fn to_fluent_args<'iter>(iter: impl Iterator<Item = DiagArg<'iter>>) -> Flue
args args
} }
pub trait Translate { #[derive(Clone)]
/// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no pub struct Translator {
/// language was requested by the user then this will be `None` and `fallback_fluent_bundle` /// Localized diagnostics for the locale requested by the user. If no language was requested by
/// should be used. /// the user then this will be `None` and `fallback_fluent_bundle` should be used.
fn fluent_bundle(&self) -> Option<&FluentBundle>; pub fluent_bundle: Option<Arc<FluentBundle>>,
/// Return `FluentBundle` with localized diagnostics for the default locale of the compiler. /// Return `FluentBundle` with localized diagnostics for the default locale of the compiler.
/// Used when the user has not requested a specific language or when a localized diagnostic is /// Used when the user has not requested a specific language or when a localized diagnostic is
/// unavailable for the requested locale. /// unavailable for the requested locale.
fn fallback_fluent_bundle(&self) -> &FluentBundle; pub fallback_fluent_bundle: LazyFallbackBundle,
}
impl Translator {
pub fn with_fallback_bundle(
resources: Vec<&'static str>,
with_directionality_markers: bool,
) -> Translator {
Translator {
fluent_bundle: None,
fallback_fluent_bundle: crate::fallback_fluent_bundle(
resources,
with_directionality_markers,
),
}
}
/// Convert `DiagMessage`s to a string, performing translation if necessary. /// Convert `DiagMessage`s to a string, performing translation if necessary.
fn translate_messages( pub fn translate_messages(
&self, &self,
messages: &[(DiagMessage, Style)], messages: &[(DiagMessage, Style)],
args: &FluentArgs<'_>, args: &FluentArgs<'_>,
@@ -54,7 +69,7 @@ pub trait Translate {
} }
/// Convert a `DiagMessage` to a string, performing translation if necessary. /// Convert a `DiagMessage` to a string, performing translation if necessary.
fn translate_message<'a>( pub fn translate_message<'a>(
&'a self, &'a self,
message: &'a DiagMessage, message: &'a DiagMessage,
args: &'a FluentArgs<'_>, args: &'a FluentArgs<'_>,
@@ -91,7 +106,7 @@ pub trait Translate {
}; };
try { try {
match self.fluent_bundle().map(|b| translate_with_bundle(b)) { match self.fluent_bundle.as_ref().map(|b| translate_with_bundle(b)) {
// The primary bundle was present and translation succeeded // The primary bundle was present and translation succeeded
Some(Ok(t)) => t, Some(Ok(t)) => t,
@@ -102,7 +117,7 @@ pub trait Translate {
primary @ TranslateError::One { primary @ TranslateError::One {
kind: TranslateErrorKind::MessageMissing, .. kind: TranslateErrorKind::MessageMissing, ..
}, },
)) => translate_with_bundle(self.fallback_fluent_bundle()) )) => translate_with_bundle(&self.fallback_fluent_bundle)
.map_err(|fallback| primary.and(fallback))?, .map_err(|fallback| primary.and(fallback))?,
// Always yeet out for errors on debug (unless // Always yeet out for errors on debug (unless
@@ -118,11 +133,11 @@ pub trait Translate {
// ..otherwise, for end users, an error about this wouldn't be useful or actionable, so // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so
// just hide it and try with the fallback bundle. // just hide it and try with the fallback bundle.
Some(Err(primary)) => translate_with_bundle(self.fallback_fluent_bundle()) Some(Err(primary)) => translate_with_bundle(&self.fallback_fluent_bundle)
.map_err(|fallback| primary.and(fallback))?, .map_err(|fallback| primary.and(fallback))?,
// The primary bundle is missing, proceed to the fallback bundle // The primary bundle is missing, proceed to the fallback bundle
None => translate_with_bundle(self.fallback_fluent_bundle()) None => translate_with_bundle(&self.fallback_fluent_bundle)
.map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?, .map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?,
} }
} }

View File

@@ -52,10 +52,9 @@ pub struct Compiler {
pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg { pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg {
cfgs.into_iter() cfgs.into_iter()
.map(|s| { .map(|s| {
let psess = ParseSess::with_silent_emitter( let psess = ParseSess::with_fatal_emitter(
vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
format!("this error occurred on the command line: `--cfg={s}`"), format!("this error occurred on the command line: `--cfg={s}`"),
true,
); );
let filename = FileName::cfg_spec_source_code(&s); let filename = FileName::cfg_spec_source_code(&s);
@@ -116,10 +115,9 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch
let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() }; let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() };
for s in specs { for s in specs {
let psess = ParseSess::with_silent_emitter( let psess = ParseSess::with_fatal_emitter(
vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
format!("this error occurred on the command line: `--check-cfg={s}`"), format!("this error occurred on the command line: `--check-cfg={s}`"),
true,
); );
let filename = FileName::cfg_spec_source_code(&s); let filename = FileName::cfg_spec_source_code(&s);

View File

@@ -14,6 +14,7 @@ use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, Toke
use rustc_ast::{self as ast, PatKind, visit}; use rustc_ast::{self as ast, PatKind, visit};
use rustc_ast_pretty::pprust::item_to_string; use rustc_ast_pretty::pprust::item_to_string;
use rustc_errors::emitter::{HumanEmitter, OutputTheme}; use rustc_errors::emitter::{HumanEmitter, OutputTheme};
use rustc_errors::translation::Translator;
use rustc_errors::{DiagCtxt, MultiSpan, PResult}; use rustc_errors::{DiagCtxt, MultiSpan, PResult};
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
use rustc_span::source_map::{FilePathMapping, SourceMap}; use rustc_span::source_map::{FilePathMapping, SourceMap};
@@ -41,9 +42,8 @@ fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> {
fn create_test_handler(theme: OutputTheme) -> (DiagCtxt, Arc<SourceMap>, Arc<Mutex<Vec<u8>>>) { fn create_test_handler(theme: OutputTheme) -> (DiagCtxt, Arc<SourceMap>, Arc<Mutex<Vec<u8>>>) {
let output = Arc::new(Mutex::new(Vec::new())); let output = Arc::new(Mutex::new(Vec::new()));
let source_map = Arc::new(SourceMap::new(FilePathMapping::empty())); let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
let fallback_bundle = let translator = Translator::with_fallback_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
rustc_errors::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false); let mut emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), translator)
let mut emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), fallback_bundle)
.sm(Some(source_map.clone())) .sm(Some(source_map.clone()))
.diagnostic_width(Some(140)); .diagnostic_width(Some(140));
emitter = emitter.theme(theme); emitter = emitter.theme(theme);

View File

@@ -8,10 +8,11 @@ use rustc_ast::attr::AttrIdGenerator;
use rustc_ast::node_id::NodeId; use rustc_ast::node_id::NodeId;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
use rustc_data_structures::sync::{AppendOnlyVec, Lock}; use rustc_data_structures::sync::{AppendOnlyVec, Lock};
use rustc_errors::emitter::{HumanEmitter, SilentEmitter, stderr_destination}; use rustc_errors::emitter::{FatalOnlyEmitter, HumanEmitter, stderr_destination};
use rustc_errors::translation::Translator;
use rustc_errors::{ use rustc_errors::{
ColorConfig, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, EmissionGuarantee, MultiSpan, ColorConfig, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, EmissionGuarantee, MultiSpan,
StashKey, fallback_fluent_bundle, StashKey,
}; };
use rustc_feature::{GateIssue, UnstableFeatures, find_feature_issue}; use rustc_feature::{GateIssue, UnstableFeatures, find_feature_issue};
use rustc_span::edition::Edition; use rustc_span::edition::Edition;
@@ -242,10 +243,10 @@ pub struct ParseSess {
impl ParseSess { impl ParseSess {
/// Used for testing. /// Used for testing.
pub fn new(locale_resources: Vec<&'static str>) -> Self { pub fn new(locale_resources: Vec<&'static str>) -> Self {
let fallback_bundle = fallback_fluent_bundle(locale_resources, false); let translator = Translator::with_fallback_bundle(locale_resources, false);
let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
let emitter = Box::new( let emitter = Box::new(
HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle) HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator)
.sm(Some(Arc::clone(&sm))), .sm(Some(Arc::clone(&sm))),
); );
let dcx = DiagCtxt::new(emitter); let dcx = DiagCtxt::new(emitter);
@@ -274,19 +275,14 @@ impl ParseSess {
} }
} }
pub fn with_silent_emitter( pub fn with_fatal_emitter(locale_resources: Vec<&'static str>, fatal_note: String) -> Self {
locale_resources: Vec<&'static str>, let translator = Translator::with_fallback_bundle(locale_resources, false);
fatal_note: String,
emit_fatal_diagnostic: bool,
) -> Self {
let fallback_bundle = fallback_fluent_bundle(locale_resources, false);
let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
let fatal_emitter = let fatal_emitter =
Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle)); Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator));
let dcx = DiagCtxt::new(Box::new(SilentEmitter { let dcx = DiagCtxt::new(Box::new(FatalOnlyEmitter {
fatal_emitter, fatal_emitter,
fatal_note: Some(fatal_note), fatal_note: Some(fatal_note),
emit_fatal_diagnostic,
})) }))
.disable_warnings(); .disable_warnings();
ParseSess::with_dcx(dcx, sm) ParseSess::with_dcx(dcx, sm)

View File

@@ -19,9 +19,10 @@ use rustc_errors::emitter::{
}; };
use rustc_errors::json::JsonEmitter; use rustc_errors::json::JsonEmitter;
use rustc_errors::timings::TimingSectionHandler; use rustc_errors::timings::TimingSectionHandler;
use rustc_errors::translation::Translator;
use rustc_errors::{ use rustc_errors::{
Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort,
FluentBundle, LazyFallbackBundle, TerminalUrl, fallback_fluent_bundle, TerminalUrl, fallback_fluent_bundle,
}; };
use rustc_macros::HashStable_Generic; use rustc_macros::HashStable_Generic;
pub use rustc_span::def_id::StableCrateId; pub use rustc_span::def_id::StableCrateId;
@@ -948,8 +949,7 @@ impl Session {
fn default_emitter( fn default_emitter(
sopts: &config::Options, sopts: &config::Options,
source_map: Arc<SourceMap>, source_map: Arc<SourceMap>,
bundle: Option<Arc<FluentBundle>>, translator: Translator,
fallback_bundle: LazyFallbackBundle,
) -> Box<DynEmitter> { ) -> Box<DynEmitter> {
let macro_backtrace = sopts.unstable_opts.macro_backtrace; let macro_backtrace = sopts.unstable_opts.macro_backtrace;
let track_diagnostics = sopts.unstable_opts.track_diagnostics; let track_diagnostics = sopts.unstable_opts.track_diagnostics;
@@ -974,17 +974,11 @@ fn default_emitter(
let short = kind.short(); let short = kind.short();
if let HumanReadableErrorType::AnnotateSnippet = kind { if let HumanReadableErrorType::AnnotateSnippet = kind {
let emitter = AnnotateSnippetEmitter::new( let emitter =
source_map, AnnotateSnippetEmitter::new(source_map, translator, short, macro_backtrace);
bundle,
fallback_bundle,
short,
macro_backtrace,
);
Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing)) Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
} else { } else {
let emitter = HumanEmitter::new(stderr_destination(color_config), fallback_bundle) let emitter = HumanEmitter::new(stderr_destination(color_config), translator)
.fluent_bundle(bundle)
.sm(source_map) .sm(source_map)
.short_message(short) .short_message(short)
.diagnostic_width(sopts.diagnostic_width) .diagnostic_width(sopts.diagnostic_width)
@@ -1006,12 +1000,11 @@ fn default_emitter(
JsonEmitter::new( JsonEmitter::new(
Box::new(io::BufWriter::new(io::stderr())), Box::new(io::BufWriter::new(io::stderr())),
source_map, source_map,
fallback_bundle, translator,
pretty, pretty,
json_rendered, json_rendered,
color_config, color_config,
) )
.fluent_bundle(bundle)
.ui_testing(sopts.unstable_opts.ui_testing) .ui_testing(sopts.unstable_opts.ui_testing)
.ignored_directories_in_source_blocks( .ignored_directories_in_source_blocks(
sopts.unstable_opts.ignore_directory_in_diagnostics_source_blocks.clone(), sopts.unstable_opts.ignore_directory_in_diagnostics_source_blocks.clone(),
@@ -1030,7 +1023,7 @@ fn default_emitter(
pub fn build_session( pub fn build_session(
sopts: config::Options, sopts: config::Options,
io: CompilerIO, io: CompilerIO,
bundle: Option<Arc<rustc_errors::FluentBundle>>, fluent_bundle: Option<Arc<rustc_errors::FluentBundle>>,
registry: rustc_errors::registry::Registry, registry: rustc_errors::registry::Registry,
fluent_resources: Vec<&'static str>, fluent_resources: Vec<&'static str>,
driver_lint_caps: FxHashMap<lint::LintId, lint::Level>, driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
@@ -1052,12 +1045,15 @@ pub fn build_session(
let cap_lints_allow = sopts.lint_cap.is_some_and(|cap| cap == lint::Allow); let cap_lints_allow = sopts.lint_cap.is_some_and(|cap| cap == lint::Allow);
let can_emit_warnings = !(warnings_allow || cap_lints_allow); let can_emit_warnings = !(warnings_allow || cap_lints_allow);
let fallback_bundle = fallback_fluent_bundle( let translator = Translator {
fluent_bundle,
fallback_fluent_bundle: fallback_fluent_bundle(
fluent_resources, fluent_resources,
sopts.unstable_opts.translate_directionality_markers, sopts.unstable_opts.translate_directionality_markers,
); ),
};
let source_map = rustc_span::source_map::get_source_map().unwrap(); let source_map = rustc_span::source_map::get_source_map().unwrap();
let emitter = default_emitter(&sopts, Arc::clone(&source_map), bundle, fallback_bundle); let emitter = default_emitter(&sopts, Arc::clone(&source_map), translator);
let mut dcx = DiagCtxt::new(emitter) let mut dcx = DiagCtxt::new(emitter)
.with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings)) .with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings))
@@ -1500,13 +1496,13 @@ impl EarlyDiagCtxt {
fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> { fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {
// FIXME(#100717): early errors aren't translated at the moment, so this is fine, but it will // FIXME(#100717): early errors aren't translated at the moment, so this is fine, but it will
// need to reference every crate that might emit an early error for translation to work. // need to reference every crate that might emit an early error for translation to work.
let fallback_bundle = let translator =
fallback_fluent_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false); Translator::with_fallback_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false);
let emitter: Box<DynEmitter> = match output { let emitter: Box<DynEmitter> = match output {
config::ErrorOutputType::HumanReadable { kind, color_config } => { config::ErrorOutputType::HumanReadable { kind, color_config } => {
let short = kind.short(); let short = kind.short();
Box::new( Box::new(
HumanEmitter::new(stderr_destination(color_config), fallback_bundle) HumanEmitter::new(stderr_destination(color_config), translator)
.theme(if let HumanReadableErrorType::Unicode = kind { .theme(if let HumanReadableErrorType::Unicode = kind {
OutputTheme::Unicode OutputTheme::Unicode
} else { } else {
@@ -1519,7 +1515,7 @@ fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {
Box::new(JsonEmitter::new( Box::new(JsonEmitter::new(
Box::new(io::BufWriter::new(io::stderr())), Box::new(io::BufWriter::new(io::stderr())),
Some(Arc::new(SourceMap::new(FilePathMapping::empty()))), Some(Arc::new(SourceMap::new(FilePathMapping::empty()))),
fallback_bundle, translator,
pretty, pretty,
json_rendered, json_rendered,
color_config, color_config,

View File

@@ -149,15 +149,12 @@ pub(crate) fn new_dcx(
diagnostic_width: Option<usize>, diagnostic_width: Option<usize>,
unstable_opts: &UnstableOptions, unstable_opts: &UnstableOptions,
) -> rustc_errors::DiagCtxt { ) -> rustc_errors::DiagCtxt {
let fallback_bundle = rustc_errors::fallback_fluent_bundle( let translator = rustc_driver::default_translator();
rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
false,
);
let emitter: Box<DynEmitter> = match error_format { let emitter: Box<DynEmitter> = match error_format {
ErrorOutputType::HumanReadable { kind, color_config } => { ErrorOutputType::HumanReadable { kind, color_config } => {
let short = kind.short(); let short = kind.short();
Box::new( Box::new(
HumanEmitter::new(stderr_destination(color_config), fallback_bundle) HumanEmitter::new(stderr_destination(color_config), translator)
.sm(source_map.map(|sm| sm as _)) .sm(source_map.map(|sm| sm as _))
.short_message(short) .short_message(short)
.diagnostic_width(diagnostic_width) .diagnostic_width(diagnostic_width)
@@ -178,7 +175,7 @@ pub(crate) fn new_dcx(
JsonEmitter::new( JsonEmitter::new(
Box::new(io::BufWriter::new(io::stderr())), Box::new(io::BufWriter::new(io::stderr())),
Some(source_map), Some(source_map),
fallback_bundle, translator,
pretty, pretty,
json_rendered, json_rendered,
color_config, color_config,

View File

@@ -456,16 +456,13 @@ fn parse_source(
let filename = FileName::anon_source_code(&wrapped_source); let filename = FileName::anon_source_code(&wrapped_source);
let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
let fallback_bundle = rustc_errors::fallback_fluent_bundle( let translator = rustc_driver::default_translator();
rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
false,
);
info.supports_color = info.supports_color =
HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone()) HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator.clone())
.supports_color(); .supports_color();
// Any errors in parsing should also appear when the doctest is compiled for real, so just // Any errors in parsing should also appear when the doctest is compiled for real, so just
// send all the errors that the parser emits directly into a `Sink` instead of stderr. // send all the errors that the parser emits directly into a `Sink` instead of stderr.
let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); let emitter = HumanEmitter::new(Box::new(io::sink()), translator);
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();

View File

@@ -6,8 +6,8 @@ use std::sync::Arc;
use rustc_data_structures::sync::Lock; use rustc_data_structures::sync::Lock;
use rustc_errors::emitter::Emitter; use rustc_errors::emitter::Emitter;
use rustc_errors::registry::Registry; use rustc_errors::registry::Registry;
use rustc_errors::translation::{Translate, to_fluent_args}; use rustc_errors::translation::{Translator, to_fluent_args};
use rustc_errors::{Applicability, DiagCtxt, DiagInner, LazyFallbackBundle}; use rustc_errors::{Applicability, DiagCtxt, DiagInner};
use rustc_parse::{source_str_to_stream, unwrap_or_emit_fatal}; use rustc_parse::{source_str_to_stream, unwrap_or_emit_fatal};
use rustc_resolve::rustdoc::source_span_for_markdown_range; use rustc_resolve::rustdoc::source_span_for_markdown_range;
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
@@ -36,11 +36,8 @@ fn check_rust_syntax(
code_block: RustCodeBlock, code_block: RustCodeBlock,
) { ) {
let buffer = Arc::new(Lock::new(Buffer::default())); let buffer = Arc::new(Lock::new(Buffer::default()));
let fallback_bundle = rustc_errors::fallback_fluent_bundle( let translator = rustc_driver::default_translator();
rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), let emitter = BufferEmitter { buffer: Arc::clone(&buffer), translator };
false,
);
let emitter = BufferEmitter { buffer: Arc::clone(&buffer), fallback_bundle };
let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
@@ -149,17 +146,7 @@ struct Buffer {
struct BufferEmitter { struct BufferEmitter {
buffer: Arc<Lock<Buffer>>, buffer: Arc<Lock<Buffer>>,
fallback_bundle: LazyFallbackBundle, translator: Translator,
}
impl Translate for BufferEmitter {
fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
None
}
fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
&self.fallback_bundle
}
} }
impl Emitter for BufferEmitter { impl Emitter for BufferEmitter {
@@ -168,6 +155,7 @@ impl Emitter for BufferEmitter {
let fluent_args = to_fluent_args(diag.args.iter()); let fluent_args = to_fluent_args(diag.args.iter());
let translated_main_message = self let translated_main_message = self
.translator
.translate_message(&diag.messages[0].0, &fluent_args) .translate_message(&diag.messages[0].0, &fluent_args)
.unwrap_or_else(|e| panic!("{e}")); .unwrap_or_else(|e| panic!("{e}"));
@@ -180,4 +168,8 @@ impl Emitter for BufferEmitter {
fn source_map(&self) -> Option<&SourceMap> { fn source_map(&self) -> Option<&SourceMap> {
None None
} }
fn translator(&self) -> &Translator {
&self.translator
}
} }

View File

@@ -42,9 +42,8 @@ pub fn check(
let mut test_attr_spans = vec![]; let mut test_attr_spans = vec![];
let filename = FileName::anon_source_code(&code); let filename = FileName::anon_source_code(&code);
let fallback_bundle = let translator = rustc_driver::default_translator();
rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false); let emitter = HumanEmitter::new(Box::new(io::sink()), translator);
let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
#[expect(clippy::arc_with_non_send_sync)] // `Arc` is expected by with_dcx #[expect(clippy::arc_with_non_send_sync)] // `Arc` is expected by with_dcx
let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));

View File

@@ -5,7 +5,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use rustc_data_structures::sync::IntoDynSyncSend; use rustc_data_structures::sync::IntoDynSyncSend;
use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter, SilentEmitter, stderr_destination}; use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter, SilentEmitter, stderr_destination};
use rustc_errors::registry::Registry; use rustc_errors::registry::Registry;
use rustc_errors::translation::Translate; use rustc_errors::translation::Translator;
use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel}; use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel};
use rustc_session::parse::ParseSess as RawParseSess; use rustc_session::parse::ParseSess as RawParseSess;
use rustc_span::{ use rustc_span::{
@@ -47,16 +47,6 @@ impl SilentOnIgnoredFilesEmitter {
} }
} }
impl Translate for SilentOnIgnoredFilesEmitter {
fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
self.emitter.fluent_bundle()
}
fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
self.emitter.fallback_fluent_bundle()
}
}
impl Emitter for SilentOnIgnoredFilesEmitter { impl Emitter for SilentOnIgnoredFilesEmitter {
fn source_map(&self) -> Option<&SourceMap> { fn source_map(&self) -> Option<&SourceMap> {
None None
@@ -84,6 +74,10 @@ impl Emitter for SilentOnIgnoredFilesEmitter {
} }
self.handle_non_ignoreable_error(diag, registry); self.handle_non_ignoreable_error(diag, registry);
} }
fn translator(&self) -> &Translator {
self.emitter.translator()
}
} }
impl From<Color> for ColorConfig { impl From<Color> for ColorConfig {
@@ -110,23 +104,15 @@ fn default_dcx(
ColorConfig::Never ColorConfig::Never
}; };
let fallback_bundle = rustc_errors::fallback_fluent_bundle( let translator = rustc_driver::default_translator();
rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
false,
);
let emitter = Box::new(
HumanEmitter::new(stderr_destination(emit_color), fallback_bundle)
.sm(Some(source_map.clone())),
);
let emitter: Box<DynEmitter> = if !show_parse_errors { let emitter: Box<DynEmitter> = if show_parse_errors {
Box::new(SilentEmitter { Box::new(
fatal_emitter: emitter, HumanEmitter::new(stderr_destination(emit_color), translator)
fatal_note: None, .sm(Some(source_map.clone())),
emit_fatal_diagnostic: false, )
})
} else { } else {
emitter Box::new(SilentEmitter { translator })
}; };
DiagCtxt::new(Box::new(SilentOnIgnoredFilesEmitter { DiagCtxt::new(Box::new(SilentOnIgnoredFilesEmitter {
has_non_ignorable_parser_errors: false, has_non_ignorable_parser_errors: false,
@@ -205,7 +191,7 @@ impl ParseSess {
} }
pub(crate) fn set_silent_emitter(&mut self) { pub(crate) fn set_silent_emitter(&mut self) {
self.raw_psess.dcx().make_silent(None, false); self.raw_psess.dcx().make_silent();
} }
pub(crate) fn span_to_filename(&self, span: Span) -> FileName { pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
@@ -335,16 +321,6 @@ mod tests {
num_emitted_errors: Arc<AtomicU32>, num_emitted_errors: Arc<AtomicU32>,
} }
impl Translate for TestEmitter {
fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
None
}
fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
panic!("test emitter attempted to translate a diagnostic");
}
}
impl Emitter for TestEmitter { impl Emitter for TestEmitter {
fn source_map(&self) -> Option<&SourceMap> { fn source_map(&self) -> Option<&SourceMap> {
None None
@@ -353,6 +329,10 @@ mod tests {
fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) { fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {
self.num_emitted_errors.fetch_add(1, Ordering::Release); self.num_emitted_errors.fetch_add(1, Ordering::Release);
} }
fn translator(&self) -> &Translator {
panic!("test emitter attempted to translate a diagnostic");
}
} }
fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> DiagInner { fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> DiagInner {