diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 9417874ffb4..8aa18b8e37c 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1708,13 +1708,13 @@ impl SharedEmitter { impl Emitter for SharedEmitter { fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) { drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { - msg: diag.message(), + msg: diag.message().to_string(), code: diag.code.clone(), lvl: diag.level(), }))); for child in &diag.children { drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { - msg: child.message(), + msg: child.message().to_string(), code: None, lvl: child.level, }))); diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 5f59eba23f8..76c8396cf91 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -41,7 +41,7 @@ impl Emitter for AnnotateSnippetEmitterWriter { self.emit_messages_default( &diag.level, - diag.message(), + diag.message().to_string(), &diag.code, &primary_span, &children, diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 32c52a6a8a6..d31593a132b 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -18,6 +18,34 @@ use std::hash::{Hash, Hasher}; #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] pub struct SuggestionsDisabled; +/// Abstraction over a message in a diagnostic to support both translatable and non-translatable +/// diagnostic messages. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] +pub enum DiagnosticMessage { + /// Non-translatable diagnostic message. + Str(String), + /// Identifier for a Fluent message corresponding to the diagnostic message. + FluentIdentifier(String), +} + +impl DiagnosticMessage { + /// Convert `DiagnosticMessage` to a `&str`. + pub fn as_str(&self) -> &str { + match self { + DiagnosticMessage::Str(msg) => msg, + DiagnosticMessage::FluentIdentifier(..) => unimplemented!(), + } + } + + /// Convert `DiagnosticMessage` to an owned `String`. + pub fn to_string(self) -> String { + match self { + DiagnosticMessage::Str(msg) => msg, + DiagnosticMessage::FluentIdentifier(..) => unimplemented!(), + } + } +} + #[must_use] #[derive(Clone, Debug, Encodable, Decodable)] pub struct Diagnostic { @@ -25,7 +53,7 @@ pub struct Diagnostic { // outside of what methods in this crate themselves allow. crate level: Level, - pub message: Vec<(String, Style)>, + pub message: Vec<(DiagnosticMessage, Style)>, pub code: Option<DiagnosticId>, pub span: MultiSpan, pub children: Vec<SubDiagnostic>, @@ -52,7 +80,7 @@ pub enum DiagnosticId { #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] pub struct SubDiagnostic { pub level: Level, - pub message: Vec<(String, Style)>, + pub message: Vec<(DiagnosticMessage, Style)>, pub span: MultiSpan, pub render_span: Option<MultiSpan>, } @@ -112,7 +140,7 @@ impl Diagnostic { pub fn new_with_code(level: Level, code: Option<DiagnosticId>, message: &str) -> Self { Diagnostic { level, - message: vec![(message.to_owned(), Style::NoStyle)], + message: vec![(DiagnosticMessage::Str(message.to_owned()), Style::NoStyle)], code, span: MultiSpan::new(), children: vec![], @@ -465,7 +493,7 @@ impl Diagnostic { .map(|(span, snippet)| SubstitutionPart { snippet, span }) .collect(), }], - msg: msg.to_owned(), + msg: DiagnosticMessage::Str(msg.to_owned()), style, applicability, tool_metadata: Default::default(), @@ -493,7 +521,7 @@ impl Diagnostic { .map(|(span, snippet)| SubstitutionPart { snippet, span }) .collect(), }], - msg: msg.to_owned(), + msg: DiagnosticMessage::Str(msg.to_owned()), style: SuggestionStyle::CompletelyHidden, applicability, tool_metadata: Default::default(), @@ -548,7 +576,7 @@ impl Diagnostic { substitutions: vec![Substitution { parts: vec![SubstitutionPart { snippet: suggestion, span: sp }], }], - msg: msg.to_owned(), + msg: DiagnosticMessage::Str(msg.to_owned()), style, applicability, tool_metadata: Default::default(), @@ -591,7 +619,7 @@ impl Diagnostic { .collect(); self.push_suggestion(CodeSuggestion { substitutions, - msg: msg.to_owned(), + msg: DiagnosticMessage::Str(msg.to_owned()), style: SuggestionStyle::ShowCode, applicability, tool_metadata: Default::default(), @@ -616,7 +644,7 @@ impl Diagnostic { .collect(), }) .collect(), - msg: msg.to_owned(), + msg: DiagnosticMessage::Str(msg.to_owned()), style: SuggestionStyle::ShowCode, applicability, tool_metadata: Default::default(), @@ -698,7 +726,7 @@ impl Diagnostic { ) { self.push_suggestion(CodeSuggestion { substitutions: vec![], - msg: msg.to_owned(), + msg: DiagnosticMessage::Str(msg.to_owned()), style: SuggestionStyle::CompletelyHidden, applicability, tool_metadata: ToolMetadata::new(tool_metadata), @@ -733,15 +761,15 @@ impl Diagnostic { } pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self { - self.message[0] = (msg.into(), Style::NoStyle); + self.message[0] = (DiagnosticMessage::Str(msg.into()), Style::NoStyle); self } - pub fn message(&self) -> String { - self.message.iter().map(|i| i.0.as_str()).collect::<String>() + pub fn message(&self) -> DiagnosticMessage { + DiagnosticMessage::Str(self.message.iter().map(|i| i.0.as_str()).collect::<String>()) } - pub fn styled_message(&self) -> &Vec<(String, Style)> { + pub fn styled_message(&self) -> &Vec<(DiagnosticMessage, Style)> { &self.message } @@ -758,7 +786,7 @@ impl Diagnostic { ) { let sub = SubDiagnostic { level, - message: vec![(message.to_owned(), Style::NoStyle)], + message: vec![(DiagnosticMessage::Str(message.to_owned()), Style::NoStyle)], span, render_span, }; @@ -770,10 +798,11 @@ impl Diagnostic { fn sub_with_highlights( &mut self, level: Level, - message: Vec<(String, Style)>, + mut message: Vec<(String, Style)>, span: MultiSpan, render_span: Option<MultiSpan>, ) { + let message = message.drain(..).map(|m| (DiagnosticMessage::Str(m.0), m.1)).collect(); let sub = SubDiagnostic { level, message, span, render_span }; self.children.push(sub); } @@ -783,7 +812,7 @@ impl Diagnostic { &self, ) -> ( &Level, - &Vec<(String, Style)>, + &Vec<(DiagnosticMessage, Style)>, &Option<DiagnosticId>, &MultiSpan, &Result<Vec<CodeSuggestion>, SuggestionsDisabled>, @@ -816,11 +845,11 @@ impl PartialEq for Diagnostic { } impl SubDiagnostic { - pub fn message(&self) -> String { - self.message.iter().map(|i| i.0.as_str()).collect::<String>() + pub fn message(&self) -> DiagnosticMessage { + DiagnosticMessage::Str(self.message.iter().map(|i| i.0.as_str()).collect::<String>()) } - pub fn styled_message(&self) -> &Vec<(String, Style)> { + pub fn styled_message(&self) -> &Vec<(DiagnosticMessage, Style)> { &self.message } } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 93b7201023a..1f26b002f6a 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -15,8 +15,8 @@ use rustc_span::{MultiSpan, SourceFile, Span}; use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString}; use crate::styled_buffer::StyledBuffer; use crate::{ - CodeSuggestion, Diagnostic, DiagnosticId, Handler, Level, SubDiagnostic, SubstitutionHighlight, - SuggestionStyle, + CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Handler, Level, SubDiagnostic, + SubstitutionHighlight, SuggestionStyle, }; use rustc_lint_defs::pluralize; @@ -236,7 +236,7 @@ pub trait Emitter { // don't display multipart suggestions as labels sugg.substitutions[0].parts.len() == 1 && // don't display long messages as labels - sugg.msg.split_whitespace().count() < 10 && + sugg.msg.as_str().split_whitespace().count() < 10 && // don't display multiline suggestions as labels !sugg.substitutions[0].parts[0].snippet.contains('\n') && ![ @@ -252,12 +252,12 @@ pub trait Emitter { let msg = if substitution.is_empty() || sugg.style.hide_inline() { // This substitution is only removal OR we explicitly don't want to show the // code inline (`hide_inline`). Therefore, we don't show the substitution. - format!("help: {}", sugg.msg) + format!("help: {}", sugg.msg.as_str()) } else { // Show the default suggestion text with the substitution format!( "help: {}{}: `{}`", - sugg.msg, + sugg.msg.as_str(), if self .source_map() .map(|sm| is_case_difference( @@ -333,7 +333,7 @@ pub trait Emitter { children.push(SubDiagnostic { level: Level::Note, - message: vec![(msg, Style::NoStyle)], + message: vec![(DiagnosticMessage::Str(msg), Style::NoStyle)], span: MultiSpan::new(), render_span: None, }); @@ -1176,7 +1176,7 @@ impl EmitterWriter { fn msg_to_buffer( &self, buffer: &mut StyledBuffer, - msg: &[(String, Style)], + msg: &[(DiagnosticMessage, Style)], padding: usize, label: &str, override_style: Option<Style>, @@ -1229,6 +1229,7 @@ impl EmitterWriter { // very *weird* formats // see? for &(ref text, ref style) in msg.iter() { + let text = text.as_str(); let lines = text.split('\n').collect::<Vec<_>>(); if lines.len() > 1 { for (i, line) in lines.iter().enumerate() { @@ -1247,7 +1248,7 @@ impl EmitterWriter { fn emit_message_default( &mut self, msp: &MultiSpan, - msg: &[(String, Style)], + msg: &[(DiagnosticMessage, Style)], code: &Option<DiagnosticId>, level: &Level, max_line_num_len: usize, @@ -1287,6 +1288,7 @@ impl EmitterWriter { label_width += 2; } for &(ref text, _) in msg.iter() { + let text = text.as_str(); // Account for newlines to align output to its label. for (line, text) in normalize_whitespace(text).lines().enumerate() { buffer.append( @@ -1852,7 +1854,7 @@ impl EmitterWriter { fn emit_messages_default( &mut self, level: &Level, - message: &[(String, Style)], + message: &[(DiagnosticMessage, Style)], code: &Option<DiagnosticId>, span: &MultiSpan, children: &[SubDiagnostic], diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index dc28d1bb452..90f6df2d571 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -346,7 +346,7 @@ struct UnusedExterns<'a, 'b, 'c> { impl Diagnostic { fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic { let sugg = diag.suggestions.iter().flatten().map(|sugg| Diagnostic { - message: sugg.msg.clone(), + message: sugg.msg.clone().to_string(), code: None, level: "help", spans: DiagnosticSpan::from_suggestion(sugg, je), @@ -385,7 +385,7 @@ impl Diagnostic { let output = String::from_utf8(output).unwrap(); Diagnostic { - message: diag.message(), + message: diag.message().to_string(), code: DiagnosticCode::map_opt_string(diag.code.clone(), je), level: diag.level.to_str(), spans: DiagnosticSpan::from_multispan(&diag.span, je), @@ -402,7 +402,7 @@ impl Diagnostic { fn from_sub_diagnostic(diag: &SubDiagnostic, je: &JsonEmitter) -> Diagnostic { Diagnostic { - message: diag.message(), + message: diag.message().to_string(), code: None, level: diag.level.to_str(), spans: diag diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index ec00910ec8b..0f55ef7a9ec 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -145,7 +145,7 @@ pub struct CodeSuggestion { /// ] /// ``` pub substitutions: Vec<Substitution>, - pub msg: String, + pub msg: DiagnosticMessage, /// Visual representation of this suggestion. pub style: SuggestionStyle, /// Whether or not the suggestion is approximate @@ -400,7 +400,9 @@ impl fmt::Display for ExplicitBug { impl error::Error for ExplicitBug {} -pub use diagnostic::{Diagnostic, DiagnosticId, DiagnosticStyledString, SubDiagnostic}; +pub use diagnostic::{ + Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, SubDiagnostic, +}; pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee}; use std::backtrace::Backtrace; diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 10b2b9f07e2..2ed11204ef6 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -68,17 +68,18 @@ fn emit_frag_parse_err( arm_span: Span, kind: AstFragmentKind, ) { - if parser.token == token::Eof && e.message().ends_with(", found `<eof>`") { + // FIXME(davidtwco): avoid depending on the error message text + if parser.token == token::Eof && e.message().as_str().ends_with(", found `<eof>`") { if !e.span.is_dummy() { // early end of macro arm (#52866) e.replace_span_with(parser.sess.source_map().next_point(parser.token.span)); } let msg = &e.message[0]; e.message[0] = ( - format!( + rustc_errors::DiagnosticMessage::Str(format!( "macro expansion ends with an incomplete expression: {}", - msg.0.replace(", found `<eof>`", ""), - ), + msg.0.as_str().replace(", found `<eof>`", ""), + )), msg.1, ); } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index e55bdb0e553..fd4b00de392 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1010,7 +1010,8 @@ impl<'a> Parser<'a> { let current_qual_sp = self.prev_token.span; let current_qual_sp = current_qual_sp.to(sp_start); if let Ok(current_qual) = self.span_to_snippet(current_qual_sp) { - if err.message() == "expected `{`, found keyword `unsafe`" { + // FIXME(davidtwco): avoid depending on the error message text + if err.message().as_str() == "expected `{`, found keyword `unsafe`" { let invalid_qual_sp = self.token.uninterpolated_span(); let invalid_qual = self.span_to_snippet(invalid_qual_sp).unwrap(); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index c5324bf85a7..f3c975c8a2c 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -780,7 +780,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if has_custom_message { err.note(&msg); } else { - err.message = vec![(msg, Style::NoStyle)]; + err.message = + vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)]; } if snippet.starts_with('&') { // This is already a literal borrow and the obligation is failing diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 8d9b3377a69..636018dbb22 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -176,7 +176,7 @@ struct BufferEmitter { impl Emitter for BufferEmitter { fn emit_diagnostic(&mut self, diag: &Diagnostic) { let mut buffer = self.buffer.borrow_mut(); - buffer.messages.push(format!("error from rustc: {}", diag.message[0].0)); + buffer.messages.push(format!("error from rustc: {}", diag.message[0].0.as_str())); if diag.is_error() { buffer.has_errors = true; }