errors: introduce DiagnosticMessage

Introduce a `DiagnosticMessage` type that will enable diagnostic
messages to be simple strings or Fluent identifiers.
`DiagnosticMessage` is now used in the implementation of the standard
`DiagnosticBuilder` APIs.

Signed-off-by: David Wood <david.wood@huawei.com>
This commit is contained in:
David Wood 2022-03-23 07:34:20 +00:00
parent a22cf2af05
commit 8c684563a5
10 changed files with 79 additions and 43 deletions

View File

@ -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,
})));

View File

@ -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,

View File

@ -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
}
}

View File

@ -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],

View File

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

View File

@ -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;

View File

@ -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,
);
}

View File

@ -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();

View File

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

View File

@ -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;
}