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>
This commit is contained in:
David Wood 2022-03-28 09:36:20 +01:00
parent 7f91697b50
commit d5119c5b9f
23 changed files with 322 additions and 46 deletions

View file

@ -1238,16 +1238,6 @@ dependencies = [
"miniz_oxide",
]
[[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"
@ -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",

View file

@ -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,

View file

@ -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" }

View file

@ -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>;
/// 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");
#[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.
#[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.

View file

@ -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.

View file

@ -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,

View file

@ -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,

View file

@ -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),

View file

@ -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,

View file

@ -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,

View file

@ -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)

View file

@ -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],

View file

@ -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,

View file

@ -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)

View file

@ -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,

View file

@ -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,

View file

@ -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()));

View file

@ -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"

View file

@ -0,0 +1,2 @@
parser-struct-literal-body-without-path = this is a test message
.suggestion = this is a test suggestion

View file

@ -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: (),
};
}

View file

@ -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,

View file

@ -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,

View file

@ -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,