Skip to content

Commit cf078f2

Browse files
Check style of messages in rustc_macros
1 parent 572df14 commit cf078f2

1 file changed

Lines changed: 53 additions & 15 deletions

File tree

compiler/rustc_macros/src/diagnostics/message.rs

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@ impl Message {
2323
quote! { crate::fluent_generated::#slug }
2424
}
2525
Message::Inline(message_span, message) => {
26-
if let Some(variant) = variant {
27-
verify_fluent_message(*message_span, &message, variant);
28-
}
26+
verify_fluent_message(*message_span, &message, variant);
2927
quote! { rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed(#message)) }
3028
}
3129
}
@@ -86,30 +84,37 @@ impl Message {
8684
}
8785
}
8886

89-
fn verify_fluent_message(msg_span: Span, message: &str, variant: &VariantInfo<'_>) {
87+
fn verify_fluent_message(msg_span: Span, message_str: &str, variant: Option<&VariantInfo<'_>>) {
9088
// Parse the fluent message
9189
const GENERATED_MSG_ID: &str = "generated_msg";
92-
let resource = FluentResource::try_new(format!("{GENERATED_MSG_ID} = {message}\n")).unwrap();
90+
let resource =
91+
FluentResource::try_new(format!("{GENERATED_MSG_ID} = {message_str}\n")).unwrap();
9392
assert_eq!(resource.entries().count(), 1);
9493
let Some(fluent_syntax::ast::Entry::Message(message)) = resource.get_entry(0) else {
9594
panic!("Did not parse into a message")
9695
};
9796

9897
// Check if all variables are used
99-
let fields: Vec<String> = variant
100-
.bindings()
101-
.iter()
102-
.flat_map(|b| b.ast().ident.as_ref())
103-
.map(|id| id.to_string())
104-
.collect();
105-
for variable in variable_references(&message) {
106-
if !fields.iter().any(|f| f == variable) {
107-
span_err(msg_span.unwrap(), format!("Variable `{variable}` not found in diagnostic "))
98+
if let Some(variant) = variant {
99+
let fields: Vec<String> = variant
100+
.bindings()
101+
.iter()
102+
.flat_map(|b| b.ast().ident.as_ref())
103+
.map(|id| id.to_string())
104+
.collect();
105+
for variable in variable_references(&message) {
106+
if !fields.iter().any(|f| f == variable) {
107+
span_err(
108+
msg_span.unwrap(),
109+
format!("Variable `{variable}` not found in diagnostic "),
110+
)
108111
.help(format!("Available fields: {:?}", fields.join(", ")))
109112
.emit();
113+
}
110114
}
111-
// assert!(, );
112115
}
116+
117+
verify_message_style(msg_span, message_str);
113118
}
114119

115120
fn variable_references<'a>(msg: &fluent_syntax::ast::Message<&'a str>) -> Vec<&'a str> {
@@ -136,3 +141,36 @@ fn variable_references<'a>(msg: &fluent_syntax::ast::Message<&'a str>) -> Vec<&'
136141
}
137142
refs
138143
}
144+
145+
const ALLOWED_CAPITALIZED_WORDS: &[&str] = &[
146+
// tidy-alphabetical-start
147+
"ABI",
148+
"C-variadic",
149+
"CGU-reuse",
150+
"Cargo",
151+
"Ferris",
152+
"MIR",
153+
"OK",
154+
"VS",
155+
// tidy-alphabetical-end
156+
];
157+
158+
/// See: https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-output-style-guide
159+
fn verify_message_style(msg_span: Span, message: &str) {
160+
// Verify that message starts with lowercase char
161+
let Some(first_word) = message.split_whitespace().next() else {
162+
span_err(msg_span.unwrap(), "message must not be empty").emit();
163+
return;
164+
};
165+
let first_char = first_word.chars().next().expect("Word is not empty");
166+
if first_char.is_uppercase() && !ALLOWED_CAPITALIZED_WORDS.contains(&first_word) {
167+
span_err(msg_span.unwrap(), "message `{value}` starts with an uppercase letter. Fix it or add it to `ALLOWED_CAPITALIZED_WORDS`").emit();
168+
return;
169+
}
170+
171+
// Verify that message does not end in `.`
172+
if message.ends_with(".") && !message.ends_with("...") {
173+
span_err(msg_span.unwrap(), "message `{value}` ends with a period").emit();
174+
return;
175+
}
176+
}

0 commit comments

Comments
 (0)