diff options
| author | David Wood <david.wood@huawei.com> | 2022-03-28 09:36:20 +0100 |
|---|---|---|
| committer | David Wood <david.wood@huawei.com> | 2022-04-05 07:01:02 +0100 |
| commit | d5119c5b9f1f71090d078e945ea6b5d39d08cffa (patch) | |
| tree | d63f8049bf2692b7fe2d34bf9e86ec7c26af6fe3 | |
| parent | 7f91697b5035f8620df4de47057024c3539b55a6 (diff) | |
| download | rust-d5119c5b9f1f71090d078e945ea6b5d39d08cffa.tar.gz rust-d5119c5b9f1f71090d078e945ea6b5d39d08cffa.zip | |
errors: implement sysroot/testing bundle loading
Extend loading of Fluent bundles so that bundles can be loaded from the sysroot based on the language requested by the user, or using a nightly flag. Sysroot bundles are loaded from `$sysroot/share/locale/$locale/*.ftl`. Signed-off-by: David Wood <david.wood@huawei.com>
23 files changed, 322 insertions, 46 deletions
diff --git a/Cargo.lock b/Cargo.lock index 7983b79d5d6..6940406a766 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1239,16 +1239,6 @@ dependencies = [ ] [[package]] -name = "fluent" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61f69378194459db76abd2ce3952b790db103ceb003008d3d50d97c41ff847a7" -dependencies = [ - "fluent-bundle", - "unic-langid", -] - -[[package]] name = "fluent-bundle" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3719,7 +3709,8 @@ version = "0.0.0" name = "rustc_error_messages" version = "0.0.0" dependencies = [ - "fluent", + "fluent-bundle", + "fluent-syntax", "rustc_data_structures", "rustc_macros", "rustc_serialize", diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index c95c59904d1..7fa3c1d17b0 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -1172,10 +1172,12 @@ static DEFAULT_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + /// When `install_ice_hook` is called, this function will be called as the panic /// hook. pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { - let fallback_bundle = rustc_errors::fallback_fluent_bundle(); + let fallback_bundle = + rustc_errors::fallback_fluent_bundle().expect("failed to load fallback fluent bundle"); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, None, + None, fallback_bundle, false, false, diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml index abd565cd529..bda44d691e5 100644 --- a/compiler/rustc_error_messages/Cargo.toml +++ b/compiler/rustc_error_messages/Cargo.toml @@ -7,7 +7,8 @@ edition = "2021" doctest = false [dependencies] -fluent = "0.16.0" +fluent-bundle = "0.15.2" +fluent-syntax = "0.11" rustc_data_structures = { path = "../rustc_data_structures" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 105bf1413de..15f0db49b07 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -1,24 +1,169 @@ +#![feature(path_try_exists)] + +use fluent_bundle::FluentResource; +use fluent_syntax::parser::ParserError; use rustc_data_structures::sync::Lrc; use rustc_macros::{Decodable, Encodable}; use rustc_span::Span; use std::borrow::Cow; -use tracing::debug; +use std::error::Error; +use std::fmt; +use std::fs; +use std::io; +use std::path::Path; +use tracing::{instrument, trace}; -pub use fluent::{FluentArgs, FluentValue}; +pub use fluent_bundle::{FluentArgs, FluentError, FluentValue}; +pub use unic_langid::{langid, LanguageIdentifier}; static FALLBACK_FLUENT_RESOURCE: &'static str = include_str!("../locales/en-US/diagnostics.ftl"); -pub type FluentBundle = fluent::FluentBundle<fluent::FluentResource>; +pub type FluentBundle = fluent_bundle::FluentBundle<FluentResource>; + +#[derive(Debug)] +pub enum TranslationBundleError { + /// Failed to read from `.ftl` file. + ReadFtl(io::Error), + /// Failed to parse contents of `.ftl` file. + ParseFtl(ParserError), + /// Failed to add `FluentResource` to `FluentBundle`. + AddResource(FluentError), + /// `$sysroot/share/locale/$locale` does not exist. + MissingLocale(io::Error), + /// Cannot read directory entries of `$sysroot/share/locale/$locale`. + ReadLocalesDir(io::Error), + /// Cannot read directory entry of `$sysroot/share/locale/$locale`. + ReadLocalesDirEntry(io::Error), + /// `$sysroot/share/locale/$locale` is not a directory. + LocaleIsNotDir, +} + +impl fmt::Display for TranslationBundleError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TranslationBundleError::ReadFtl(e) => write!(f, "could not read ftl file: {}", e), + TranslationBundleError::ParseFtl(e) => { + write!(f, "could not parse ftl file: {}", e) + } + TranslationBundleError::AddResource(e) => write!(f, "failed to add resource: {}", e), + TranslationBundleError::MissingLocale(e) => { + write!(f, "missing locale directory: {}", e) + } + TranslationBundleError::ReadLocalesDir(e) => { + write!(f, "could not read locales dir: {}", e) + } + TranslationBundleError::ReadLocalesDirEntry(e) => { + write!(f, "could not read locales dir entry: {}", e) + } + TranslationBundleError::LocaleIsNotDir => { + write!(f, "`$sysroot/share/locales/$locale` is not a directory") + } + } + } +} + +impl Error for TranslationBundleError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + TranslationBundleError::ReadFtl(e) => Some(e), + TranslationBundleError::ParseFtl(e) => Some(e), + TranslationBundleError::AddResource(e) => Some(e), + TranslationBundleError::MissingLocale(e) => Some(e), + TranslationBundleError::ReadLocalesDir(e) => Some(e), + TranslationBundleError::ReadLocalesDirEntry(e) => Some(e), + TranslationBundleError::LocaleIsNotDir => None, + } + } +} + +impl From<(FluentResource, Vec<ParserError>)> for TranslationBundleError { + fn from((_, mut errs): (FluentResource, Vec<ParserError>)) -> Self { + TranslationBundleError::ParseFtl(errs.pop().expect("failed ftl parse with no errors")) + } +} + +impl From<Vec<FluentError>> for TranslationBundleError { + fn from(mut errs: Vec<FluentError>) -> Self { + TranslationBundleError::AddResource( + errs.pop().expect("failed adding resource to bundle with no errors"), + ) + } +} + +/// Returns Fluent bundle with the user's locale resources from +/// `$sysroot/share/locale/$requested_locale/*.ftl`. +/// +/// If `-Z additional-ftl-path` was provided, load that resource and add it to the bundle +/// (overriding any conflicting messages). +#[instrument(level = "trace")] +pub fn fluent_bundle( + sysroot: &Path, + requested_locale: Option<LanguageIdentifier>, + additional_ftl_path: Option<&Path>, +) -> Result<Option<Lrc<FluentBundle>>, TranslationBundleError> { + if requested_locale.is_none() && additional_ftl_path.is_none() { + return Ok(None); + } + + // If there is only `-Z additional-ftl-path`, assume locale is "en-US", otherwise use user + // provided locale. + let locale = requested_locale.clone().unwrap_or_else(|| langid!("en-US")); + trace!(?locale); + let mut bundle = FluentBundle::new(vec![locale]); + + if let Some(requested_locale) = requested_locale { + let mut sysroot = sysroot.to_path_buf(); + sysroot.push("share"); + sysroot.push("locale"); + sysroot.push(requested_locale.to_string()); + trace!(?sysroot); + + let _ = sysroot.try_exists().map_err(TranslationBundleError::MissingLocale)?; + + if !sysroot.is_dir() { + return Err(TranslationBundleError::LocaleIsNotDir); + } + + for entry in sysroot.read_dir().map_err(TranslationBundleError::ReadLocalesDir)? { + let entry = entry.map_err(TranslationBundleError::ReadLocalesDirEntry)?; + let path = entry.path(); + trace!(?path); + if path.extension().and_then(|s| s.to_str()) != Some("ftl") { + trace!("skipping"); + continue; + } + + let resource_str = fs::read_to_string(path).map_err(TranslationBundleError::ReadFtl)?; + let resource = + FluentResource::try_new(resource_str).map_err(TranslationBundleError::from)?; + trace!(?resource); + bundle.add_resource(resource).map_err(TranslationBundleError::from)?; + } + } + + if let Some(additional_ftl_path) = additional_ftl_path { + let resource_str = + fs::read_to_string(additional_ftl_path).map_err(TranslationBundleError::ReadFtl)?; + let resource = + FluentResource::try_new(resource_str).map_err(TranslationBundleError::from)?; + trace!(?resource); + bundle.add_resource_overriding(resource); + } + + let bundle = Lrc::new(bundle); + Ok(Some(bundle)) +} -/// Return the default `FluentBundle` with standard en-US diagnostic messages. -pub fn fallback_fluent_bundle() -> Lrc<FluentBundle> { - let fallback_resource = fluent::FluentResource::try_new(FALLBACK_FLUENT_RESOURCE.to_string()) - .expect("failed to parse ftl resource"); - debug!(?fallback_resource); - let mut fallback_bundle = FluentBundle::new(vec![unic_langid::langid!("en-US")]); - fallback_bundle.add_resource(fallback_resource).expect("failed to add resource to bundle"); +/// Return the default `FluentBundle` with standard "en-US" diagnostic messages. +#[instrument(level = "trace")] +pub fn fallback_fluent_bundle() -> Result<Lrc<FluentBundle>, TranslationBundleError> { + let fallback_resource = FluentResource::try_new(FALLBACK_FLUENT_RESOURCE.to_string()) + .map_err(TranslationBundleError::from)?; + trace!(?fallback_resource); + let mut fallback_bundle = FluentBundle::new(vec![langid!("en-US")]); + fallback_bundle.add_resource(fallback_resource).map_err(TranslationBundleError::from)?; let fallback_bundle = Lrc::new(fallback_bundle); - fallback_bundle + Ok(fallback_bundle) } /// Identifier for the Fluent message/attribute corresponding to a diagnostic message. diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 4dc99b8dd0f..003fd1eea3a 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -21,6 +21,7 @@ use rustc_span::SourceFile; /// Generates diagnostics using annotate-snippet pub struct AnnotateSnippetEmitterWriter { source_map: Option<Lrc<SourceMap>>, + fluent_bundle: Option<Lrc<FluentBundle>>, fallback_bundle: Lrc<FluentBundle>, /// If true, hides the longer explanation text @@ -63,7 +64,7 @@ impl Emitter for AnnotateSnippetEmitterWriter { } fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { - None + self.fluent_bundle.as_ref() } fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> { @@ -99,11 +100,19 @@ fn annotation_type_for_level(level: Level) -> AnnotationType { impl AnnotateSnippetEmitterWriter { pub fn new( source_map: Option<Lrc<SourceMap>>, + fluent_bundle: Option<Lrc<FluentBundle>>, fallback_bundle: Lrc<FluentBundle>, short_message: bool, macro_backtrace: bool, ) -> Self { - Self { source_map, fallback_bundle, short_message, ui_testing: false, macro_backtrace } + Self { + 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. diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 43bcaa646f3..c8281bd37d1 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -59,6 +59,7 @@ impl HumanReadableErrorType { self, dst: Box<dyn Write + Send>, source_map: Option<Lrc<SourceMap>>, + bundle: Option<Lrc<FluentBundle>>, fallback_bundle: Lrc<FluentBundle>, teach: bool, terminal_width: Option<usize>, @@ -69,6 +70,7 @@ impl HumanReadableErrorType { EmitterWriter::new( dst, source_map, + bundle, fallback_bundle, short, teach, @@ -568,7 +570,7 @@ impl Emitter for EmitterWriter { } fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { - None + self.fluent_bundle.as_ref() } fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> { @@ -686,6 +688,7 @@ impl ColorConfig { pub struct EmitterWriter { dst: Destination, sm: Option<Lrc<SourceMap>>, + fluent_bundle: Option<Lrc<FluentBundle>>, fallback_bundle: Lrc<FluentBundle>, short_message: bool, teach: bool, @@ -706,6 +709,7 @@ impl EmitterWriter { pub fn stderr( color_config: ColorConfig, source_map: Option<Lrc<SourceMap>>, + fluent_bundle: Option<Lrc<FluentBundle>>, fallback_bundle: Lrc<FluentBundle>, short_message: bool, teach: bool, @@ -716,6 +720,7 @@ impl EmitterWriter { EmitterWriter { dst, sm: source_map, + fluent_bundle, fallback_bundle, short_message, teach, @@ -728,6 +733,7 @@ impl EmitterWriter { pub fn new( dst: Box<dyn Write + Send>, source_map: Option<Lrc<SourceMap>>, + fluent_bundle: Option<Lrc<FluentBundle>>, fallback_bundle: Lrc<FluentBundle>, short_message: bool, teach: bool, @@ -738,6 +744,7 @@ impl EmitterWriter { EmitterWriter { dst: Raw(dst, colored), sm: source_map, + fluent_bundle, fallback_bundle, short_message, teach, diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 3e7bcf2d37e..f78490da245 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -37,6 +37,7 @@ pub struct JsonEmitter { dst: Box<dyn Write + Send>, registry: Option<Registry>, sm: Lrc<SourceMap>, + fluent_bundle: Option<Lrc<FluentBundle>>, fallback_bundle: Lrc<FluentBundle>, pretty: bool, ui_testing: bool, @@ -49,6 +50,7 @@ impl JsonEmitter { pub fn stderr( registry: Option<Registry>, source_map: Lrc<SourceMap>, + fluent_bundle: Option<Lrc<FluentBundle>>, fallback_bundle: Lrc<FluentBundle>, pretty: bool, json_rendered: HumanReadableErrorType, @@ -59,6 +61,7 @@ impl JsonEmitter { dst: Box::new(io::BufWriter::new(io::stderr())), registry, sm: source_map, + fluent_bundle, fallback_bundle, pretty, ui_testing: false, @@ -71,6 +74,7 @@ impl JsonEmitter { pub fn basic( pretty: bool, json_rendered: HumanReadableErrorType, + fluent_bundle: Option<Lrc<FluentBundle>>, fallback_bundle: Lrc<FluentBundle>, terminal_width: Option<usize>, macro_backtrace: bool, @@ -79,6 +83,7 @@ impl JsonEmitter { JsonEmitter::stderr( None, Lrc::new(SourceMap::new(file_path_mapping)), + fluent_bundle, fallback_bundle, pretty, json_rendered, @@ -91,6 +96,7 @@ impl JsonEmitter { dst: Box<dyn Write + Send>, registry: Option<Registry>, source_map: Lrc<SourceMap>, + fluent_bundle: Option<Lrc<FluentBundle>>, fallback_bundle: Lrc<FluentBundle>, pretty: bool, json_rendered: HumanReadableErrorType, @@ -101,6 +107,7 @@ impl JsonEmitter { dst, registry, sm: source_map, + fluent_bundle, fallback_bundle, pretty, ui_testing: false, @@ -182,7 +189,7 @@ impl Emitter for JsonEmitter { } fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { - None + self.fluent_bundle.as_ref() } fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> { @@ -395,6 +402,7 @@ impl Diagnostic { .new_emitter( Box::new(buf), Some(je.sm.clone()), + je.fluent_bundle.clone(), je.fallback_bundle.clone(), false, je.terminal_width, diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index fa0ccd65d06..4174a85204f 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -39,13 +39,15 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { rustc_span::create_default_session_globals_then(|| { let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned()); - let fallback_bundle = crate::fallback_fluent_bundle(); + let fallback_bundle = + crate::fallback_fluent_bundle().expect("failed to load fallback fluent bundle"); let output = Arc::new(Mutex::new(Vec::new())); let je = JsonEmitter::new( Box::new(Shared { data: output.clone() }), None, sm, + None, fallback_bundle, true, HumanReadableErrorType::Short(ColorConfig::Never), diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 04a0a9f7b73..5db6614c141 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -32,7 +32,8 @@ use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{self, Lock, Lrc}; use rustc_data_structures::AtomicRef; pub use rustc_error_messages::{ - fallback_fluent_bundle, DiagnosticMessage, FluentBundle, MultiSpan, SpanLabel, + fallback_fluent_bundle, fluent_bundle, DiagnosticMessage, FluentBundle, LanguageIdentifier, + MultiSpan, SpanLabel, }; pub use rustc_lint_defs::{pluralize, Applicability}; use rustc_serialize::json::Json; @@ -544,11 +545,13 @@ impl Handler { can_emit_warnings: bool, treat_err_as_bug: Option<NonZeroUsize>, sm: Option<Lrc<SourceMap>>, + fluent_bundle: Option<Lrc<FluentBundle>>, fallback_bundle: Lrc<FluentBundle>, ) -> Self { Self::with_tty_emitter_and_flags( color_config, sm, + fluent_bundle, fallback_bundle, HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, ) @@ -557,12 +560,14 @@ impl Handler { pub fn with_tty_emitter_and_flags( color_config: ColorConfig, sm: Option<Lrc<SourceMap>>, + fluent_bundle: Option<Lrc<FluentBundle>>, fallback_bundle: Lrc<FluentBundle>, flags: HandlerFlags, ) -> Self { let emitter = Box::new(EmitterWriter::stderr( color_config, sm, + fluent_bundle, fallback_bundle, false, false, diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index 65128b91770..406095ab5e0 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -127,7 +127,8 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: & create_default_session_if_not_set_then(|_| { let output = Arc::new(Mutex::new(Vec::new())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle(); + let fallback_bundle = + rustc_errors::fallback_fluent_bundle().expect("failed to load fallback fluent bundle"); let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned()); @@ -143,6 +144,7 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: & let emitter = EmitterWriter::new( Box::new(Shared { data: output.clone() }), Some(source_map.clone()), + None, fallback_bundle, false, false, diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 4182a5d0711..5a447aa6237 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2856,6 +2856,7 @@ crate mod dep_tracking { use crate::lint; use crate::options::WasiExecModel; use crate::utils::{NativeLib, NativeLibKind}; + use rustc_errors::LanguageIdentifier; use rustc_feature::UnstableFeatures; use rustc_span::edition::Edition; use rustc_span::RealFileName; @@ -2948,6 +2949,7 @@ crate mod dep_tracking { LocationDetail, BranchProtection, OomStrategy, + LanguageIdentifier, ); impl<T1, T2> DepTrackingHash for (T1, T2) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 463c5c9d8b5..7303d701ae1 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -4,6 +4,7 @@ use crate::early_error; use crate::lint; use crate::search_paths::SearchPath; use crate::utils::NativeLib; +use rustc_errors::LanguageIdentifier; use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy, SanitizerSet}; use rustc_target::spec::{ RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, @@ -365,6 +366,7 @@ mod desc { pub const parse_string: &str = "a string"; pub const parse_opt_string: &str = parse_string; pub const parse_string_push: &str = parse_string; + pub const parse_opt_langid: &str = "a language identifier"; pub const parse_opt_pathbuf: &str = "a path"; pub const parse_list: &str = "a space-separated list of strings"; pub const parse_opt_comma_list: &str = "a comma-separated list of strings"; @@ -487,6 +489,17 @@ mod parse { } } + /// Parse an optional language identifier, e.g. `en-US` or `zh-CN`. + crate fn parse_opt_langid(slot: &mut Option<LanguageIdentifier>, v: Option<&str>) -> bool { + match v { + Some(s) => { + *slot = rustc_errors::LanguageIdentifier::from_str(s).ok(); + true + } + None => false, + } + } + crate fn parse_opt_pathbuf(slot: &mut Option<PathBuf>, v: Option<&str>) -> bool { match v { Some(s) => { @@ -1462,6 +1475,13 @@ options! { "the directory the intermediate files are written to"), terminal_width: Option<usize> = (None, parse_opt_number, [UNTRACKED], "set the current terminal width"), + // Diagnostics are considered side-effects of a query (see `QuerySideEffects`) and are saved + // alongside query results and changes to translation options can affect diagnostics - so + // translation options should be tracked. + translate_lang: Option<LanguageIdentifier> = (None, parse_opt_langid, [TRACKED], + "language identifier for diagnostic output"), + translate_additional_ftl: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], + "additional fluent translation to preferentially use (for testing translation)"), tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED], "select processor to schedule for (`rustc --print target-cpus` for details)"), thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED], diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 49c1a1be201..6b99e011c45 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -174,13 +174,15 @@ pub struct ParseSess { impl ParseSess { /// Used for testing. pub fn new(file_path_mapping: FilePathMapping) -> Self { - let fallback_bundle = fallback_fluent_bundle(); + let fallback_bundle = + fallback_fluent_bundle().expect("failed to load fallback fluent bundle"); let sm = Lrc::new(SourceMap::new(file_path_mapping)); let handler = Handler::with_tty_emitter( ColorConfig::Auto, true, None, Some(sm.clone()), + None, fallback_bundle, ); ParseSess::with_span_handler(handler, sm) @@ -211,10 +213,11 @@ impl ParseSess { } pub fn with_silent_emitter(fatal_note: Option<String>) -> Self { - let fallback_bundle = fallback_fluent_bundle(); + let fallback_bundle = + fallback_fluent_bundle().expect("failed to load fallback fluent bundle"); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let fatal_handler = - Handler::with_tty_emitter(ColorConfig::Auto, false, None, None, fallback_bundle); + Handler::with_tty_emitter(ColorConfig::Auto, false, None, None, None, fallback_bundle); let handler = Handler::with_emitter( false, None, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 9a11f537c25..76a2c579990 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -20,8 +20,8 @@ use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType}; use rustc_errors::json::JsonEmitter; use rustc_errors::registry::Registry; use rustc_errors::{ - fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, ErrorGuaranteed, - FluentBundle, MultiSpan, + fallback_fluent_bundle, fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, + ErrorGuaranteed, FluentBundle, MultiSpan, }; use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; @@ -1069,6 +1069,7 @@ fn default_emitter( sopts: &config::Options, registry: rustc_errors::registry::Registry, source_map: Lrc<SourceMap>, + bundle: Option<Lrc<FluentBundle>>, fallback_bundle: Lrc<FluentBundle>, emitter_dest: Option<Box<dyn Write + Send>>, ) -> Box<dyn Emitter + sync::Send> { @@ -1080,6 +1081,7 @@ fn default_emitter( if let HumanReadableErrorType::AnnotateSnippet(_) = kind { let emitter = AnnotateSnippetEmitterWriter::new( Some(source_map), + bundle, fallback_bundle, short, macro_backtrace, @@ -1090,6 +1092,7 @@ fn default_emitter( None => EmitterWriter::stderr( color_config, Some(source_map), + bundle, fallback_bundle, short, sopts.debugging_opts.teach, @@ -1099,6 +1102,7 @@ fn default_emitter( Some(dst) => EmitterWriter::new( dst, Some(source_map), + bundle, fallback_bundle, short, false, // no teach messages when writing to a buffer @@ -1114,6 +1118,7 @@ fn default_emitter( JsonEmitter::stderr( Some(registry), source_map, + bundle, fallback_bundle, pretty, json_rendered, @@ -1127,6 +1132,7 @@ fn default_emitter( dst, Some(registry), source_map, + bundle, fallback_bundle, pretty, json_rendered, @@ -1198,9 +1204,15 @@ pub fn build_session( hash_kind, )); - let fallback_bundle = fallback_fluent_bundle(); + let bundle = fluent_bundle( + &sysroot, + sopts.debugging_opts.translate_lang.clone(), + sopts.debugging_opts.translate_additional_ftl.as_deref(), + ) + .expect("failed to load fluent bundle"); + let fallback_bundle = fallback_fluent_bundle().expect("failed to load fallback fluent bundle"); let emitter = - default_emitter(&sopts, registry, source_map.clone(), fallback_bundle.clone(), write_dest); + default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle, write_dest); let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags( emitter, @@ -1433,13 +1445,14 @@ pub enum IncrCompSession { } fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler { - let fallback_bundle = fallback_fluent_bundle(); + let fallback_bundle = fallback_fluent_bundle().expect("failed to load fallback fluent bundle"); let emitter: Box<dyn Emitter + sync::Send> = match output { config::ErrorOutputType::HumanReadable(kind) => { let (short, color_config) = kind.unzip(); Box::new(EmitterWriter::stderr( color_config, None, + None, fallback_bundle, short, false, @@ -1448,7 +1461,7 @@ fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler )) } config::ErrorOutputType::Json { pretty, json_rendered } => { - Box::new(JsonEmitter::basic(pretty, json_rendered, fallback_bundle, None, false)) + Box::new(JsonEmitter::basic(pretty, json_rendered, None, fallback_bundle, None, false)) } }; rustc_errors::Handler::with_emitter(true, None, emitter) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 44b6b4b62ce..7cd799d84f0 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -143,7 +143,8 @@ crate fn new_handler( source_map: Option<Lrc<source_map::SourceMap>>, debugging_opts: &DebuggingOptions, ) -> rustc_errors::Handler { - let fallback_bundle = rustc_errors::fallback_fluent_bundle(); + let fallback_bundle = + rustc_errors::fallback_fluent_bundle().expect("failed to load fallback fluent bundle"); let emitter: Box<dyn Emitter + sync::Send> = match error_format { ErrorOutputType::HumanReadable(kind) => { let (short, color_config) = kind.unzip(); @@ -151,6 +152,7 @@ crate fn new_handler( EmitterWriter::stderr( color_config, source_map.map(|sm| sm as _), + None, fallback_bundle, short, debugging_opts.teach, @@ -168,6 +170,7 @@ crate fn new_handler( JsonEmitter::stderr( None, source_map, + None, fallback_bundle, pretty, json_rendered, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index fc079619786..94e3c996400 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -537,10 +537,12 @@ crate fn make_test( // Any errors in parsing should also appear when the doctest is compiled for real, so just // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle(); + let fallback_bundle = rustc_errors::fallback_fluent_bundle() + .expect("failed to load fallback fluent bundle"); supports_color = EmitterWriter::stderr( ColorConfig::Auto, None, + None, fallback_bundle.clone(), false, false, @@ -552,6 +554,7 @@ crate fn make_test( let emitter = EmitterWriter::new( box io::sink(), None, + None, fallback_bundle, false, false, diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 10f1fc05404..01bfc522e1b 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -32,7 +32,8 @@ struct SyntaxChecker<'a, 'tcx> { impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { fn check_rust_syntax(&self, item: &clean::Item, dox: &str, code_block: RustCodeBlock) { let buffer = Lrc::new(Lock::new(Buffer::default())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle(); + let fallback_bundle = + rustc_errors::fallback_fluent_bundle().expect("failed to load fallback fluent bundle"); let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle }; let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); diff --git a/src/test/run-make/translation/Makefile b/src/test/run-make/translation/Makefile new file mode 100644 index 00000000000..22a3bf57ecf --- /dev/null +++ b/src/test/run-make/translation/Makefile @@ -0,0 +1,33 @@ +include ../../run-make-fulldeps/tools.mk + +# This test uses `ln -s` rather than copying to save testing time, but its +# usage doesn't work on Windows. +# ignore-windows + +SYSROOT:=$(shell $(RUSTC) --print sysroot) +FAKEROOT=$(TMPDIR)/fakeroot + +all: normal custom sysroot + +normal: basic-translation.rs + $(RUSTC) $< 2>&1 | grep "struct literal body without path" + +custom: basic-translation.rs basic-translation.ftl + $(RUSTC) $< -Ztranslate-additional-ftl=$(CURDIR)/basic-translation.ftl 2>&1 | grep "this is a test message" + +# Make a local copy of the sysroot and add the custom locale to it. +sysroot: basic-translation.rs basic-translation.ftl + mkdir $(FAKEROOT) + ln -s $(SYSROOT)/* $(FAKEROOT) + rm -f $(FAKEROOT)/lib + mkdir $(FAKEROOT)/lib + ln -s $(SYSROOT)/lib/* $(FAKEROOT)/lib + rm -f $(FAKEROOT)/lib/rustlib + mkdir $(FAKEROOT)/lib/rustlib + ln -s $(SYSROOT)/lib/rustlib/* $(FAKEROOT)/lib/rustlib + rm -f $(FAKEROOT)/lib/rustlib/src + mkdir $(FAKEROOT)/lib/rustlib/src + ln -s $(SYSROOT)/lib/rustlib/src/* $(FAKEROOT)/lib/rustlib/src + mkdir -p $(FAKEROOT)/share/locale/zh-CN/ + ln -s $(CURDIR)/basic-translation.ftl $(FAKEROOT)/share/locale/zh-CN/basic-translation.ftl + $(RUSTC) $< --sysroot $(FAKEROOT) -Ztranslate-lang=zh-CN 2>&1 | grep "this is a test message" diff --git a/src/test/run-make/translation/basic-translation.ftl b/src/test/run-make/translation/basic-translation.ftl new file mode 100644 index 00000000000..4681b879cda --- /dev/null +++ b/src/test/run-make/translation/basic-translation.ftl @@ -0,0 +1,2 @@ +parser-struct-literal-body-without-path = this is a test message + .suggestion = this is a test suggestion diff --git a/src/test/run-make/translation/basic-translation.rs b/src/test/run-make/translation/basic-translation.rs new file mode 100644 index 00000000000..b8f5bff3153 --- /dev/null +++ b/src/test/run-make/translation/basic-translation.rs @@ -0,0 +1,18 @@ +// Exact error being tested isn't relevant, it just needs to be known that it uses Fluent-backed +// diagnostics. + +struct Foo { + val: (), +} + +fn foo() -> Foo { + val: (), +} + +fn main() { + let x = foo(); + x.val == 42; + let x = { + val: (), + }; +} diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index b836363b31b..e08e9e49993 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -621,10 +621,12 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let filename = FileName::anon_source_code(&code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle(); + let fallback_bundle = rustc_errors::fallback_fluent_bundle() + .expect("failed to load fallback fluent bundle"); let emitter = EmitterWriter::new( Box::new(io::sink()), None, + None, fallback_bundle, false, false, diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index bfce787af5e..f04535b2bea 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -165,10 +165,12 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // Separate the output with an empty line eprintln!(); - let fallback_bundle = rustc_errors::fallback_fluent_bundle(); + let fallback_bundle = rustc_errors::fallback_fluent_bundle() + .expect("failed to load fallback fluent bundle"); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, None, + None, fallback_bundle, false, false, diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 4563dbf6c16..6967028d55f 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -114,10 +114,12 @@ fn default_handler( let emitter = if hide_parse_errors { silent_emitter() } else { - let fallback_bundle = rustc_errors::fallback_fluent_bundle(); + let fallback_bundle = rustc_errors::fallback_fluent_bundle() + .expect("failed to load fallback fluent bundle"); Box::new(EmitterWriter::stderr( color_cfg, Some(source_map.clone()), + None, fallback_bundle, false, false, |
