Remove the translation compiler options

This commit is contained in:
Jonathan Brouwer
2026-02-10 18:23:57 +01:00
parent d00ba92259
commit 6782119567
15 changed files with 13 additions and 446 deletions
+2 -97
View File
@@ -5,9 +5,8 @@
use std::borrow::Cow;
use std::error::Error;
use std::path::Path;
use std::sync::{Arc, LazyLock};
use std::{fmt, fs, io};
use std::{fmt, io};
use fluent_bundle::FluentResource;
pub use fluent_bundle::types::FluentType;
@@ -17,7 +16,7 @@
use rustc_data_structures::sync::{DynSend, IntoDynSyncSend};
use rustc_macros::{Decodable, Encodable};
use rustc_span::Span;
use tracing::{instrument, trace};
use tracing::instrument;
pub use unic_langid::{LanguageIdentifier, langid};
mod diagnostic_impls;
@@ -98,100 +97,6 @@ fn from(mut errs: Vec<FluentError>) -> Self {
}
}
/// 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_candidates: &[&Path],
requested_locale: Option<LanguageIdentifier>,
additional_ftl_path: Option<&Path>,
with_directionality_markers: bool,
) -> Result<Option<Arc<FluentBundle>>, TranslationBundleError> {
if requested_locale.is_none() && additional_ftl_path.is_none() {
return Ok(None);
}
let fallback_locale = langid!("en-US");
let requested_fallback_locale = requested_locale.as_ref() == Some(&fallback_locale);
trace!(?requested_fallback_locale);
if requested_fallback_locale && 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(fallback_locale);
trace!(?locale);
let mut bundle = new_bundle(vec![locale]);
// Add convenience functions available to ftl authors.
register_functions(&mut bundle);
// Fluent diagnostics can insert directionality isolation markers around interpolated variables
// indicating that there may be a shift from right-to-left to left-to-right text (or
// vice-versa). These are disabled because they are sometimes visible in the error output, but
// may be worth investigating in future (for example: if type names are left-to-right and the
// surrounding diagnostic messages are right-to-left, then these might be helpful).
bundle.set_use_isolating(with_directionality_markers);
// If the user requests the default locale then don't try to load anything.
if let Some(requested_locale) = requested_locale {
let mut found_resources = false;
for sysroot in sysroot_candidates {
let mut sysroot = sysroot.to_path_buf();
sysroot.push("share");
sysroot.push("locale");
sysroot.push(requested_locale.to_string());
trace!(?sysroot);
if !sysroot.exists() {
trace!("skipping");
continue;
}
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)?;
found_resources = true;
}
}
if !found_resources {
return Err(TranslationBundleError::MissingLocale);
}
}
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 = Arc::new(bundle);
Ok(Some(bundle))
}
pub fn register_functions<R, M>(bundle: &mut fluent_bundle::bundle::FluentBundle<R, M>) {
bundle
.add_function("STREQ", |positional, _named| match positional {
+3 -8
View File
@@ -1,6 +1,5 @@
use std::borrow::Cow;
use std::error::Report;
use std::sync::Arc;
pub use rustc_error_messages::{FluentArgs, LazyFallbackBundle};
use rustc_error_messages::{langid, register_functions};
@@ -8,7 +7,7 @@
use crate::error::TranslateError;
use crate::fluent_bundle::FluentResource;
use crate::{DiagArg, DiagMessage, FluentBundle, Style, fluent_bundle};
use crate::{DiagArg, DiagMessage, Style, fluent_bundle};
/// Convert diagnostic arguments (a rustc internal type that exists to implement
/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
@@ -30,15 +29,11 @@ pub fn to_fluent_args<'iter>(iter: impl Iterator<Item = DiagArg<'iter>>) -> Flue
}
#[derive(Clone)]
pub struct Translator {
/// Localized diagnostics for the locale requested by the user. If no language was requested by
/// the user then this will be `None` and `fallback_fluent_bundle` should be used.
pub fluent_bundle: Option<Arc<FluentBundle>>,
}
pub struct Translator;
impl Translator {
pub fn new() -> Translator {
Translator { fluent_bundle: None }
Self
}
/// Convert `DiagMessage`s to a string, performing translation if necessary.
-11
View File
@@ -436,16 +436,6 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
let temps_dir = config.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
let bundle = match rustc_errors::fluent_bundle(
&config.opts.sysroot.all_paths().collect::<Vec<_>>(),
config.opts.unstable_opts.translate_lang.clone(),
config.opts.unstable_opts.translate_additional_ftl.as_deref(),
config.opts.unstable_opts.translate_directionality_markers,
) {
Ok(bundle) => bundle,
Err(e) => early_dcx.early_fatal(format!("failed to load fluent bundle: {e}")),
};
let mut sess = rustc_session::build_session(
config.opts,
CompilerIO {
@@ -454,7 +444,6 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
output_file: config.output_file,
temps_dir,
},
bundle,
config.lint_caps,
target,
util::rustc_version_str().unwrap_or("unknown"),
+1 -22
View File
@@ -7,7 +7,7 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::profiling::TimePassesFormat;
use rustc_data_structures::stable_hasher::StableHasher;
use rustc_errors::{ColorConfig, LanguageIdentifier, TerminalUrl};
use rustc_errors::{ColorConfig, TerminalUrl};
use rustc_feature::UnstableFeatures;
use rustc_hashes::Hash64;
use rustc_hir::attrs::CollapseMacroDebuginfo;
@@ -790,7 +790,6 @@ mod desc {
pub(crate) const parse_string: &str = "a string";
pub(crate) const parse_opt_string: &str = parse_string;
pub(crate) const parse_string_push: &str = parse_string;
pub(crate) const parse_opt_langid: &str = "a language identifier";
pub(crate) const parse_opt_pathbuf: &str = "a path";
pub(crate) const parse_list: &str = "a space-separated list of strings";
pub(crate) const parse_list_with_polarity: &str =
@@ -998,17 +997,6 @@ pub(crate) fn parse_opt_string(slot: &mut Option<String>, v: Option<&str>) -> bo
}
}
/// Parse an optional language identifier, e.g. `en-US` or `zh-CN`.
pub(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,
}
}
pub(crate) fn parse_opt_pathbuf(slot: &mut Option<PathBuf>, v: Option<&str>) -> bool {
match v {
Some(s) => {
@@ -2710,15 +2698,6 @@ pub(crate) fn parse_align(slot: &mut Option<Align>, v: Option<&str>) -> bool {
"for every macro invocation, print its name and arguments (default: no)"),
track_diagnostics: bool = (false, parse_bool, [UNTRACKED],
"tracks where in rustc a diagnostic was emitted"),
// Diagnostics are considered side-effects of a query (see `QuerySideEffect`) and are saved
// alongside query results and changes to translation options can affect diagnostics - so
// translation options should be tracked.
translate_additional_ftl: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
"additional fluent translation to preferentially use (for testing translation)"),
translate_directionality_markers: bool = (false, parse_bool, [TRACKED],
"emit directionality isolation markers in translated diagnostics"),
translate_lang: Option<LanguageIdentifier> = (None, parse_opt_langid, [TRACKED],
"language identifier for diagnostic output"),
translate_remapped_path_to_local_path: bool = (true, parse_bool, [TRACKED],
"translate remapped paths into local paths when possible (default: yes)"),
trap_unreachable: Option<bool> = (None, parse_opt_bool, [TRACKED],
+1 -2
View File
@@ -966,7 +966,6 @@ fn default_emitter(
pub fn build_session(
sopts: config::Options,
io: CompilerIO,
fluent_bundle: Option<Arc<rustc_errors::FluentBundle>>,
driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
target: Target,
cfg_version: &'static str,
@@ -984,7 +983,7 @@ pub fn build_session(
let cap_lints_allow = sopts.lint_cap.is_some_and(|cap| cap == lint::Allow);
let can_emit_warnings = !(warnings_allow || cap_lints_allow);
let translator = Translator { fluent_bundle };
let translator = Translator;
let source_map = rustc_span::source_map::get_source_map().unwrap();
let emitter = default_emitter(&sopts, Arc::clone(&source_map), translator);
-3
View File
@@ -1,3 +0,0 @@
# `foo` isn't provided by this diagnostic so it is expected that the fallback message is used.
parse_struct_literal_body_without_path = this is a {$foo} message
.suggestion = this is a test suggestion
-3
View File
@@ -1,3 +0,0 @@
# `parse_struct_literal_body_without_path` isn't provided by this resource at all, so the
# fallback should be used.
foo = bar
-197
View File
@@ -1,197 +0,0 @@
//! Smoke test for the rustc diagnostics translation infrastructure.
//!
//! # References
//!
//! - Current tracking issue: <https://github.com/rust-lang/rust/issues/132181>.
//! - Old tracking issue: <https://github.com/rust-lang/rust/issues/100717>
//! - Initial translation infra implementation: <https://github.com/rust-lang/rust/pull/95512>.
// This test uses symbolic links to stub out a fake sysroot to save testing time.
//@ needs-symlink
//@ needs-subprocess
// FIXME(151366) Currently `-Ztranslate-additional-ftl` is currently broken
//@ ignore-test
#![deny(warnings)]
use std::path::{Path, PathBuf};
use run_make_support::rustc::sysroot;
use run_make_support::{cwd, rfs, run_in_tmpdir, rustc};
fn main() {
builtin_fallback_bundle();
additional_primary_bundle();
missing_slug_prefers_fallback_bundle();
broken_primary_bundle_prefers_fallback_bundle();
locale_sysroot();
missing_sysroot();
file_sysroot();
}
/// Check that the test works normally, using the built-in fallback bundle.
fn builtin_fallback_bundle() {
rustc().input("test.rs").run_fail().assert_stderr_contains("struct literal body without path");
}
/// Check that a primary bundle can be loaded and will be preferentially used where possible.
fn additional_primary_bundle() {
rustc()
.input("test.rs")
.arg("-Ztranslate-additional-ftl=working.ftl")
.run_fail()
.assert_stderr_contains("this is a test message");
}
/// Check that a primary bundle without the desired message will use the fallback bundle.
fn missing_slug_prefers_fallback_bundle() {
rustc()
.input("test.rs")
.arg("-Ztranslate-additional-ftl=missing.ftl")
.run_fail()
.assert_stderr_contains("struct literal body without path");
}
/// Check that a primary bundle with a broken message (e.g. an interpolated variable is not
/// provided) will use the fallback bundle.
fn broken_primary_bundle_prefers_fallback_bundle() {
// FIXME(#135817): as of the rmake.rs port, the compiler actually ICEs on the additional
// `broken.ftl`, even though the original intention seems to be that it should gracefully
// failover to the fallback bundle. On `aarch64-apple-darwin`, somehow it *doesn't* ICE.
rustc()
.env("RUSTC_ICE", "0") // disable ICE dump file, not needed
.input("test.rs")
.arg("-Ztranslate-additional-ftl=broken.ftl")
.run_fail();
}
#[track_caller]
fn shallow_symlink_dir_entries(src_dir: &Path, dst_dir: &Path) {
for entry in rfs::read_dir(src_dir) {
let entry = entry.unwrap();
let src_entry_path = entry.path();
let src_filename = src_entry_path.file_name().unwrap();
let meta = rfs::symlink_metadata(&src_entry_path);
if meta.is_symlink() || meta.is_file() {
rfs::symlink_file(&src_entry_path, dst_dir.join(src_filename));
} else if meta.is_dir() {
rfs::symlink_dir(&src_entry_path, dst_dir.join(src_filename));
} else {
unreachable!()
}
}
}
#[track_caller]
fn shallow_symlink_dir_entries_materialize_single_dir(
src_dir: &Path,
dst_dir: &Path,
dir_filename: &str,
) {
shallow_symlink_dir_entries(src_dir, dst_dir);
let dst_symlink_meta = rfs::symlink_metadata(dst_dir.join(dir_filename));
if dst_symlink_meta.is_file() || dst_symlink_meta.is_dir() {
unreachable!();
}
#[cfg(windows)]
{
use std::os::windows::fs::FileTypeExt as _;
if dst_symlink_meta.file_type().is_symlink_file() {
rfs::remove_file(dst_dir.join(dir_filename));
} else if dst_symlink_meta.file_type().is_symlink_dir() {
rfs::remove_dir(dst_dir.join(dir_filename));
} else {
unreachable!();
}
}
#[cfg(not(windows))]
{
rfs::remove_file(dst_dir.join(dir_filename));
}
rfs::create_dir_all(dst_dir.join(dir_filename));
}
#[track_caller]
fn setup_fakeroot_parents() -> PathBuf {
let sysroot = sysroot();
let fakeroot = cwd().join("fakeroot");
rfs::create_dir_all(&fakeroot);
shallow_symlink_dir_entries_materialize_single_dir(&sysroot, &fakeroot, "lib");
shallow_symlink_dir_entries_materialize_single_dir(
&sysroot.join("lib"),
&fakeroot.join("lib"),
"rustlib",
);
shallow_symlink_dir_entries_materialize_single_dir(
&sysroot.join("lib").join("rustlib"),
&fakeroot.join("lib").join("rustlib"),
"src",
);
shallow_symlink_dir_entries(
&sysroot.join("lib").join("rustlib").join("src"),
&fakeroot.join("lib").join("rustlib").join("src"),
);
fakeroot
}
/// Check that a locale can be loaded from the sysroot given a language identifier by making a local
/// copy of the sysroot and adding the custom locale to it.
fn locale_sysroot() {
run_in_tmpdir(|| {
let fakeroot = setup_fakeroot_parents();
// When download-rustc is enabled, real sysroot will have a share directory. Delete the link
// to it.
let _ = std::fs::remove_file(fakeroot.join("share"));
let fake_locale_path = fakeroot.join("share").join("locale").join("zh-CN");
rfs::create_dir_all(&fake_locale_path);
rfs::symlink_file(
cwd().join("working.ftl"),
fake_locale_path.join("basic-translation.ftl"),
);
rustc()
.env("RUSTC_ICE", "0")
.input("test.rs")
.sysroot(&fakeroot)
.arg("-Ztranslate-lang=zh-CN")
.run_fail()
.assert_stderr_contains("this is a test message");
});
}
/// Check that the compiler errors out when the sysroot requested cannot be found. This test might
/// start failing if there actually exists a Klingon translation of rustc's error messages.
fn missing_sysroot() {
run_in_tmpdir(|| {
rustc()
.input("test.rs")
.arg("-Ztranslate-lang=tlh")
.run_fail()
.assert_stderr_contains("missing locale directory");
});
}
/// Check that the compiler errors out when the directory for the locale in the sysroot is actually
/// a file.
fn file_sysroot() {
run_in_tmpdir(|| {
let fakeroot = setup_fakeroot_parents();
rfs::create_dir_all(fakeroot.join("share").join("locale"));
rfs::write(fakeroot.join("share").join("locale").join("zh-CN"), b"not a dir");
rustc()
.input("test.rs")
.sysroot(&fakeroot)
.arg("-Ztranslate-lang=zh-CN")
.run_fail()
.assert_stderr_contains("is not a directory");
});
}
-18
View File
@@ -1,18 +0,0 @@
// 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: (),
};
}
-2
View File
@@ -1,2 +0,0 @@
parse_struct_literal_body_without_path = this is a test message
.suggestion = this is a test suggestion
-4
View File
@@ -446,10 +446,6 @@ Everything to do with `--diagnostic-width`.
Exercises `#[diagnostic::*]` namespaced attributes. See [RFC 3368 Diagnostic attribute namespace](https://github.com/rust-lang/rfcs/blob/master/text/3368-diagnostic-attribute-namespace.md).
## `tests/ui/diagnostics-infra`
This directory contains tests and infrastructure related to the diagnostics system, including support for translatable diagnostics
## `tests/ui/did_you_mean/`
Tests for miscellaneous suggestions.
@@ -1,24 +0,0 @@
//! Regression test for https://github.com/rust-lang/rust/issues/106755
//@ compile-flags:-Ztranslate-lang=en_US
#![feature(negative_impls)]
#![feature(marker_trait_attr)]
#[marker]
trait MyTrait {}
struct TestType<T>(::std::marker::PhantomData<T>);
unsafe impl<T: MyTrait + 'static> Send for TestType<T> {}
impl<T: MyTrait> !Send for TestType<T> {}
//~^ ERROR found both positive and negative implementation
//~| ERROR `!Send` impl requires `T: MyTrait` but the struct it is implemented for does not
unsafe impl<T: 'static> Send for TestType<T> {} //~ ERROR conflicting implementations
impl !Send for TestType<i32> {}
//~^ ERROR `!Send` impls cannot be specialized
fn main() {}
@@ -1,47 +0,0 @@
error[E0751]: found both positive and negative implementation of trait `Send` for type `TestType<_>`:
--> $DIR/primary-fluent-bundle-missing.rs:15:1
|
LL | unsafe impl<T: MyTrait + 'static> Send for TestType<T> {}
| ------------------------------------------------------ positive implementation here
LL |
LL | impl<T: MyTrait> !Send for TestType<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ negative implementation here
error[E0119]: conflicting implementations of trait `Send` for type `TestType<_>`
--> $DIR/primary-fluent-bundle-missing.rs:19:1
|
LL | unsafe impl<T: MyTrait + 'static> Send for TestType<T> {}
| ------------------------------------------------------ first implementation here
...
LL | unsafe impl<T: 'static> Send for TestType<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `TestType<_>`
error[E0367]: `!Send` impl requires `T: MyTrait` but the struct it is implemented for does not
--> $DIR/primary-fluent-bundle-missing.rs:15:9
|
LL | impl<T: MyTrait> !Send for TestType<T> {}
| ^^^^^^^
|
note: the implementor must specify the same requirement
--> $DIR/primary-fluent-bundle-missing.rs:11:1
|
LL | struct TestType<T>(::std::marker::PhantomData<T>);
| ^^^^^^^^^^^^^^^^^^
error[E0366]: `!Send` impls cannot be specialized
--> $DIR/primary-fluent-bundle-missing.rs:21:1
|
LL | impl !Send for TestType<i32> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `i32` is not a generic parameter
note: use the same sequence of generic lifetime, type and const parameters as the struct definition
--> $DIR/primary-fluent-bundle-missing.rs:11:1
|
LL | struct TestType<T>(::std::marker::PhantomData<T>);
| ^^^^^^^^^^^^^^^^^^
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0119, E0366, E0367, E0751.
For more information about an error, try `rustc --explain E0119`.
@@ -1,5 +1,3 @@
//@ compile-flags:-Ztranslate-lang=en_US
#![feature(negative_impls)]
#![feature(marker_trait_attr)]
@@ -1,5 +1,5 @@
error[E0751]: found both positive and negative implementation of trait `Send` for type `TestType<_>`:
--> $DIR/conflicting-send-impls-for-marker-trait-106755.rs:13:1
--> $DIR/conflicting-send-impls-for-marker-trait-106755.rs:11:1
|
LL | unsafe impl<T: MyTrait + 'static> Send for TestType<T> {}
| ------------------------------------------------------ positive implementation here
@@ -8,7 +8,7 @@ LL | impl<T: MyTrait> !Send for TestType<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ negative implementation here
error[E0119]: conflicting implementations of trait `Send` for type `TestType<_>`
--> $DIR/conflicting-send-impls-for-marker-trait-106755.rs:17:1
--> $DIR/conflicting-send-impls-for-marker-trait-106755.rs:15:1
|
LL | unsafe impl<T: MyTrait + 'static> Send for TestType<T> {}
| ------------------------------------------------------ first implementation here
@@ -17,26 +17,26 @@ LL | unsafe impl<T: 'static> Send for TestType<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `TestType<_>`
error[E0367]: `!Send` impl requires `T: MyTrait` but the struct it is implemented for does not
--> $DIR/conflicting-send-impls-for-marker-trait-106755.rs:13:9
--> $DIR/conflicting-send-impls-for-marker-trait-106755.rs:11:9
|
LL | impl<T: MyTrait> !Send for TestType<T> {}
| ^^^^^^^
|
note: the implementor must specify the same requirement
--> $DIR/conflicting-send-impls-for-marker-trait-106755.rs:9:1
--> $DIR/conflicting-send-impls-for-marker-trait-106755.rs:7:1
|
LL | struct TestType<T>(::std::marker::PhantomData<T>);
| ^^^^^^^^^^^^^^^^^^
error[E0366]: `!Send` impls cannot be specialized
--> $DIR/conflicting-send-impls-for-marker-trait-106755.rs:19:1
--> $DIR/conflicting-send-impls-for-marker-trait-106755.rs:17:1
|
LL | impl !Send for TestType<i32> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `i32` is not a generic parameter
note: use the same sequence of generic lifetime, type and const parameters as the struct definition
--> $DIR/conflicting-send-impls-for-marker-trait-106755.rs:9:1
--> $DIR/conflicting-send-impls-for-marker-trait-106755.rs:7:1
|
LL | struct TestType<T>(::std::marker::PhantomData<T>);
| ^^^^^^^^^^^^^^^^^^