Rollup merge of #95512 - davidtwco:diagnostic-translation, r=oli-obk

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`
This commit is contained in:
Dylan DPC 2022-04-05 09:33:22 +02:00 committed by GitHub
commit d4730244d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
101 changed files with 2916 additions and 1076 deletions

View file

@ -32,10 +32,10 @@ use rustc_ast::node_id::NodeMap;
use rustc_ast::visit::{self, Visitor};
use rustc_ast_lowering::ResolverAstLowering;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::pluralize;
use rustc_errors::{pluralize, MultiSpan};
use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_IMPORTS};
use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_span::{MultiSpan, Span, DUMMY_SP};
use rustc_span::{Span, DUMMY_SP};
struct UnusedImport<'a> {
use_tree: &'a ast::UseTree,

View file

@ -4,7 +4,7 @@ use rustc_ast::{self as ast, Path};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
};
use rustc_feature::BUILTIN_ATTRIBUTES;
use rustc_hir::def::Namespace::{self, *};
@ -18,7 +18,7 @@ use rustc_span::hygiene::MacroKind;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, MultiSpan, Span};
use rustc_span::{BytePos, Span};
use tracing::debug;
use crate::imports::{Import, ImportKind, ImportResolver};
@ -1341,7 +1341,7 @@ impl<'a> Resolver<'a> {
let def_span = self.session.source_map().guess_head_span(binding.span);
let mut note_span = MultiSpan::from_span(def_span);
if !first && binding.vis.is_public() {
note_span.push_span_label(def_span, "consider importing it directly".into());
note_span.push_span_label(def_span, "consider importing it directly");
}
err.span_note(note_span, &msg);
}

View file

@ -12,7 +12,7 @@ use crate::{NameBinding, NameBindingKind, PathResult, PrivacyError, ToNameBindin
use rustc_ast::NodeId;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::intern::Interned;
use rustc_errors::{pluralize, struct_span_err, Applicability};
use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
use rustc_hir::def::{self, PartialRes};
use rustc_hir::def_id::DefId;
use rustc_middle::metadata::ModChild;
@ -23,7 +23,7 @@ use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_span::hygiene::LocalExpnId;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::{MultiSpan, Span};
use rustc_span::Span;
use tracing::*;
@ -739,7 +739,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
if let Some((_, UnresolvedImportError { note, .. })) = errors.iter().last() {
for message in note {
diag.note(&message);
diag.note(message);
}
}

View file

@ -14,6 +14,7 @@ use rustc_ast_pretty::pprust::path_segment_to_string;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
MultiSpan,
};
use rustc_hir as hir;
use rustc_hir::def::Namespace::{self, *};
@ -25,7 +26,7 @@ use rustc_span::edition::Edition;
use rustc_span::hygiene::MacroKind;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP};
use rustc_span::{BytePos, Span, DUMMY_SP};
use std::iter;
use std::ops::Deref;
@ -1106,7 +1107,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
.collect();
if non_visible_spans.len() > 0 {
let mut m: rustc_span::MultiSpan = non_visible_spans.clone().into();
let mut m: MultiSpan = non_visible_spans.clone().into();
non_visible_spans
.into_iter()
.for_each(|s| m.push_span_label(s, "private field".to_string()));
@ -1139,7 +1140,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
}
err.span_suggestion(
span,
&"use this syntax instead",
"use this syntax instead",
path_str.to_string(),
Applicability::MaybeIncorrect,
);