Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit d5119c5

Browse files
committed
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>
1 parent 7f91697 commit d5119c5

File tree

23 files changed

+322
-46
lines changed

23 files changed

+322
-46
lines changed

Cargo.lock

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,16 +1238,6 @@ dependencies = [
12381238
"miniz_oxide",
12391239
]
12401240

1241-
[[package]]
1242-
name = "fluent"
1243-
version = "0.16.0"
1244-
source = "registry+https://github.com/rust-lang/crates.io-index"
1245-
checksum = "61f69378194459db76abd2ce3952b790db103ceb003008d3d50d97c41ff847a7"
1246-
dependencies = [
1247-
"fluent-bundle",
1248-
"unic-langid",
1249-
]
1250-
12511241
[[package]]
12521242
name = "fluent-bundle"
12531243
version = "0.15.2"
@@ -3719,7 +3709,8 @@ version = "0.0.0"
37193709
name = "rustc_error_messages"
37203710
version = "0.0.0"
37213711
dependencies = [
3722-
"fluent",
3712+
"fluent-bundle",
3713+
"fluent-syntax",
37233714
"rustc_data_structures",
37243715
"rustc_macros",
37253716
"rustc_serialize",

compiler/rustc_driver/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1172,10 +1172,12 @@ static DEFAULT_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send +
11721172
/// When `install_ice_hook` is called, this function will be called as the panic
11731173
/// hook.
11741174
pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
1175-
let fallback_bundle = rustc_errors::fallback_fluent_bundle();
1175+
let fallback_bundle =
1176+
rustc_errors::fallback_fluent_bundle().expect("failed to load fallback fluent bundle");
11761177
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
11771178
rustc_errors::ColorConfig::Auto,
11781179
None,
1180+
None,
11791181
fallback_bundle,
11801182
false,
11811183
false,

compiler/rustc_error_messages/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ edition = "2021"
77
doctest = false
88

99
[dependencies]
10-
fluent = "0.16.0"
10+
fluent-bundle = "0.15.2"
11+
fluent-syntax = "0.11"
1112
rustc_data_structures = { path = "../rustc_data_structures" }
1213
rustc_serialize = { path = "../rustc_serialize" }
1314
rustc_span = { path = "../rustc_span" }

compiler/rustc_error_messages/src/lib.rs

Lines changed: 156 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,169 @@
1+
#![feature(path_try_exists)]
2+
3+
use fluent_bundle::FluentResource;
4+
use fluent_syntax::parser::ParserError;
15
use rustc_data_structures::sync::Lrc;
26
use rustc_macros::{Decodable, Encodable};
37
use rustc_span::Span;
48
use std::borrow::Cow;
5-
use tracing::debug;
9+
use std::error::Error;
10+
use std::fmt;
11+
use std::fs;
12+
use std::io;
13+
use std::path::Path;
14+
use tracing::{instrument, trace};
615

7-
pub use fluent::{FluentArgs, FluentValue};
16+
pub use fluent_bundle::{FluentArgs, FluentError, FluentValue};
17+
pub use unic_langid::{langid, LanguageIdentifier};
818

919
static FALLBACK_FLUENT_RESOURCE: &'static str = include_str!("../locales/en-US/diagnostics.ftl");
1020

11-
pub type FluentBundle = fluent::FluentBundle<fluent::FluentResource>;
21+
pub type FluentBundle = fluent_bundle::FluentBundle<FluentResource>;
22+
23+
#[derive(Debug)]
24+
pub enum TranslationBundleError {
25+
/// Failed to read from `.ftl` file.
26+
ReadFtl(io::Error),
27+
/// Failed to parse contents of `.ftl` file.
28+
ParseFtl(ParserError),
29+
/// Failed to add `FluentResource` to `FluentBundle`.
30+
AddResource(FluentError),
31+
/// `$sysroot/share/locale/$locale` does not exist.
32+
MissingLocale(io::Error),
33+
/// Cannot read directory entries of `$sysroot/share/locale/$locale`.
34+
ReadLocalesDir(io::Error),
35+
/// Cannot read directory entry of `$sysroot/share/locale/$locale`.
36+
ReadLocalesDirEntry(io::Error),
37+
/// `$sysroot/share/locale/$locale` is not a directory.
38+
LocaleIsNotDir,
39+
}
40+
41+
impl fmt::Display for TranslationBundleError {
42+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43+
match self {
44+
TranslationBundleError::ReadFtl(e) => write!(f, "could not read ftl file: {}", e),
45+
TranslationBundleError::ParseFtl(e) => {
46+
write!(f, "could not parse ftl file: {}", e)
47+
}
48+
TranslationBundleError::AddResource(e) => write!(f, "failed to add resource: {}", e),
49+
TranslationBundleError::MissingLocale(e) => {
50+
write!(f, "missing locale directory: {}", e)
51+
}
52+
TranslationBundleError::ReadLocalesDir(e) => {
53+
write!(f, "could not read locales dir: {}", e)
54+
}
55+
TranslationBundleError::ReadLocalesDirEntry(e) => {
56+
write!(f, "could not read locales dir entry: {}", e)
57+
}
58+
TranslationBundleError::LocaleIsNotDir => {
59+
write!(f, "`$sysroot/share/locales/$locale` is not a directory")
60+
}
61+
}
62+
}
63+
}
64+
65+
impl Error for TranslationBundleError {
66+
fn source(&self) -> Option<&(dyn Error + 'static)> {
67+
match self {
68+
TranslationBundleError::ReadFtl(e) => Some(e),
69+
TranslationBundleError::ParseFtl(e) => Some(e),
70+
TranslationBundleError::AddResource(e) => Some(e),
71+
TranslationBundleError::MissingLocale(e) => Some(e),
72+
TranslationBundleError::ReadLocalesDir(e) => Some(e),
73+
TranslationBundleError::ReadLocalesDirEntry(e) => Some(e),
74+
TranslationBundleError::LocaleIsNotDir => None,
75+
}
76+
}
77+
}
78+
79+
impl From<(FluentResource, Vec<ParserError>)> for TranslationBundleError {
80+
fn from((_, mut errs): (FluentResource, Vec<ParserError>)) -> Self {
81+
TranslationBundleError::ParseFtl(errs.pop().expect("failed ftl parse with no errors"))
82+
}
83+
}
84+
85+
impl From<Vec<FluentError>> for TranslationBundleError {
86+
fn from(mut errs: Vec<FluentError>) -> Self {
87+
TranslationBundleError::AddResource(
88+
errs.pop().expect("failed adding resource to bundle with no errors"),
89+
)
90+
}
91+
}
92+
93+
/// Returns Fluent bundle with the user's locale resources from
94+
/// `$sysroot/share/locale/$requested_locale/*.ftl`.
95+
///
96+
/// If `-Z additional-ftl-path` was provided, load that resource and add it to the bundle
97+
/// (overriding any conflicting messages).
98+
#[instrument(level = "trace")]
99+
pub fn fluent_bundle(
100+
sysroot: &Path,
101+
requested_locale: Option<LanguageIdentifier>,
102+
additional_ftl_path: Option<&Path>,
103+
) -> Result<Option<Lrc<FluentBundle>>, TranslationBundleError> {
104+
if requested_locale.is_none() && additional_ftl_path.is_none() {
105+
return Ok(None);
106+
}
107+
108+
// If there is only `-Z additional-ftl-path`, assume locale is "en-US", otherwise use user
109+
// provided locale.
110+
let locale = requested_locale.clone().unwrap_or_else(|| langid!("en-US"));
111+
trace!(?locale);
112+
let mut bundle = FluentBundle::new(vec![locale]);
113+
114+
if let Some(requested_locale) = requested_locale {
115+
let mut sysroot = sysroot.to_path_buf();
116+
sysroot.push("share");
117+
sysroot.push("locale");
118+
sysroot.push(requested_locale.to_string());
119+
trace!(?sysroot);
120+
121+
let _ = sysroot.try_exists().map_err(TranslationBundleError::MissingLocale)?;
122+
123+
if !sysroot.is_dir() {
124+
return Err(TranslationBundleError::LocaleIsNotDir);
125+
}
126+
127+
for entry in sysroot.read_dir().map_err(TranslationBundleError::ReadLocalesDir)? {
128+
let entry = entry.map_err(TranslationBundleError::ReadLocalesDirEntry)?;
129+
let path = entry.path();
130+
trace!(?path);
131+
if path.extension().and_then(|s| s.to_str()) != Some("ftl") {
132+
trace!("skipping");
133+
continue;
134+
}
135+
136+
let resource_str = fs::read_to_string(path).map_err(TranslationBundleError::ReadFtl)?;
137+
let resource =
138+
FluentResource::try_new(resource_str).map_err(TranslationBundleError::from)?;
139+
trace!(?resource);
140+
bundle.add_resource(resource).map_err(TranslationBundleError::from)?;
141+
}
142+
}
143+
144+
if let Some(additional_ftl_path) = additional_ftl_path {
145+
let resource_str =
146+
fs::read_to_string(additional_ftl_path).map_err(TranslationBundleError::ReadFtl)?;
147+
let resource =
148+
FluentResource::try_new(resource_str).map_err(TranslationBundleError::from)?;
149+
trace!(?resource);
150+
bundle.add_resource_overriding(resource);
151+
}
152+
153+
let bundle = Lrc::new(bundle);
154+
Ok(Some(bundle))
155+
}
12156

13-
/// Return the default `FluentBundle` with standard en-US diagnostic messages.
14-
pub fn fallback_fluent_bundle() -> Lrc<FluentBundle> {
15-
let fallback_resource = fluent::FluentResource::try_new(FALLBACK_FLUENT_RESOURCE.to_string())
16-
.expect("failed to parse ftl resource");
17-
debug!(?fallback_resource);
18-
let mut fallback_bundle = FluentBundle::new(vec![unic_langid::langid!("en-US")]);
19-
fallback_bundle.add_resource(fallback_resource).expect("failed to add resource to bundle");
157+
/// Return the default `FluentBundle` with standard "en-US" diagnostic messages.
158+
#[instrument(level = "trace")]
159+
pub fn fallback_fluent_bundle() -> Result<Lrc<FluentBundle>, TranslationBundleError> {
160+
let fallback_resource = FluentResource::try_new(FALLBACK_FLUENT_RESOURCE.to_string())
161+
.map_err(TranslationBundleError::from)?;
162+
trace!(?fallback_resource);
163+
let mut fallback_bundle = FluentBundle::new(vec![langid!("en-US")]);
164+
fallback_bundle.add_resource(fallback_resource).map_err(TranslationBundleError::from)?;
20165
let fallback_bundle = Lrc::new(fallback_bundle);
21-
fallback_bundle
166+
Ok(fallback_bundle)
22167
}
23168

24169
/// Identifier for the Fluent message/attribute corresponding to a diagnostic message.

compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use rustc_span::SourceFile;
2121
/// Generates diagnostics using annotate-snippet
2222
pub struct AnnotateSnippetEmitterWriter {
2323
source_map: Option<Lrc<SourceMap>>,
24+
fluent_bundle: Option<Lrc<FluentBundle>>,
2425
fallback_bundle: Lrc<FluentBundle>,
2526

2627
/// If true, hides the longer explanation text
@@ -63,7 +64,7 @@ impl Emitter for AnnotateSnippetEmitterWriter {
6364
}
6465

6566
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
66-
None
67+
self.fluent_bundle.as_ref()
6768
}
6869

6970
fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> {
@@ -99,11 +100,19 @@ fn annotation_type_for_level(level: Level) -> AnnotationType {
99100
impl AnnotateSnippetEmitterWriter {
100101
pub fn new(
101102
source_map: Option<Lrc<SourceMap>>,
103+
fluent_bundle: Option<Lrc<FluentBundle>>,
102104
fallback_bundle: Lrc<FluentBundle>,
103105
short_message: bool,
104106
macro_backtrace: bool,
105107
) -> Self {
106-
Self { source_map, fallback_bundle, short_message, ui_testing: false, macro_backtrace }
108+
Self {
109+
source_map,
110+
fluent_bundle,
111+
fallback_bundle,
112+
short_message,
113+
ui_testing: false,
114+
macro_backtrace,
115+
}
107116
}
108117

109118
/// Allows to modify `Self` to enable or disable the `ui_testing` flag.

compiler/rustc_errors/src/emitter.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ impl HumanReadableErrorType {
5959
self,
6060
dst: Box<dyn Write + Send>,
6161
source_map: Option<Lrc<SourceMap>>,
62+
bundle: Option<Lrc<FluentBundle>>,
6263
fallback_bundle: Lrc<FluentBundle>,
6364
teach: bool,
6465
terminal_width: Option<usize>,
@@ -69,6 +70,7 @@ impl HumanReadableErrorType {
6970
EmitterWriter::new(
7071
dst,
7172
source_map,
73+
bundle,
7274
fallback_bundle,
7375
short,
7476
teach,
@@ -568,7 +570,7 @@ impl Emitter for EmitterWriter {
568570
}
569571

570572
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
571-
None
573+
self.fluent_bundle.as_ref()
572574
}
573575

574576
fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> {
@@ -686,6 +688,7 @@ impl ColorConfig {
686688
pub struct EmitterWriter {
687689
dst: Destination,
688690
sm: Option<Lrc<SourceMap>>,
691+
fluent_bundle: Option<Lrc<FluentBundle>>,
689692
fallback_bundle: Lrc<FluentBundle>,
690693
short_message: bool,
691694
teach: bool,
@@ -706,6 +709,7 @@ impl EmitterWriter {
706709
pub fn stderr(
707710
color_config: ColorConfig,
708711
source_map: Option<Lrc<SourceMap>>,
712+
fluent_bundle: Option<Lrc<FluentBundle>>,
709713
fallback_bundle: Lrc<FluentBundle>,
710714
short_message: bool,
711715
teach: bool,
@@ -716,6 +720,7 @@ impl EmitterWriter {
716720
EmitterWriter {
717721
dst,
718722
sm: source_map,
723+
fluent_bundle,
719724
fallback_bundle,
720725
short_message,
721726
teach,
@@ -728,6 +733,7 @@ impl EmitterWriter {
728733
pub fn new(
729734
dst: Box<dyn Write + Send>,
730735
source_map: Option<Lrc<SourceMap>>,
736+
fluent_bundle: Option<Lrc<FluentBundle>>,
731737
fallback_bundle: Lrc<FluentBundle>,
732738
short_message: bool,
733739
teach: bool,
@@ -738,6 +744,7 @@ impl EmitterWriter {
738744
EmitterWriter {
739745
dst: Raw(dst, colored),
740746
sm: source_map,
747+
fluent_bundle,
741748
fallback_bundle,
742749
short_message,
743750
teach,

0 commit comments

Comments
 (0)