errors: lazily load fallback fluent bundle

Loading the fallback bundle in compilation sessions that won't go on to
emit any errors unnecessarily degrades compile time performance, so
lazily create the Fluent bundle when it is first required.

Signed-off-by: David Wood <david.wood@huawei.com>
This commit is contained in:
David Wood 2022-04-12 09:34:40 +01:00
parent f6cef572d6
commit 9bfe0e39e4
19 changed files with 94 additions and 81 deletions

View File

@ -1731,7 +1731,7 @@ fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
None
}
fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> {
fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
panic!("shared emitter attempted to translate a diagnostic");
}
}

View File

@ -1173,7 +1173,7 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
/// hook.
pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle");
rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
rustc_errors::ColorConfig::Auto,
None,

View File

@ -0,0 +1,3 @@
parser-struct-literal-body-without-path =
struct literal body without path
.suggestion = you might have forgotten to add the struct literal inside the block

View File

@ -1,7 +1,3 @@
parser-struct-literal-body-without-path =
struct literal body without path
.suggestion = you might have forgotten to add the struct literal inside the block
typeck-field-multiply-specified-in-initializer =
field `{$ident}` specified more than once
.label = used more than once

View File

@ -1,5 +1,7 @@
#![feature(let_chains)]
#![feature(once_cell)]
#![feature(path_try_exists)]
#![feature(type_alias_impl_trait)]
use fluent_bundle::FluentResource;
use fluent_syntax::parser::ParserError;
@ -14,6 +16,11 @@
use std::path::{Path, PathBuf};
use tracing::{instrument, trace};
#[cfg(not(parallel_compiler))]
use std::lazy::Lazy;
#[cfg(parallel_compiler)]
use std::lazy::SyncLazy as Lazy;
#[cfg(parallel_compiler)]
use intl_memoizer::concurrent::IntlLangMemoizer;
#[cfg(not(parallel_compiler))]
@ -22,7 +29,8 @@
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 static DEFAULT_LOCALE_RESOURCES: &'static [&'static str] =
&[include_str!("../locales/en-US/typeck.ftl"), include_str!("../locales/en-US/parser.ftl")];
pub type FluentBundle = fluent_bundle::bundle::FluentBundle<FluentResource, IntlLangMemoizer>;
@ -192,20 +200,30 @@ pub fn fluent_bundle(
Ok(Some(bundle))
}
/// Type alias for the result of `fallback_fluent_bundle` - a reference-counted pointer to a lazily
/// evaluated fluent bundle.
pub type LazyFallbackBundle = Lrc<Lazy<FluentBundle, impl FnOnce() -> FluentBundle>>;
/// Return the default `FluentBundle` with standard "en-US" diagnostic messages.
#[instrument(level = "trace")]
pub fn fallback_fluent_bundle(
resources: &'static [&'static str],
with_directionality_markers: bool,
) -> 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 = new_bundle(vec![langid!("en-US")]);
// See comment in `fluent_bundle`.
fallback_bundle.set_use_isolating(with_directionality_markers);
fallback_bundle.add_resource(fallback_resource).map_err(TranslationBundleError::from)?;
let fallback_bundle = Lrc::new(fallback_bundle);
Ok(fallback_bundle)
) -> LazyFallbackBundle {
Lrc::new(Lazy::new(move || {
let mut fallback_bundle = new_bundle(vec![langid!("en-US")]);
// See comment in `fluent_bundle`.
fallback_bundle.set_use_isolating(with_directionality_markers);
for resource in resources {
let resource = FluentResource::try_new(resource.to_string())
.expect("failed to parse fallback fluent resource");
trace!(?resource);
fallback_bundle.add_resource_overriding(resource);
}
fallback_bundle
}))
}
/// Identifier for the Fluent message/attribute corresponding to a diagnostic message.

View File

@ -8,8 +8,8 @@
use crate::emitter::FileWithAnnotatedLines;
use crate::snippet::Line;
use crate::{
CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle, Level,
MultiSpan, Style, SubDiagnostic,
CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle,
LazyFallbackBundle, Level, MultiSpan, Style, SubDiagnostic,
};
use annotate_snippets::display_list::{DisplayList, FormatOptions};
use annotate_snippets::snippet::*;
@ -22,7 +22,7 @@
pub struct AnnotateSnippetEmitterWriter {
source_map: Option<Lrc<SourceMap>>,
fluent_bundle: Option<Lrc<FluentBundle>>,
fallback_bundle: Lrc<FluentBundle>,
fallback_bundle: LazyFallbackBundle,
/// If true, hides the longer explanation text
short_message: bool,
@ -67,8 +67,8 @@ fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
self.fluent_bundle.as_ref()
}
fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> {
&self.fallback_bundle
fn fallback_fluent_bundle(&self) -> &FluentBundle {
&**self.fallback_bundle
}
fn should_show_explain(&self) -> bool {
@ -101,7 +101,7 @@ impl AnnotateSnippetEmitterWriter {
pub fn new(
source_map: Option<Lrc<SourceMap>>,
fluent_bundle: Option<Lrc<FluentBundle>>,
fallback_bundle: Lrc<FluentBundle>,
fallback_bundle: LazyFallbackBundle,
short_message: bool,
macro_backtrace: bool,
) -> Self {

View File

@ -16,7 +16,8 @@
use crate::styled_buffer::StyledBuffer;
use crate::{
CodeSuggestion, Diagnostic, DiagnosticArg, DiagnosticId, DiagnosticMessage, FluentBundle,
Handler, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, SuggestionStyle,
Handler, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight,
SuggestionStyle,
};
use rustc_lint_defs::pluralize;
@ -60,7 +61,7 @@ pub fn new_emitter(
dst: Box<dyn Write + Send>,
source_map: Option<Lrc<SourceMap>>,
bundle: Option<Lrc<FluentBundle>>,
fallback_bundle: Lrc<FluentBundle>,
fallback_bundle: LazyFallbackBundle,
teach: bool,
terminal_width: Option<usize>,
macro_backtrace: bool,
@ -233,7 +234,7 @@ fn supports_color(&self) -> bool {
/// 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) -> &Lrc<FluentBundle>;
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.
@ -579,8 +580,8 @@ fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
self.fluent_bundle.as_ref()
}
fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> {
&self.fallback_bundle
fn fallback_fluent_bundle(&self) -> &FluentBundle {
&**self.fallback_bundle
}
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
@ -635,7 +636,7 @@ fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
None
}
fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> {
fn fallback_fluent_bundle(&self) -> &FluentBundle {
panic!("silent emitter attempted to translate message")
}
@ -695,7 +696,7 @@ pub struct EmitterWriter {
dst: Destination,
sm: Option<Lrc<SourceMap>>,
fluent_bundle: Option<Lrc<FluentBundle>>,
fallback_bundle: Lrc<FluentBundle>,
fallback_bundle: LazyFallbackBundle,
short_message: bool,
teach: bool,
ui_testing: bool,
@ -716,7 +717,7 @@ pub fn stderr(
color_config: ColorConfig,
source_map: Option<Lrc<SourceMap>>,
fluent_bundle: Option<Lrc<FluentBundle>>,
fallback_bundle: Lrc<FluentBundle>,
fallback_bundle: LazyFallbackBundle,
short_message: bool,
teach: bool,
terminal_width: Option<usize>,
@ -740,7 +741,7 @@ pub fn new(
dst: Box<dyn Write + Send>,
source_map: Option<Lrc<SourceMap>>,
fluent_bundle: Option<Lrc<FluentBundle>>,
fallback_bundle: Lrc<FluentBundle>,
fallback_bundle: LazyFallbackBundle,
short_message: bool,
teach: bool,
colored: bool,

View File

@ -15,7 +15,9 @@
use crate::registry::Registry;
use crate::DiagnosticId;
use crate::ToolMetadata;
use crate::{CodeSuggestion, FluentBundle, MultiSpan, SpanLabel, SubDiagnostic};
use crate::{
CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic,
};
use rustc_lint_defs::Applicability;
use rustc_data_structures::sync::Lrc;
@ -38,7 +40,7 @@ pub struct JsonEmitter {
registry: Option<Registry>,
sm: Lrc<SourceMap>,
fluent_bundle: Option<Lrc<FluentBundle>>,
fallback_bundle: Lrc<FluentBundle>,
fallback_bundle: LazyFallbackBundle,
pretty: bool,
ui_testing: bool,
json_rendered: HumanReadableErrorType,
@ -51,7 +53,7 @@ pub fn stderr(
registry: Option<Registry>,
source_map: Lrc<SourceMap>,
fluent_bundle: Option<Lrc<FluentBundle>>,
fallback_bundle: Lrc<FluentBundle>,
fallback_bundle: LazyFallbackBundle,
pretty: bool,
json_rendered: HumanReadableErrorType,
terminal_width: Option<usize>,
@ -75,7 +77,7 @@ pub fn basic(
pretty: bool,
json_rendered: HumanReadableErrorType,
fluent_bundle: Option<Lrc<FluentBundle>>,
fallback_bundle: Lrc<FluentBundle>,
fallback_bundle: LazyFallbackBundle,
terminal_width: Option<usize>,
macro_backtrace: bool,
) -> JsonEmitter {
@ -97,7 +99,7 @@ pub fn new(
registry: Option<Registry>,
source_map: Lrc<SourceMap>,
fluent_bundle: Option<Lrc<FluentBundle>>,
fallback_bundle: Lrc<FluentBundle>,
fallback_bundle: LazyFallbackBundle,
pretty: bool,
json_rendered: HumanReadableErrorType,
terminal_width: Option<usize>,
@ -192,8 +194,8 @@ fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
self.fluent_bundle.as_ref()
}
fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> {
&self.fallback_bundle
fn fallback_fluent_bundle(&self) -> &FluentBundle {
&**self.fallback_bundle
}
fn should_show_explain(&self) -> bool {

View File

@ -40,7 +40,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
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(false).expect("failed to load fallback fluent bundle");
crate::fallback_fluent_bundle(rustc_error_messages::DEFAULT_LOCALE_RESOURCES, false);
let output = Arc::new(Mutex::new(Vec::new()));
let je = JsonEmitter::new(

View File

@ -33,7 +33,7 @@
use rustc_data_structures::AtomicRef;
pub use rustc_error_messages::{
fallback_fluent_bundle, fluent_bundle, DiagnosticMessage, FluentBundle, LanguageIdentifier,
MultiSpan, SpanLabel,
LazyFallbackBundle, MultiSpan, SpanLabel, DEFAULT_LOCALE_RESOURCES,
};
pub use rustc_lint_defs::{pluralize, Applicability};
use rustc_serialize::json::Json;
@ -547,7 +547,7 @@ pub fn with_tty_emitter(
treat_err_as_bug: Option<NonZeroUsize>,
sm: Option<Lrc<SourceMap>>,
fluent_bundle: Option<Lrc<FluentBundle>>,
fallback_bundle: Lrc<FluentBundle>,
fallback_bundle: LazyFallbackBundle,
) -> Self {
Self::with_tty_emitter_and_flags(
color_config,
@ -562,7 +562,7 @@ pub fn with_tty_emitter_and_flags(
color_config: ColorConfig,
sm: Option<Lrc<SourceMap>>,
fluent_bundle: Option<Lrc<FluentBundle>>,
fallback_bundle: Lrc<FluentBundle>,
fallback_bundle: LazyFallbackBundle,
flags: HandlerFlags,
) -> Self {
let emitter = Box::new(EmitterWriter::stderr(

View File

@ -127,8 +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(false)
.expect("failed to load fallback fluent bundle");
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
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());

View File

@ -173,8 +173,7 @@ pub struct ParseSess {
impl ParseSess {
/// Used for testing.
pub fn new(file_path_mapping: FilePathMapping) -> Self {
let fallback_bundle =
fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle");
let fallback_bundle = fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
let sm = Lrc::new(SourceMap::new(file_path_mapping));
let handler = Handler::with_tty_emitter(
ColorConfig::Auto,
@ -211,8 +210,7 @@ pub fn with_span_handler(handler: Handler, source_map: Lrc<SourceMap>) -> Self {
}
pub fn with_silent_emitter(fatal_note: Option<String>) -> Self {
let fallback_bundle =
fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle");
let fallback_bundle = fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
let fatal_handler =
Handler::with_tty_emitter(ColorConfig::Auto, false, None, None, None, fallback_bundle);

View File

@ -21,7 +21,7 @@
use rustc_errors::registry::Registry;
use rustc_errors::{
fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, EmissionGuarantee,
ErrorGuaranteed, FluentBundle, MultiSpan,
ErrorGuaranteed, FluentBundle, LazyFallbackBundle, MultiSpan,
};
use rustc_macros::HashStable_Generic;
pub use rustc_span::def_id::StableCrateId;
@ -1080,7 +1080,7 @@ fn default_emitter(
registry: rustc_errors::registry::Registry,
source_map: Lrc<SourceMap>,
bundle: Option<Lrc<FluentBundle>>,
fallback_bundle: Lrc<FluentBundle>,
fallback_bundle: LazyFallbackBundle,
emitter_dest: Option<Box<dyn Write + Send>>,
) -> Box<dyn Emitter + sync::Send> {
let macro_backtrace = sopts.debugging_opts.macro_backtrace;
@ -1215,17 +1215,10 @@ pub fn build_session(
hash_kind,
));
let fallback_bundle =
match fallback_fluent_bundle(sopts.debugging_opts.translate_directionality_markers) {
Ok(bundle) => bundle,
Err(e) => {
early_error(
sopts.error_format,
&format!("failed to load fallback fluent bundle: {e}"),
);
}
};
let fallback_bundle = fallback_fluent_bundle(
rustc_errors::DEFAULT_LOCALE_RESOURCES,
sopts.debugging_opts.translate_directionality_markers,
);
let emitter =
default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle, write_dest);
@ -1460,8 +1453,7 @@ pub enum IncrCompSession {
}
fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler {
let fallback_bundle =
fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle");
let fallback_bundle = fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
let emitter: Box<dyn Emitter + sync::Send> = match output {
config::ErrorOutputType::HumanReadable(kind) => {
let (short, color_config) = kind.unzip();

View File

@ -146,7 +146,7 @@ impl<'tcx> DocContext<'tcx> {
debugging_opts: &DebuggingOptions,
) -> rustc_errors::Handler {
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle");
rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
let emitter: Box<dyn Emitter + sync::Send> = match error_format {
ErrorOutputType::HumanReadable(kind) => {
let (short, color_config) = kind.unzip();

View File

@ -537,8 +537,8 @@ fn drop(&mut self) {
// 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(false)
.expect("failed to load fallback fluent bundle");
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
supports_color = EmitterWriter::stderr(
ColorConfig::Auto,
None,

View File

@ -1,6 +1,6 @@
//! Validates syntax inside Rust code blocks (\`\`\`rust).
use rustc_data_structures::sync::{Lock, Lrc};
use rustc_errors::{emitter::Emitter, Applicability, Diagnostic, Handler};
use rustc_errors::{emitter::Emitter, Applicability, Diagnostic, Handler, LazyFallbackBundle};
use rustc_middle::lint::LintDiagnosticBuilder;
use rustc_parse::parse_stream_from_source_str;
use rustc_session::parse::ParseSess;
@ -32,8 +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(false)
.expect("failed to load fallback fluent bundle");
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle };
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
@ -173,7 +173,7 @@ struct Buffer {
struct BufferEmitter {
buffer: Lrc<Lock<Buffer>>,
fallback_bundle: Lrc<rustc_errors::FluentBundle>,
fallback_bundle: LazyFallbackBundle,
}
impl Emitter for BufferEmitter {
@ -194,7 +194,7 @@ fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
None
}
fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> {
&self.fallback_bundle
fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
&**self.fallback_bundle
}
}

View File

@ -621,8 +621,10 @@ fn has_needless_main(code: String, edition: Edition) -> bool {
let filename = FileName::anon_source_code(&code);
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle");
let fallback_bundle = rustc_errors::fallback_fluent_bundle(
rustc_errors::DEFAULT_LOCALE_RESOURCES,
false
);
let emitter = EmitterWriter::new(
Box::new(io::sink()),
None,

View File

@ -165,7 +165,8 @@ 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(false).expect("failed to load fallback fluent bundle");
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
rustc_errors::ColorConfig::Auto,
None,

View File

@ -36,7 +36,7 @@ fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
None
}
fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> {
fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
panic!("silent emitter attempted to translate a diagnostic");
}
}
@ -93,7 +93,7 @@ fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
self.emitter.fluent_bundle()
}
fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> {
fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
self.emitter.fallback_fluent_bundle()
}
}
@ -114,8 +114,8 @@ fn default_handler(
let emitter = if hide_parse_errors {
silent_emitter()
} else {
let fallback_bundle = rustc_errors::fallback_fluent_bundle(false)
.expect("failed to load fallback fluent bundle");
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
Box::new(EmitterWriter::stderr(
color_cfg,
Some(source_map.clone()),
@ -350,7 +350,7 @@ fn emit_diagnostic(&mut self, _db: &Diagnostic) {
fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
None
}
fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> {
fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
panic!("test emitter attempted to translate a diagnostic");
}
}