diff options
| author | David Wood <david.wood@huawei.com> | 2022-08-10 17:30:47 +0100 |
|---|---|---|
| committer | David Wood <david.wood@huawei.com> | 2022-08-15 12:26:35 +0100 |
| commit | 510ba031dcf119e81834babf83e671b2cc00416a (patch) | |
| tree | ef2711d898d3a8fa001fca4dda5b4bc28f3b24cf /compiler/rustc_errors/src | |
| parent | 6ce76091c7cef21692a15dce1f0a4c415d245be4 (diff) | |
| download | rust-510ba031dcf119e81834babf83e671b2cc00416a.tar.gz rust-510ba031dcf119e81834babf83e671b2cc00416a.zip | |
errors: move translation logic into module
Just moving code around so that triagebot can ping relevant parties when translation logic is modified. Signed-off-by: David Wood <david.wood@huawei.com>
Diffstat (limited to 'compiler/rustc_errors/src')
| -rw-r--r-- | compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs | 19 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/emitter.rs | 128 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/json.rs | 19 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/lib.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/translation.rs | 103 |
5 files changed, 144 insertions, 126 deletions
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 3df562c7eda..b32fc3c719b 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -7,6 +7,7 @@ use crate::emitter::FileWithAnnotatedLines; use crate::snippet::Line; +use crate::translation::Translate; use crate::{ CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle, LazyFallbackBundle, Level, MultiSpan, Style, SubDiagnostic, @@ -32,6 +33,16 @@ pub struct AnnotateSnippetEmitterWriter { macro_backtrace: bool, } +impl Translate for AnnotateSnippetEmitterWriter { + fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { + self.fluent_bundle.as_ref() + } + + fn fallback_fluent_bundle(&self) -> &FluentBundle { + &**self.fallback_bundle + } +} + impl Emitter for AnnotateSnippetEmitterWriter { /// The entry point for the diagnostics generation fn emit_diagnostic(&mut self, diag: &Diagnostic) { @@ -63,14 +74,6 @@ impl Emitter for AnnotateSnippetEmitterWriter { self.source_map.as_ref() } - fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { - self.fluent_bundle.as_ref() - } - - fn fallback_fluent_bundle(&self) -> &FluentBundle { - &**self.fallback_bundle - } - fn should_show_explain(&self) -> bool { !self.short_message } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 753e2f07c04..6c1bfcb9919 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -14,10 +14,10 @@ use rustc_span::{FileLines, SourceFile, Span}; use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString}; use crate::styled_buffer::StyledBuffer; +use crate::translation::Translate; use crate::{ - CodeSuggestion, Diagnostic, DiagnosticArg, DiagnosticId, DiagnosticMessage, FluentBundle, - Handler, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, - SuggestionStyle, + CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, FluentBundle, Handler, + LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, SuggestionStyle, }; use rustc_lint_defs::pluralize; @@ -200,7 +200,7 @@ impl Margin { const ANONYMIZED_LINE_NUM: &str = "LL"; /// Emitter trait for emitting errors. -pub trait Emitter { +pub trait Emitter: Translate { /// Emit a structured diagnostic. fn emit_diagnostic(&mut self, diag: &Diagnostic); @@ -231,102 +231,6 @@ pub trait Emitter { fn source_map(&self) -> Option<&Lrc<SourceMap>>; - /// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no - /// language was requested by the user then this will be `None` and `fallback_fluent_bundle` - /// should be used. - fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>>; - - /// 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 - /// unavailable for the requested locale. - fn fallback_fluent_bundle(&self) -> &FluentBundle; - - /// Convert diagnostic arguments (a rustc internal type that exists to implement - /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation. - /// - /// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then - /// passed around as a reference thereafter. - fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> { - FromIterator::from_iter(args.to_vec().drain(..)) - } - - /// Convert `DiagnosticMessage`s to a string, performing translation if necessary. - fn translate_messages( - &self, - messages: &[(DiagnosticMessage, Style)], - args: &FluentArgs<'_>, - ) -> Cow<'_, str> { - Cow::Owned( - messages.iter().map(|(m, _)| self.translate_message(m, args)).collect::<String>(), - ) - } - - /// Convert a `DiagnosticMessage` to a string, performing translation if necessary. - fn translate_message<'a>( - &'a self, - message: &'a DiagnosticMessage, - args: &'a FluentArgs<'_>, - ) -> Cow<'_, str> { - trace!(?message, ?args); - let (identifier, attr) = match message { - DiagnosticMessage::Str(msg) => return Cow::Borrowed(&msg), - DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr), - }; - - let translate_with_bundle = |bundle: &'a FluentBundle| -> Option<(Cow<'_, str>, Vec<_>)> { - let message = bundle.get_message(&identifier)?; - let value = match attr { - Some(attr) => message.get_attribute(attr)?.value(), - None => message.value()?, - }; - debug!(?message, ?value); - - let mut errs = vec![]; - let translated = bundle.format_pattern(value, Some(&args), &mut errs); - debug!(?translated, ?errs); - Some((translated, errs)) - }; - - self.fluent_bundle() - .and_then(|bundle| translate_with_bundle(bundle)) - // If `translate_with_bundle` returns `None` with the primary bundle, this is likely - // just that the primary bundle doesn't contain the message being translated, so - // proceed to the fallback bundle. - // - // However, when errors are produced from translation, then that means the translation - // is broken (e.g. `{$foo}` exists in a translation but `foo` isn't provided). - // - // In debug builds, assert so that compiler devs can spot the broken translation and - // fix it.. - .inspect(|(_, errs)| { - debug_assert!( - errs.is_empty(), - "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}", - identifier, - attr, - args, - errs - ); - }) - // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so - // just hide it and try with the fallback bundle. - .filter(|(_, errs)| errs.is_empty()) - .or_else(|| translate_with_bundle(self.fallback_fluent_bundle())) - .map(|(translated, errs)| { - // Always bail out for errors with the fallback bundle. - assert!( - errs.is_empty(), - "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}", - identifier, - attr, - args, - errs - ); - translated - }) - .expect("failed to find message in primary or fallback fluent bundles") - } - /// Formats the substitutions of the primary_span /// /// There are a lot of conditions to this method, but in short: @@ -616,11 +520,7 @@ pub trait Emitter { } } -impl Emitter for EmitterWriter { - fn source_map(&self) -> Option<&Lrc<SourceMap>> { - self.sm.as_ref() - } - +impl Translate for EmitterWriter { fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { self.fluent_bundle.as_ref() } @@ -628,6 +528,12 @@ impl Emitter for EmitterWriter { fn fallback_fluent_bundle(&self) -> &FluentBundle { &**self.fallback_bundle } +} + +impl Emitter for EmitterWriter { + fn source_map(&self) -> Option<&Lrc<SourceMap>> { + self.sm.as_ref() + } fn emit_diagnostic(&mut self, diag: &Diagnostic) { let fluent_args = self.to_fluent_args(diag.args()); @@ -672,11 +578,7 @@ pub struct SilentEmitter { pub fatal_note: Option<String>, } -impl Emitter for SilentEmitter { - fn source_map(&self) -> Option<&Lrc<SourceMap>> { - None - } - +impl Translate for SilentEmitter { fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { None } @@ -684,6 +586,12 @@ impl Emitter for SilentEmitter { fn fallback_fluent_bundle(&self) -> &FluentBundle { panic!("silent emitter attempted to translate message") } +} + +impl Emitter for SilentEmitter { + fn source_map(&self) -> Option<&Lrc<SourceMap>> { + None + } fn emit_diagnostic(&mut self, d: &Diagnostic) { if d.level == Level::Fatal { diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index b8cd334b4c6..1680c6accd7 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -13,6 +13,7 @@ use rustc_span::source_map::{FilePathMapping, SourceMap}; use crate::emitter::{Emitter, HumanReadableErrorType}; use crate::registry::Registry; +use crate::translation::Translate; use crate::DiagnosticId; use crate::{ CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic, @@ -122,6 +123,16 @@ impl JsonEmitter { } } +impl Translate for JsonEmitter { + fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { + self.fluent_bundle.as_ref() + } + + fn fallback_fluent_bundle(&self) -> &FluentBundle { + &**self.fallback_bundle + } +} + impl Emitter for JsonEmitter { fn emit_diagnostic(&mut self, diag: &crate::Diagnostic) { let data = Diagnostic::from_errors_diagnostic(diag, self); @@ -189,14 +200,6 @@ impl Emitter for JsonEmitter { Some(&self.sm) } - fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { - self.fluent_bundle.as_ref() - } - - fn fallback_fluent_bundle(&self) -> &FluentBundle { - &**self.fallback_bundle - } - fn should_show_explain(&self) -> bool { !matches!(self.json_rendered, HumanReadableErrorType::Short(_)) } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index d2eb4f212eb..53e787afc38 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -58,6 +58,7 @@ mod lock; pub mod registry; mod snippet; mod styled_buffer; +pub mod translation; pub use snippet::Style; diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs new file mode 100644 index 00000000000..65338f56d9c --- /dev/null +++ b/compiler/rustc_errors/src/translation.rs @@ -0,0 +1,103 @@ +use crate::snippet::Style; +use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle}; +use rustc_data_structures::sync::Lrc; +use rustc_error_messages::FluentArgs; +use std::borrow::Cow; + +pub trait Translate { + /// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no + /// language was requested by the user then this will be `None` and `fallback_fluent_bundle` + /// should be used. + fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>>; + + /// 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 + /// unavailable for the requested locale. + fn fallback_fluent_bundle(&self) -> &FluentBundle; + + /// Convert diagnostic arguments (a rustc internal type that exists to implement + /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation. + /// + /// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then + /// passed around as a reference thereafter. + fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> { + FromIterator::from_iter(args.to_vec().drain(..)) + } + + /// Convert `DiagnosticMessage`s to a string, performing translation if necessary. + fn translate_messages( + &self, + messages: &[(DiagnosticMessage, Style)], + args: &FluentArgs<'_>, + ) -> Cow<'_, str> { + Cow::Owned( + messages.iter().map(|(m, _)| self.translate_message(m, args)).collect::<String>(), + ) + } + + /// Convert a `DiagnosticMessage` to a string, performing translation if necessary. + fn translate_message<'a>( + &'a self, + message: &'a DiagnosticMessage, + args: &'a FluentArgs<'_>, + ) -> Cow<'_, str> { + trace!(?message, ?args); + let (identifier, attr) = match message { + DiagnosticMessage::Str(msg) => return Cow::Borrowed(&msg), + DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr), + }; + + let translate_with_bundle = |bundle: &'a FluentBundle| -> Option<(Cow<'_, str>, Vec<_>)> { + let message = bundle.get_message(&identifier)?; + let value = match attr { + Some(attr) => message.get_attribute(attr)?.value(), + None => message.value()?, + }; + debug!(?message, ?value); + + let mut errs = vec![]; + let translated = bundle.format_pattern(value, Some(&args), &mut errs); + debug!(?translated, ?errs); + Some((translated, errs)) + }; + + self.fluent_bundle() + .and_then(|bundle| translate_with_bundle(bundle)) + // If `translate_with_bundle` returns `None` with the primary bundle, this is likely + // just that the primary bundle doesn't contain the message being translated, so + // proceed to the fallback bundle. + // + // However, when errors are produced from translation, then that means the translation + // is broken (e.g. `{$foo}` exists in a translation but `foo` isn't provided). + // + // In debug builds, assert so that compiler devs can spot the broken translation and + // fix it.. + .inspect(|(_, errs)| { + debug_assert!( + errs.is_empty(), + "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}", + identifier, + attr, + args, + errs + ); + }) + // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so + // just hide it and try with the fallback bundle. + .filter(|(_, errs)| errs.is_empty()) + .or_else(|| translate_with_bundle(self.fallback_fluent_bundle())) + .map(|(translated, errs)| { + // Always bail out for errors with the fallback bundle. + assert!( + errs.is_empty(), + "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}", + identifier, + attr, + args, + errs + ); + translated + }) + .expect("failed to find message in primary or fallback fluent bundles") + } +} |
