Skip to content

Commit

Permalink
Rollup merge of #95512 - davidtwco:diagnostic-translation, r=oli-obk
Browse files Browse the repository at this point in the history
diagnostics: translation infrastructure

An implementation of the infrastructure required to have translatable diagnostic messages.

- Introduces a `DiagnosticMessage` type which can represent both the current non-translatable messages and identifiers for [Fluent](https://projectfluent.org/).
- Modifies current diagnostic API so that existing calls still work but `DiagnosticMessage`s can be provided too.
- Adds support for always loading a "fallback bundle" containing the English diagnostic messages, which are used when a `DiagnosticMessage::FluentIdentifier` is used in a diagnostic being emitted.
- Adds support for loading a "primary bundle" which contains the user's preferred language translation, and is used preferentially when it contains a diagnostic message being emitted. Primary bundles are loaded either from the path provided to `-Ztranslate-alternate-ftl` (for testing), or from the sysroot at `$sysroot/locale/$locale/*.ftl` given a locale with `-Ztranslate-lang` (which is parsed as a language identifier).
- Adds "diagnostic args" which enable normally-interpolated variables to be made available as variables for Fluent messages to use.
- Updates `#[derive(SessionDiagnostic)]` so that it can only be used for translatable diagnostics and update the handful of diagnostics which used the derive to be translatable.

For example, the following diagnostic...

```rust
#[derive(SessionDiagnostic)]
#[error = "E0195"]
pub struct LifetimesOrBoundsMismatchOnTrait {
    #[message = "lifetime parameters or bounds on {item_kind} `{ident}` do not match the trait declaration"]
    #[label = "lifetimes do not match {item_kind} in trait"]
    pub span: Span,
    #[label = "lifetimes in impl do not match this {item_kind} in trait"]
    pub generics_span: Option<Span>,
    pub item_kind: &'static str,
    pub ident: Ident,
}
```

...becomes...

```rust
#[derive(SessionDiagnostic)]
#[error(code = "E0195", slug = "typeck-lifetimes-or-bounds-mismatch-on-trait")]
pub struct LifetimesOrBoundsMismatchOnTrait {
    #[primary_span]
    #[label]
    pub span: Span,
    #[label = "generics-label"]
    pub generics_span: Option<Span>,
    pub item_kind: &'static str,
    pub ident: Ident,
}
```

```fluent
typeck-lifetimes-or-bounds-mismatch-on-trait =
    lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration
    .label = lifetimes do not match {$item_kind} in trait
    .generics-label = lifetimes in impl do not match this {$item_kind} in trait
```

r? `@estebank`
cc `@oli-obk` `@Manishearth`
  • Loading branch information
Dylan-DPC authored Apr 5, 2022
2 parents 73eab35 + ccd4820 commit d473024
Show file tree
Hide file tree
Showing 101 changed files with 2,911 additions and 1,071 deletions.
142 changes: 142 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1238,6 +1238,40 @@ dependencies = [
"miniz_oxide",
]

[[package]]
name = "fluent-bundle"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd"
dependencies = [
"fluent-langneg",
"fluent-syntax",
"intl-memoizer",
"intl_pluralrules",
"rustc-hash",
"self_cell",
"smallvec",
"unic-langid",
]

[[package]]
name = "fluent-langneg"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94"
dependencies = [
"unic-langid",
]

[[package]]
name = "fluent-syntax"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78"
dependencies = [
"thiserror",
]

[[package]]
name = "fnv"
version = "1.0.7"
Expand Down Expand Up @@ -1782,6 +1816,26 @@ dependencies = [
"cfg-if 1.0.0",
]

[[package]]
name = "intl-memoizer"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f"
dependencies = [
"type-map",
"unic-langid",
]

[[package]]
name = "intl_pluralrules"
version = "7.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b18f988384267d7066cc2be425e6faf352900652c046b6971d2e228d3b1c5ecf"
dependencies = [
"tinystr",
"unic-langid",
]

[[package]]
name = "itertools"
version = "0.10.1"
Expand Down Expand Up @@ -2812,6 +2866,12 @@ dependencies = [
"version_check",
]

[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"

[[package]]
name = "proc-macro2"
version = "1.0.30"
Expand Down Expand Up @@ -3645,13 +3705,29 @@ dependencies = [
name = "rustc_error_codes"
version = "0.0.0"

[[package]]
name = "rustc_error_messages"
version = "0.0.0"
dependencies = [
"fluent-bundle",
"fluent-syntax",
"intl-memoizer",
"rustc_data_structures",
"rustc_macros",
"rustc_serialize",
"rustc_span",
"tracing",
"unic-langid",
]

[[package]]
name = "rustc_errors"
version = "0.0.0"
dependencies = [
"annotate-snippets",
"atty",
"rustc_data_structures",
"rustc_error_messages",
"rustc_lint_defs",
"rustc_macros",
"rustc_serialize",
Expand Down Expand Up @@ -3708,6 +3784,7 @@ dependencies = [
"odht",
"rustc_ast",
"rustc_data_structures",
"rustc_error_messages",
"rustc_feature",
"rustc_index",
"rustc_macros",
Expand Down Expand Up @@ -3864,6 +3941,7 @@ version = "0.0.0"
dependencies = [
"rustc_ast",
"rustc_data_structures",
"rustc_error_messages",
"rustc_hir",
"rustc_macros",
"rustc_serialize",
Expand Down Expand Up @@ -4573,6 +4651,12 @@ dependencies = [
"libc",
]

[[package]]
name = "self_cell"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af"

[[package]]
name = "semver"
version = "1.0.3"
Expand Down Expand Up @@ -5104,6 +5188,12 @@ dependencies = [
"winapi",
]

[[package]]
name = "tinystr"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29738eedb4388d9ea620eeab9384884fc3f06f586a2eddb56bedc5885126c7c1"

[[package]]
name = "tinyvec"
version = "0.3.4"
Expand Down Expand Up @@ -5262,6 +5352,15 @@ dependencies = [
"tracing-subscriber",
]

[[package]]
name = "type-map"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46"
dependencies = [
"rustc-hash",
]

[[package]]
name = "typenum"
version = "1.12.0"
Expand Down Expand Up @@ -5316,6 +5415,49 @@ dependencies = [
"unic-ucd-version",
]

[[package]]
name = "unic-langid"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73328fcd730a030bdb19ddf23e192187a6b01cd98be6d3140622a89129459ce5"
dependencies = [
"unic-langid-impl",
"unic-langid-macros",
]

[[package]]
name = "unic-langid-impl"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a4a8eeaf0494862c1404c95ec2f4c33a2acff5076f64314b465e3ddae1b934d"
dependencies = [
"tinystr",
]

[[package]]
name = "unic-langid-macros"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18f980d6d87e8805f2836d64b4138cc95aa7986fa63b1f51f67d5fbff64dd6e5"
dependencies = [
"proc-macro-hack",
"tinystr",
"unic-langid-impl",
"unic-langid-macros-impl",
]

[[package]]
name = "unic-langid-macros-impl"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29396ffd97e27574c3e01368b1a64267d3064969e4848e2e130ff668be9daa9f"
dependencies = [
"proc-macro-hack",
"quote",
"syn",
"unic-langid-impl",
]

[[package]]
name = "unic-ucd-version"
version = "0.9.0"
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_borrowck/src/borrowck_errors.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use rustc_errors::{struct_span_err, DiagnosticBuilder, DiagnosticId, ErrorGuaranteed};
use rustc_errors::{struct_span_err, DiagnosticBuilder, DiagnosticId, ErrorGuaranteed, MultiSpan};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::{MultiSpan, Span};
use rustc_span::Span;

impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
crate fn cannot_move_when_borrowed(
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use either::Either;
use rustc_const_eval::util::CallKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
Expand All @@ -15,7 +15,7 @@ use rustc_middle::mir::{
use rustc_middle::ty::{self, subst::Subst, suggest_constraining_type_params, PredicateKind, Ty};
use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
use rustc_span::symbol::sym;
use rustc_span::{BytePos, MultiSpan, Span};
use rustc_span::{BytePos, Span};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::TraitEngineExt as _;

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/diagnostics/region_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl RegionName {
*span,
format!("lifetime `{}` represents this closure's body", self),
);
diag.note(&note);
diag.note(note);
}
RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::CannotMatchHirTy(
span,
Expand Down
11 changes: 7 additions & 4 deletions compiler/rustc_builtin_macros/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{self, Visitor};
use rustc_ast::{token, BlockCheckMode, UnsafeSource};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{pluralize, Applicability, PResult};
use rustc_errors::{pluralize, Applicability, MultiSpan, PResult};
use rustc_expand::base::{self, *};
use rustc_parse_format as parse;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{InnerSpan, MultiSpan, Span};
use rustc_span::{InnerSpan, Span};
use smallvec::SmallVec;

use std::borrow::Cow;
Expand Down Expand Up @@ -446,7 +446,9 @@ impl<'a, 'b> Context<'a, 'b> {
.iter()
.filter(|fmt| fmt.precision_span.is_some())
.count();
e.span_label(span, &format!(
e.span_label(
span,
&format!(
"this precision flag adds an extra required argument at position {}, \
which is why there {} expected",
pos,
Expand All @@ -455,7 +457,8 @@ impl<'a, 'b> Context<'a, 'b> {
} else {
format!("are {} arguments", count)
},
));
),
);
if let Some(arg) = self.args.get(pos) {
e.span_label(
arg.span,
Expand Down
20 changes: 15 additions & 5 deletions compiler/rustc_codegen_ssa/src/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1707,23 +1707,33 @@ impl SharedEmitter {

impl Emitter for SharedEmitter {
fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) {
let fluent_args = self.to_fluent_args(diag.args());
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
msg: diag.message(),
msg: self.translate_messages(&diag.message, &fluent_args).to_string(),
code: diag.code.clone(),
lvl: diag.level(),
})));
for child in &diag.children {
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
msg: child.message(),
msg: self.translate_messages(&child.message, &fluent_args).to_string(),
code: None,
lvl: child.level,
})));
}
drop(self.sender.send(SharedEmitterMessage::AbortIfErrors));
}

fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}

fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
None
}

fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> {
panic!("shared emitter attempted to translate a diagnostic");
}
}

impl SharedEmitterMain {
Expand Down Expand Up @@ -1754,9 +1764,9 @@ impl SharedEmitterMain {
let msg = msg.strip_prefix("error: ").unwrap_or(&msg);

let mut err = match level {
Level::Error { lint: false } => sess.struct_err(&msg).forget_guarantee(),
Level::Warning => sess.struct_warn(&msg),
Level::Note => sess.struct_note_without_error(&msg),
Level::Error { lint: false } => sess.struct_err(msg).forget_guarantee(),
Level::Warning => sess.struct_warn(msg),
Level::Note => sess.struct_note_without_error(msg),
_ => bug!("Invalid inline asm diagnostic level"),
};

Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1172,9 +1172,13 @@ 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(false).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,
None,
Expand Down Expand Up @@ -1209,7 +1213,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
}

for note in &xs {
handler.note_without_error(note);
handler.note_without_error(note.as_ref());
}

// If backtraces are enabled, also print the query stack
Expand Down
18 changes: 18 additions & 0 deletions compiler/rustc_error_messages/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "rustc_error_messages"
version = "0.0.0"
edition = "2021"

[lib]
doctest = false

[dependencies]
fluent-bundle = "0.15.2"
fluent-syntax = "0.11"
intl-memoizer = "0.5.1"
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_span = { path = "../rustc_span" }
rustc_macros = { path = "../rustc_macros" }
tracing = "0.1"
unic-langid = { version = "0.9.0", features = ["macros"] }
Loading

0 comments on commit d473024

Please sign in to comment.