@@ -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
115120fn 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