Skip to content

Commit 262ff86

Browse files
author
mejrs
committed
Make translate_message return result and add tests
1 parent 0b5d6ae commit 262ff86

File tree

7 files changed

+224
-17
lines changed

7 files changed

+224
-17
lines changed

compiler/rustc_errors/src/emitter.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use rustc_error_messages::{FluentArgs, SpanLabel};
2828
use rustc_span::hygiene::{ExpnKind, MacroKind};
2929
use std::borrow::Cow;
3030
use std::cmp::{max, min, Reverse};
31+
use std::error::Report;
3132
use std::io::prelude::*;
3233
use std::io::{self, IsTerminal};
3334
use std::iter;
@@ -250,7 +251,7 @@ pub trait Emitter: Translate {
250251
let mut primary_span = diag.span.clone();
251252
let suggestions = diag.suggestions.as_deref().unwrap_or(&[]);
252253
if let Some((sugg, rest)) = suggestions.split_first() {
253-
let msg = self.translate_message(&sugg.msg, fluent_args);
254+
let msg = self.translate_message(&sugg.msg, fluent_args).map_err(Report::new).unwrap();
254255
if rest.is_empty() &&
255256
// ^ if there is only one suggestion
256257
// don't display multi-suggestions as labels
@@ -1325,7 +1326,7 @@ impl EmitterWriter {
13251326
// very *weird* formats
13261327
// see?
13271328
for (text, style) in msg.iter() {
1328-
let text = self.translate_message(text, args);
1329+
let text = self.translate_message(text, args).map_err(Report::new).unwrap();
13291330
let lines = text.split('\n').collect::<Vec<_>>();
13301331
if lines.len() > 1 {
13311332
for (i, line) in lines.iter().enumerate() {
@@ -1387,7 +1388,7 @@ impl EmitterWriter {
13871388
label_width += 2;
13881389
}
13891390
for (text, _) in msg.iter() {
1390-
let text = self.translate_message(text, args);
1391+
let text = self.translate_message(text, args).map_err(Report::new).unwrap();
13911392
// Account for newlines to align output to its label.
13921393
for (line, text) in normalize_whitespace(&text).lines().enumerate() {
13931394
buffer.append(
@@ -2301,7 +2302,9 @@ impl FileWithAnnotatedLines {
23012302
hi.col_display += 1;
23022303
}
23032304

2304-
let label = label.as_ref().map(|m| emitter.translate_message(m, args).to_string());
2305+
let label = label.as_ref().map(|m| {
2306+
emitter.translate_message(m, args).map_err(Report::new).unwrap().to_string()
2307+
});
23052308

23062309
if lo.line != hi.line {
23072310
let ml = MultilineAnnotation {

compiler/rustc_errors/src/error.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,14 @@ impl fmt::Display for TranslateError<'_> {
6565

6666
match self {
6767
Self::One { id, args, kind } => {
68-
writeln!(f, "\nfailed while formatting fluent string `{id}`: ")?;
68+
writeln!(f, "failed while formatting fluent string `{id}`: ")?;
6969
match kind {
7070
MessageMissing => writeln!(f, "message was missing")?,
7171
PrimaryBundleMissing => writeln!(f, "the primary bundle was missing")?,
72-
AttributeMissing { attr } => writeln!(f, "the attribute `{attr}` was missing")?,
72+
AttributeMissing { attr } => {
73+
writeln!(f, "the attribute `{attr}` was missing")?;
74+
writeln!(f, "help: add `.{attr} = <message>`")?;
75+
}
7376
ValueMissing => writeln!(f, "the value was missing")?,
7477
Fluent { errs } => {
7578
for err in errs {

compiler/rustc_errors/src/json.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use rustc_data_structures::sync::Lrc;
2424
use rustc_error_messages::FluentArgs;
2525
use rustc_span::hygiene::ExpnData;
2626
use rustc_span::Span;
27+
use std::error::Report;
2728
use std::io::{self, Write};
2829
use std::path::Path;
2930
use std::sync::{Arc, Mutex};
@@ -321,7 +322,8 @@ impl Diagnostic {
321322
fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
322323
let args = to_fluent_args(diag.args());
323324
let sugg = diag.suggestions.iter().flatten().map(|sugg| {
324-
let translated_message = je.translate_message(&sugg.msg, &args);
325+
let translated_message =
326+
je.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap();
325327
Diagnostic {
326328
message: translated_message.to_string(),
327329
code: None,
@@ -411,7 +413,10 @@ impl DiagnosticSpan {
411413
Self::from_span_etc(
412414
span.span,
413415
span.is_primary,
414-
span.label.as_ref().map(|m| je.translate_message(m, args)).map(|m| m.to_string()),
416+
span.label
417+
.as_ref()
418+
.map(|m| je.translate_message(m, args).unwrap())
419+
.map(|m| m.to_string()),
415420
suggestion,
416421
je,
417422
)

compiler/rustc_errors/src/lib.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use rustc_span::{Loc, Span};
4646

4747
use std::any::Any;
4848
use std::borrow::Cow;
49+
use std::error::Report;
4950
use std::fmt;
5051
use std::hash::Hash;
5152
use std::num::NonZeroUsize;
@@ -65,6 +66,8 @@ mod lock;
6566
pub mod registry;
6667
mod snippet;
6768
mod styled_buffer;
69+
#[cfg(test)]
70+
mod tests;
6871
pub mod translation;
6972

7073
pub use diagnostic_builder::IntoDiagnostic;
@@ -627,7 +630,14 @@ impl Handler {
627630
) -> SubdiagnosticMessage {
628631
let inner = self.inner.borrow();
629632
let args = crate::translation::to_fluent_args(args);
630-
SubdiagnosticMessage::Eager(inner.emitter.translate_message(&message, &args).to_string())
633+
SubdiagnosticMessage::Eager(
634+
inner
635+
.emitter
636+
.translate_message(&message, &args)
637+
.map_err(Report::new)
638+
.unwrap()
639+
.to_string(),
640+
)
631641
}
632642

633643
// This is here to not allow mutation of flags;

compiler/rustc_errors/src/tests.rs

+183
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
use crate::error::{TranslateError, TranslateErrorKind};
2+
use crate::fluent_bundle::*;
3+
use crate::translation::Translate;
4+
use crate::FluentBundle;
5+
use rustc_data_structures::sync::Lrc;
6+
use rustc_error_messages::fluent_bundle::resolver::errors::{ReferenceKind, ResolverError};
7+
use rustc_error_messages::langid;
8+
use rustc_error_messages::DiagnosticMessage;
9+
10+
struct Dummy {
11+
bundle: FluentBundle,
12+
}
13+
14+
impl Translate for Dummy {
15+
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
16+
None
17+
}
18+
19+
fn fallback_fluent_bundle(&self) -> &FluentBundle {
20+
&self.bundle
21+
}
22+
}
23+
24+
fn make_dummy(ftl: &'static str) -> Dummy {
25+
let resource = FluentResource::try_new(ftl.into()).expect("Failed to parse an FTL string.");
26+
27+
let langid_en = langid!("en-US");
28+
let mut bundle = FluentBundle::new(vec![langid_en]);
29+
30+
bundle.add_resource(resource).expect("Failed to add FTL resources to the bundle.");
31+
32+
Dummy { bundle }
33+
}
34+
35+
#[test]
36+
fn wellformed_fluent() {
37+
let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value
38+
.label = value moved into `{$name}` here
39+
.occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement the `Copy` trait
40+
.value_borrowed_label = value borrowed here after move
41+
.suggestion = borrow this binding in the pattern to avoid moving the value");
42+
43+
let mut args = FluentArgs::new();
44+
args.set("name", "Foo");
45+
args.set("ty", "std::string::String");
46+
{
47+
let message = DiagnosticMessage::FluentIdentifier(
48+
"mir_build_borrow_of_moved_value".into(),
49+
Some("suggestion".into()),
50+
);
51+
52+
assert_eq!(
53+
dummy.translate_message(&message, &args).unwrap(),
54+
"borrow this binding in the pattern to avoid moving the value"
55+
);
56+
}
57+
58+
{
59+
let message = DiagnosticMessage::FluentIdentifier(
60+
"mir_build_borrow_of_moved_value".into(),
61+
Some("value_borrowed_label".into()),
62+
);
63+
64+
assert_eq!(
65+
dummy.translate_message(&message, &args).unwrap(),
66+
"value borrowed here after move"
67+
);
68+
}
69+
70+
{
71+
let message = DiagnosticMessage::FluentIdentifier(
72+
"mir_build_borrow_of_moved_value".into(),
73+
Some("occurs_because_label".into()),
74+
);
75+
76+
assert_eq!(
77+
dummy.translate_message(&message, &args).unwrap(),
78+
"move occurs because `\u{2068}Foo\u{2069}` has type `\u{2068}std::string::String\u{2069}` which does not implement the `Copy` trait"
79+
);
80+
81+
{
82+
let message = DiagnosticMessage::FluentIdentifier(
83+
"mir_build_borrow_of_moved_value".into(),
84+
Some("label".into()),
85+
);
86+
87+
assert_eq!(
88+
dummy.translate_message(&message, &args).unwrap(),
89+
"value moved into `\u{2068}Foo\u{2069}` here"
90+
);
91+
}
92+
}
93+
}
94+
95+
#[test]
96+
fn misformed_fluent() {
97+
let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value
98+
.label = value moved into `{name}` here
99+
.occurs_because_label = move occurs because `{$oops}` has type `{$ty}` which does not implement the `Copy` trait
100+
.suggestion = borrow this binding in the pattern to avoid moving the value");
101+
102+
let mut args = FluentArgs::new();
103+
args.set("name", "Foo");
104+
args.set("ty", "std::string::String");
105+
{
106+
let message = DiagnosticMessage::FluentIdentifier(
107+
"mir_build_borrow_of_moved_value".into(),
108+
Some("value_borrowed_label".into()),
109+
);
110+
111+
let err = dummy.translate_message(&message, &args).unwrap_err();
112+
assert!(
113+
matches!(
114+
&err,
115+
TranslateError::Two {
116+
primary: box TranslateError::One {
117+
kind: TranslateErrorKind::PrimaryBundleMissing,
118+
..
119+
},
120+
fallback: box TranslateError::One {
121+
kind: TranslateErrorKind::AttributeMissing { attr: "value_borrowed_label" },
122+
..
123+
}
124+
}
125+
),
126+
"{err:#?}"
127+
);
128+
assert_eq!(
129+
format!("{err}"),
130+
"failed while formatting fluent string `mir_build_borrow_of_moved_value`: \nthe attribute `value_borrowed_label` was missing\nhelp: add `.value_borrowed_label = <message>`\n"
131+
);
132+
}
133+
134+
{
135+
let message = DiagnosticMessage::FluentIdentifier(
136+
"mir_build_borrow_of_moved_value".into(),
137+
Some("label".into()),
138+
);
139+
140+
let err = dummy.translate_message(&message, &args).unwrap_err();
141+
if let TranslateError::Two {
142+
primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. },
143+
fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. },
144+
} = &err
145+
&& let [FluentError::ResolverError(ResolverError::Reference(
146+
ReferenceKind::Message { id, .. }
147+
| ReferenceKind::Variable { id, .. },
148+
))] = &**errs
149+
&& id == "name"
150+
{} else {
151+
panic!("{err:#?}")
152+
};
153+
assert_eq!(
154+
format!("{err}"),
155+
"failed while formatting fluent string `mir_build_borrow_of_moved_value`: \nargument `name` exists but was not referenced correctly\nhelp: try using `{$name}` instead\n"
156+
);
157+
}
158+
159+
{
160+
let message = DiagnosticMessage::FluentIdentifier(
161+
"mir_build_borrow_of_moved_value".into(),
162+
Some("occurs_because_label".into()),
163+
);
164+
165+
let err = dummy.translate_message(&message, &args).unwrap_err();
166+
if let TranslateError::Two {
167+
primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. },
168+
fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. },
169+
} = &err
170+
&& let [FluentError::ResolverError(ResolverError::Reference(
171+
ReferenceKind::Message { id, .. }
172+
| ReferenceKind::Variable { id, .. },
173+
))] = &**errs
174+
&& id == "oops"
175+
{} else {
176+
panic!("{err:#?}")
177+
};
178+
assert_eq!(
179+
format!("{err}"),
180+
"failed while formatting fluent string `mir_build_borrow_of_moved_value`: \nthe fluent string has an argument `oops` that was not found.\nhelp: the arguments `name` and `ty` are available\n"
181+
);
182+
}
183+
}

compiler/rustc_errors/src/translation.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ pub trait Translate {
4545
args: &FluentArgs<'_>,
4646
) -> Cow<'_, str> {
4747
Cow::Owned(
48-
messages.iter().map(|(m, _)| self.translate_message(m, args)).collect::<String>(),
48+
messages
49+
.iter()
50+
.map(|(m, _)| self.translate_message(m, args).map_err(Report::new).unwrap())
51+
.collect::<String>(),
4952
)
5053
}
5154

@@ -54,11 +57,11 @@ pub trait Translate {
5457
&'a self,
5558
message: &'a DiagnosticMessage,
5659
args: &'a FluentArgs<'_>,
57-
) -> Cow<'_, str> {
60+
) -> Result<Cow<'_, str>, TranslateError<'_>> {
5861
trace!(?message, ?args);
5962
let (identifier, attr) = match message {
6063
DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => {
61-
return Cow::Borrowed(msg);
64+
return Ok(Cow::Borrowed(msg));
6265
}
6366
DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
6467
};
@@ -86,7 +89,7 @@ pub trait Translate {
8689
}
8790
};
8891

89-
let ret: Result<Cow<'_, str>, TranslateError<'_>> = try {
92+
try {
9093
match self.fluent_bundle().map(|b| translate_with_bundle(b)) {
9194
// The primary bundle was present and translation succeeded
9295
Some(Ok(t)) => t,
@@ -104,8 +107,6 @@ pub trait Translate {
104107
None => translate_with_bundle(self.fallback_fluent_bundle())
105108
.map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?,
106109
}
107-
};
108-
ret.map_err(Report::new)
109-
.expect("failed to find message in primary or fallback fluent bundles")
110+
}
110111
}
111112
}

src/librustdoc/passes/lint/check_code_block_syntax.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,9 @@ impl Emitter for BufferEmitter {
156156
let mut buffer = self.buffer.borrow_mut();
157157

158158
let fluent_args = to_fluent_args(diag.args());
159-
let translated_main_message = self.translate_message(&diag.message[0].0, &fluent_args);
159+
let translated_main_message = self
160+
.translate_message(&diag.message[0].0, &fluent_args)
161+
.unwrap_or_else(|e| panic!("{e}"));
160162

161163
buffer.messages.push(format!("error from rustc: {}", translated_main_message));
162164
if diag.is_error() {

0 commit comments

Comments
 (0)