Skip to content

triagebot: add translation-related mention groups #100379

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 17, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions compiler/rustc_codegen_ssa/src/back/write.rs
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ use rustc_data_structures::profiling::TimingGuard;
use rustc_data_structures::profiling::VerboseTimingGuard;
use rustc_data_structures::sync::Lrc;
use rustc_errors::emitter::Emitter;
use rustc_errors::{DiagnosticId, FatalError, Handler, Level};
use rustc_errors::{translation::Translate, DiagnosticId, FatalError, Handler, Level};
use rustc_fs_util::link_or_copy;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_incremental::{
@@ -1740,6 +1740,16 @@ impl SharedEmitter {
}
}

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

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

impl Emitter for SharedEmitter {
fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) {
let fluent_args = self.to_fluent_args(diag.args());
@@ -1761,14 +1771,6 @@ impl Emitter for SharedEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}

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

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

impl SharedEmitterMain {
19 changes: 11 additions & 8 deletions compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
use crate::emitter::FileWithAnnotatedLines;
use crate::snippet::Line;
use crate::translation::Translate;
use crate::{
CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle,
LazyFallbackBundle, Level, MultiSpan, Style, SubDiagnostic,
@@ -32,6 +33,16 @@ pub struct AnnotateSnippetEmitterWriter {
macro_backtrace: bool,
}

impl Translate for AnnotateSnippetEmitterWriter {
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
self.fluent_bundle.as_ref()
}

fn fallback_fluent_bundle(&self) -> &FluentBundle {
&**self.fallback_bundle
}
}

impl Emitter for AnnotateSnippetEmitterWriter {
/// The entry point for the diagnostics generation
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
@@ -63,14 +74,6 @@ impl Emitter for AnnotateSnippetEmitterWriter {
self.source_map.as_ref()
}

fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
self.fluent_bundle.as_ref()
}

fn fallback_fluent_bundle(&self) -> &FluentBundle {
&**self.fallback_bundle
}

fn should_show_explain(&self) -> bool {
!self.short_message
}
128 changes: 18 additions & 110 deletions compiler/rustc_errors/src/emitter.rs
Original file line number Diff line number Diff line change
@@ -14,10 +14,10 @@ use rustc_span::{FileLines, SourceFile, Span};

use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString};
use crate::styled_buffer::StyledBuffer;
use crate::translation::Translate;
use crate::{
CodeSuggestion, Diagnostic, DiagnosticArg, DiagnosticId, DiagnosticMessage, FluentBundle,
Handler, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight,
SuggestionStyle,
CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, FluentBundle, Handler,
LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, SuggestionStyle,
};

use rustc_lint_defs::pluralize;
@@ -200,7 +200,7 @@ impl Margin {
const ANONYMIZED_LINE_NUM: &str = "LL";

/// Emitter trait for emitting errors.
pub trait Emitter {
pub trait Emitter: Translate {
/// Emit a structured diagnostic.
fn emit_diagnostic(&mut self, diag: &Diagnostic);

@@ -231,102 +231,6 @@ pub trait Emitter {

fn source_map(&self) -> Option<&Lrc<SourceMap>>;

/// Return `FluentBundle` with 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.
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>>;

/// 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) -> &FluentBundle;

/// Convert diagnostic arguments (a rustc internal type that exists to implement
/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
///
/// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
/// passed around as a reference thereafter.
fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> {
FromIterator::from_iter(args.to_vec().drain(..))
}

/// Convert `DiagnosticMessage`s to a string, performing translation if necessary.
fn translate_messages(
&self,
messages: &[(DiagnosticMessage, Style)],
args: &FluentArgs<'_>,
) -> Cow<'_, str> {
Cow::Owned(
messages.iter().map(|(m, _)| self.translate_message(m, args)).collect::<String>(),
)
}

/// Convert a `DiagnosticMessage` to a string, performing translation if necessary.
fn translate_message<'a>(
&'a self,
message: &'a DiagnosticMessage,
args: &'a FluentArgs<'_>,
) -> Cow<'_, str> {
trace!(?message, ?args);
let (identifier, attr) = match message {
DiagnosticMessage::Str(msg) => return Cow::Borrowed(&msg),
DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
};

let translate_with_bundle = |bundle: &'a FluentBundle| -> Option<(Cow<'_, str>, Vec<_>)> {
let message = bundle.get_message(&identifier)?;
let value = match attr {
Some(attr) => message.get_attribute(attr)?.value(),
None => message.value()?,
};
debug!(?message, ?value);

let mut errs = vec![];
let translated = bundle.format_pattern(value, Some(&args), &mut errs);
debug!(?translated, ?errs);
Some((translated, errs))
};

self.fluent_bundle()
.and_then(|bundle| translate_with_bundle(bundle))
// If `translate_with_bundle` returns `None` with the primary bundle, this is likely
// just that the primary bundle doesn't contain the message being translated, so
// proceed to the fallback bundle.
//
// However, when errors are produced from translation, then that means the translation
// is broken (e.g. `{$foo}` exists in a translation but `foo` isn't provided).
//
// In debug builds, assert so that compiler devs can spot the broken translation and
// fix it..
.inspect(|(_, errs)| {
debug_assert!(
errs.is_empty(),
"identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}",
identifier,
attr,
args,
errs
);
})
// ..otherwise, for end users, an error about this wouldn't be useful or actionable, so
// just hide it and try with the fallback bundle.
.filter(|(_, errs)| errs.is_empty())
.or_else(|| translate_with_bundle(self.fallback_fluent_bundle()))
.map(|(translated, errs)| {
// Always bail out for errors with the fallback bundle.
assert!(
errs.is_empty(),
"identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}",
identifier,
attr,
args,
errs
);
translated
})
.expect("failed to find message in primary or fallback fluent bundles")
}

/// Formats the substitutions of the primary_span
///
/// There are a lot of conditions to this method, but in short:
@@ -616,18 +520,20 @@ pub trait Emitter {
}
}

impl Emitter for EmitterWriter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
self.sm.as_ref()
}

impl Translate for EmitterWriter {
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
self.fluent_bundle.as_ref()
}

fn fallback_fluent_bundle(&self) -> &FluentBundle {
&**self.fallback_bundle
}
}

impl Emitter for EmitterWriter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
self.sm.as_ref()
}

fn emit_diagnostic(&mut self, diag: &Diagnostic) {
let fluent_args = self.to_fluent_args(diag.args());
@@ -672,18 +578,20 @@ pub struct SilentEmitter {
pub fatal_note: Option<String>,
}

impl Emitter for SilentEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}

impl Translate for SilentEmitter {
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
None
}

fn fallback_fluent_bundle(&self) -> &FluentBundle {
panic!("silent emitter attempted to translate message")
}
}

impl Emitter for SilentEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}

fn emit_diagnostic(&mut self, d: &Diagnostic) {
if d.level == Level::Fatal {
19 changes: 11 additions & 8 deletions compiler/rustc_errors/src/json.rs
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ use rustc_span::source_map::{FilePathMapping, SourceMap};

use crate::emitter::{Emitter, HumanReadableErrorType};
use crate::registry::Registry;
use crate::translation::Translate;
use crate::DiagnosticId;
use crate::{
CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic,
@@ -122,6 +123,16 @@ impl JsonEmitter {
}
}

impl Translate for JsonEmitter {
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
self.fluent_bundle.as_ref()
}

fn fallback_fluent_bundle(&self) -> &FluentBundle {
&**self.fallback_bundle
}
}

impl Emitter for JsonEmitter {
fn emit_diagnostic(&mut self, diag: &crate::Diagnostic) {
let data = Diagnostic::from_errors_diagnostic(diag, self);
@@ -189,14 +200,6 @@ impl Emitter for JsonEmitter {
Some(&self.sm)
}

fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
self.fluent_bundle.as_ref()
}

fn fallback_fluent_bundle(&self) -> &FluentBundle {
&**self.fallback_bundle
}

fn should_show_explain(&self) -> bool {
!matches!(self.json_rendered, HumanReadableErrorType::Short(_))
}
1 change: 1 addition & 0 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
@@ -58,6 +58,7 @@ mod lock;
pub mod registry;
mod snippet;
mod styled_buffer;
pub mod translation;

pub use snippet::Style;

103 changes: 103 additions & 0 deletions compiler/rustc_errors/src/translation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use crate::snippet::Style;
use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle};
use rustc_data_structures::sync::Lrc;
use rustc_error_messages::FluentArgs;
use std::borrow::Cow;

pub trait Translate {
/// Return `FluentBundle` with 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.
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>>;

/// 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) -> &FluentBundle;

/// Convert diagnostic arguments (a rustc internal type that exists to implement
/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
///
/// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
/// passed around as a reference thereafter.
fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> {
FromIterator::from_iter(args.to_vec().drain(..))
}

/// Convert `DiagnosticMessage`s to a string, performing translation if necessary.
fn translate_messages(
&self,
messages: &[(DiagnosticMessage, Style)],
args: &FluentArgs<'_>,
) -> Cow<'_, str> {
Cow::Owned(
messages.iter().map(|(m, _)| self.translate_message(m, args)).collect::<String>(),
)
}

/// Convert a `DiagnosticMessage` to a string, performing translation if necessary.
fn translate_message<'a>(
&'a self,
message: &'a DiagnosticMessage,
args: &'a FluentArgs<'_>,
) -> Cow<'_, str> {
trace!(?message, ?args);
let (identifier, attr) = match message {
DiagnosticMessage::Str(msg) => return Cow::Borrowed(&msg),
DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
};

let translate_with_bundle = |bundle: &'a FluentBundle| -> Option<(Cow<'_, str>, Vec<_>)> {
let message = bundle.get_message(&identifier)?;
let value = match attr {
Some(attr) => message.get_attribute(attr)?.value(),
None => message.value()?,
};
debug!(?message, ?value);

let mut errs = vec![];
let translated = bundle.format_pattern(value, Some(&args), &mut errs);
debug!(?translated, ?errs);
Some((translated, errs))
};

self.fluent_bundle()
.and_then(|bundle| translate_with_bundle(bundle))
// If `translate_with_bundle` returns `None` with the primary bundle, this is likely
// just that the primary bundle doesn't contain the message being translated, so
// proceed to the fallback bundle.
//
// However, when errors are produced from translation, then that means the translation
// is broken (e.g. `{$foo}` exists in a translation but `foo` isn't provided).
//
// In debug builds, assert so that compiler devs can spot the broken translation and
// fix it..
.inspect(|(_, errs)| {
debug_assert!(
errs.is_empty(),
"identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}",
identifier,
attr,
args,
errs
);
})
// ..otherwise, for end users, an error about this wouldn't be useful or actionable, so
// just hide it and try with the fallback bundle.
.filter(|(_, errs)| errs.is_empty())
.or_else(|| translate_with_bundle(self.fallback_fluent_bundle()))
.map(|(translated, errs)| {
// Always bail out for errors with the fallback bundle.
assert!(
errs.is_empty(),
"identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}",
identifier,
attr,
args,
errs
);
translated
})
.expect("failed to find message in primary or fallback fluent bundles")
}
}
21 changes: 12 additions & 9 deletions src/librustdoc/passes/check_code_block_syntax.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Validates syntax inside Rust code blocks (\`\`\`rust).
use rustc_data_structures::sync::{Lock, Lrc};
use rustc_errors::{
emitter::Emitter, Applicability, Diagnostic, Handler, LazyFallbackBundle, LintDiagnosticBuilder,
emitter::Emitter, translation::Translate, Applicability, Diagnostic, Handler,
LazyFallbackBundle, LintDiagnosticBuilder,
};
use rustc_parse::parse_stream_from_source_str;
use rustc_session::parse::ParseSess;
@@ -181,6 +182,16 @@ struct BufferEmitter {
fallback_bundle: LazyFallbackBundle,
}

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

fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
&**self.fallback_bundle
}
}

impl Emitter for BufferEmitter {
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
let mut buffer = self.buffer.borrow_mut();
@@ -194,12 +205,4 @@ impl Emitter for BufferEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}

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

fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
&**self.fallback_bundle
}
}
52 changes: 33 additions & 19 deletions src/tools/rustfmt/src/parse/session.rs
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ use std::sync::atomic::{AtomicBool, Ordering};

use rustc_data_structures::sync::{Lrc, Send};
use rustc_errors::emitter::{Emitter, EmitterWriter};
use rustc_errors::translation::Translate;
use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel};
use rustc_session::parse::ParseSess as RawParseSess;
use rustc_span::{
@@ -28,19 +29,24 @@ pub(crate) struct ParseSess {
/// Emitter which discards every error.
struct SilentEmitter;

impl Emitter for SilentEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}
fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
impl Translate for SilentEmitter {
fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
None
}

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

impl Emitter for SilentEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}

fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
}

fn silent_emitter() -> Box<dyn Emitter + Send> {
Box::new(SilentEmitter {})
}
@@ -62,10 +68,21 @@ impl SilentOnIgnoredFilesEmitter {
}
}

impl Translate for SilentOnIgnoredFilesEmitter {
fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
self.emitter.fluent_bundle()
}

fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
self.emitter.fallback_fluent_bundle()
}
}

impl Emitter for SilentOnIgnoredFilesEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}

fn emit_diagnostic(&mut self, db: &Diagnostic) {
if db.level() == DiagnosticLevel::Fatal {
return self.handle_non_ignoreable_error(db);
@@ -88,14 +105,6 @@ impl Emitter for SilentOnIgnoredFilesEmitter {
}
self.handle_non_ignoreable_error(db);
}

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

fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
self.emitter.fallback_fluent_bundle()
}
}

fn default_handler(
@@ -340,19 +349,24 @@ mod tests {
num_emitted_errors: Lrc<AtomicU32>,
}

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

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

impl Emitter for TestEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}

fn emit_diagnostic(&mut self, _db: &Diagnostic) {
self.num_emitted_errors.fetch_add(1, Ordering::Release);
}
fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
None
}
fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle {
panic!("test emitter attempted to translate a diagnostic");
}
}

fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
19 changes: 19 additions & 0 deletions triagebot.toml
Original file line number Diff line number Diff line change
@@ -178,6 +178,13 @@ trigger_files = [
"src/tools/bump-stage0",
]

[autolabel."A-translation"]
trigger_files = [
"compiler/rustc_error_messages",
"compiler/rustc_errors/src/translation.rs",
"compiler/rustc_macros/src/diagnostics"
]

[notify-zulip."I-prioritize"]
zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts
topic = "#{number} {title}"
@@ -342,3 +349,15 @@ cc = ["@rust-lang/rustfmt"]
[mentions."compiler/rustc_middle/src/mir/syntax.rs"]
message = "This PR changes MIR"
cc = ["@oli-obk", "@RalfJung", "@JakobDegen", "@davidtwco", "@celinval", "@vakaras"]

[mentions."compiler/rustc_error_messages"]
message = "`rustc_error_messages` was changed"
cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@estebank"]

[mentions."compiler/rustc_errors/src/translation.rs"]
message = "`rustc_errors::translation` was changed"
cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@estebank"]

[mentions."compiler/rustc_macros/src/diagnostics"]
message = "`rustc_macros::diagnostics` was changed"
cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@estebank"]