errors: implement fallback diagnostic translation

This commit updates the signatures of all diagnostic functions to accept
types that can be converted into a `DiagnosticMessage`. This enables
existing diagnostic calls to continue to work as before and Fluent
identifiers to be provided. The `SessionDiagnostic` derive just
generates normal diagnostic calls, so these APIs had to be modified to
accept Fluent identifiers.

In addition, loading of the "fallback" Fluent bundle, which contains the
built-in English messages, has been implemented.

Each diagnostic now has "arguments" which correspond to variables in the
Fluent messages (necessary to render a Fluent message) but no API for
adding arguments has been added yet. Therefore, diagnostics (that do not
require interpolation) can be converted to use Fluent identifiers and
will be output as before.
This commit is contained in:
David Wood 2022-03-26 07:27:43 +00:00
parent c45f29595d
commit 7f91697b50
46 changed files with 919 additions and 293 deletions

View file

@ -7,16 +7,22 @@
use crate::emitter::FileWithAnnotatedLines;
use crate::snippet::Line;
use crate::{CodeSuggestion, Diagnostic, DiagnosticId, Emitter, Level, MultiSpan, SubDiagnostic};
use crate::{
CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle, Level,
MultiSpan, Style, SubDiagnostic,
};
use annotate_snippets::display_list::{DisplayList, FormatOptions};
use annotate_snippets::snippet::*;
use rustc_data_structures::sync::Lrc;
use rustc_error_messages::FluentArgs;
use rustc_span::source_map::SourceMap;
use rustc_span::SourceFile;
/// Generates diagnostics using annotate-snippet
pub struct AnnotateSnippetEmitterWriter {
source_map: Option<Lrc<SourceMap>>,
fallback_bundle: Lrc<FluentBundle>,
/// If true, hides the longer explanation text
short_message: bool,
/// If true, will normalize line numbers with `LL` to prevent noise in UI test diffs.
@ -28,8 +34,10 @@ pub struct AnnotateSnippetEmitterWriter {
impl Emitter for AnnotateSnippetEmitterWriter {
/// The entry point for the diagnostics generation
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
let fluent_args = self.to_fluent_args(diag.args());
let mut children = diag.children.clone();
let (mut primary_span, suggestions) = self.primary_span_formatted(&diag);
let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args);
self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
&self.source_map,
@ -41,7 +49,8 @@ impl Emitter for AnnotateSnippetEmitterWriter {
self.emit_messages_default(
&diag.level,
diag.message().to_string(),
&diag.message,
&fluent_args,
&diag.code,
&primary_span,
&children,
@ -53,6 +62,14 @@ impl Emitter for AnnotateSnippetEmitterWriter {
self.source_map.as_ref()
}
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
None
}
fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> {
&self.fallback_bundle
}
fn should_show_explain(&self) -> bool {
!self.short_message
}
@ -82,10 +99,11 @@ fn annotation_type_for_level(level: Level) -> AnnotationType {
impl AnnotateSnippetEmitterWriter {
pub fn new(
source_map: Option<Lrc<SourceMap>>,
fallback_bundle: Lrc<FluentBundle>,
short_message: bool,
macro_backtrace: bool,
) -> Self {
Self { source_map, short_message, ui_testing: false, macro_backtrace }
Self { source_map, fallback_bundle, short_message, ui_testing: false, macro_backtrace }
}
/// Allows to modify `Self` to enable or disable the `ui_testing` flag.
@ -99,12 +117,14 @@ impl AnnotateSnippetEmitterWriter {
fn emit_messages_default(
&mut self,
level: &Level,
message: String,
messages: &[(DiagnosticMessage, Style)],
args: &FluentArgs<'_>,
code: &Option<DiagnosticId>,
msp: &MultiSpan,
_children: &[SubDiagnostic],
_suggestions: &[CodeSuggestion],
) {
let message = self.translate_messages(messages, args);
if let Some(source_map) = &self.source_map {
// Make sure our primary file comes first
let primary_lo = if let Some(ref primary_span) = msp.primary_span().as_ref() {
@ -120,8 +140,7 @@ impl AnnotateSnippetEmitterWriter {
// should be done if it happens
return;
};
let mut annotated_files =
FileWithAnnotatedLines::collect_annotations(msp, &self.source_map);
let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp);
if let Ok(pos) =
annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name))
{

View file

@ -4,10 +4,12 @@ use crate::{
SuggestionStyle, ToolMetadata,
};
use rustc_data_structures::stable_map::FxHashMap;
use rustc_error_messages::FluentValue;
use rustc_lint_defs::{Applicability, LintExpectationId};
use rustc_serialize::json::Json;
use rustc_span::edition::LATEST_STABLE_EDITION;
use rustc_span::{Span, DUMMY_SP};
use std::borrow::Cow;
use std::fmt;
use std::hash::{Hash, Hasher};
@ -16,6 +18,28 @@ use std::hash::{Hash, Hasher};
#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
pub struct SuggestionsDisabled;
/// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of
/// `DiagnosticArg` are converted to `FluentArgs` (consuming the collection) at the start of
/// diagnostic emission.
pub type DiagnosticArg<'source> = (Cow<'source, str>, DiagnosticArgValue<'source>);
/// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted
/// to a `FluentValue` by the emitter to be used in diagnostic translation.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
pub enum DiagnosticArgValue<'source> {
Str(Cow<'source, str>),
Number(usize),
}
impl<'source> Into<FluentValue<'source>> for DiagnosticArgValue<'source> {
fn into(self) -> FluentValue<'source> {
match self {
DiagnosticArgValue::Str(s) => From::from(s),
DiagnosticArgValue::Number(n) => From::from(n),
}
}
}
#[must_use]
#[derive(Clone, Debug, Encodable, Decodable)]
pub struct Diagnostic {
@ -28,6 +52,7 @@ pub struct Diagnostic {
pub span: MultiSpan,
pub children: Vec<SubDiagnostic>,
pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
args: Vec<DiagnosticArg<'static>>,
/// This is not used for highlighting or rendering any error message. Rather, it can be used
/// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
@ -103,18 +128,23 @@ impl StringPart {
}
impl Diagnostic {
pub fn new(level: Level, message: &str) -> Self {
pub fn new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self {
Diagnostic::new_with_code(level, None, message)
}
pub fn new_with_code(level: Level, code: Option<DiagnosticId>, message: &str) -> Self {
pub fn new_with_code<M: Into<DiagnosticMessage>>(
level: Level,
code: Option<DiagnosticId>,
message: M,
) -> Self {
Diagnostic {
level,
message: vec![(DiagnosticMessage::Str(message.to_owned()), Style::NoStyle)],
message: vec![(message.into(), Style::NoStyle)],
code,
span: MultiSpan::new(),
children: vec![],
suggestions: Ok(vec![]),
args: vec![],
sort_span: DUMMY_SP,
is_lint: false,
}
@ -232,7 +262,7 @@ impl Diagnostic {
self.set_span(after);
for span_label in before.span_labels() {
if let Some(label) = span_label.label {
self.span.push_span_message(after, label);
self.span.push_span_label(after, label);
}
}
self
@ -326,52 +356,67 @@ impl Diagnostic {
}
/// Add a note attached to this diagnostic.
pub fn note(&mut self, msg: &str) -> &mut Self {
pub fn note(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
self.sub(Level::Note, msg, MultiSpan::new(), None);
self
}
pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
pub fn highlighted_note<M: Into<DiagnosticMessage>>(
&mut self,
msg: Vec<(M, Style)>,
) -> &mut Self {
self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
self
}
/// Prints the span with a note above it.
/// This is like [`Diagnostic::note()`], but it gets its own span.
pub fn note_once(&mut self, msg: &str) -> &mut Self {
pub fn note_once(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
self.sub(Level::OnceNote, msg, MultiSpan::new(), None);
self
}
/// Prints the span with a note above it.
/// This is like [`Diagnostic::note()`], but it gets its own span.
pub fn span_note<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
pub fn span_note<S: Into<MultiSpan>>(
&mut self,
sp: S,
msg: impl Into<DiagnosticMessage>,
) -> &mut Self {
self.sub(Level::Note, msg, sp.into(), None);
self
}
/// Prints the span with a note above it.
/// This is like [`Diagnostic::note()`], but it gets its own span.
pub fn span_note_once<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
pub fn span_note_once<S: Into<MultiSpan>>(
&mut self,
sp: S,
msg: impl Into<DiagnosticMessage>,
) -> &mut Self {
self.sub(Level::OnceNote, msg, sp.into(), None);
self
}
/// Add a warning attached to this diagnostic.
pub fn warn(&mut self, msg: &str) -> &mut Self {
pub fn warn(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
self.sub(Level::Warning, msg, MultiSpan::new(), None);
self
}
/// Prints the span with a warning above it.
/// This is like [`Diagnostic::warn()`], but it gets its own span.
pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
pub fn span_warn<S: Into<MultiSpan>>(
&mut self,
sp: S,
msg: impl Into<DiagnosticMessage>,
) -> &mut Self {
self.sub(Level::Warning, msg, sp.into(), None);
self
}
/// Add a help message attached to this diagnostic.
pub fn help(&mut self, msg: &str) -> &mut Self {
pub fn help(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
self.sub(Level::Help, msg, MultiSpan::new(), None);
self
}
@ -384,7 +429,11 @@ impl Diagnostic {
/// Prints the span with some help above it.
/// This is like [`Diagnostic::help()`], but it gets its own span.
pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
pub fn span_help<S: Into<MultiSpan>>(
&mut self,
sp: S,
msg: impl Into<DiagnosticMessage>,
) -> &mut Self {
self.sub(Level::Help, msg, sp.into(), None);
self
}
@ -420,7 +469,7 @@ impl Diagnostic {
/// In other words, multiple changes need to be applied as part of this suggestion.
pub fn multipart_suggestion(
&mut self,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
) -> &mut Self {
@ -436,7 +485,7 @@ impl Diagnostic {
/// In other words, multiple changes need to be applied as part of this suggestion.
pub fn multipart_suggestion_verbose(
&mut self,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
) -> &mut Self {
@ -450,7 +499,7 @@ impl Diagnostic {
/// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`].
pub fn multipart_suggestion_with_style(
&mut self,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
style: SuggestionStyle,
@ -463,7 +512,7 @@ impl Diagnostic {
.map(|(span, snippet)| SubstitutionPart { snippet, span })
.collect(),
}],
msg: DiagnosticMessage::Str(msg.to_owned()),
msg: msg.into(),
style,
applicability,
tool_metadata: Default::default(),
@ -479,7 +528,7 @@ impl Diagnostic {
/// improve understandability.
pub fn tool_only_multipart_suggestion(
&mut self,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
) -> &mut Self {
@ -491,7 +540,7 @@ impl Diagnostic {
.map(|(span, snippet)| SubstitutionPart { snippet, span })
.collect(),
}],
msg: DiagnosticMessage::Str(msg.to_owned()),
msg: msg.into(),
style: SuggestionStyle::CompletelyHidden,
applicability,
tool_metadata: Default::default(),
@ -519,7 +568,7 @@ impl Diagnostic {
pub fn span_suggestion(
&mut self,
sp: Span,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: String,
applicability: Applicability,
) -> &mut Self {
@ -537,7 +586,7 @@ impl Diagnostic {
pub fn span_suggestion_with_style(
&mut self,
sp: Span,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: String,
applicability: Applicability,
style: SuggestionStyle,
@ -546,7 +595,7 @@ impl Diagnostic {
substitutions: vec![Substitution {
parts: vec![SubstitutionPart { snippet: suggestion, span: sp }],
}],
msg: DiagnosticMessage::Str(msg.to_owned()),
msg: msg.into(),
style,
applicability,
tool_metadata: Default::default(),
@ -558,7 +607,7 @@ impl Diagnostic {
pub fn span_suggestion_verbose(
&mut self,
sp: Span,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: String,
applicability: Applicability,
) -> &mut Self {
@ -577,7 +626,7 @@ impl Diagnostic {
pub fn span_suggestions(
&mut self,
sp: Span,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestions: impl Iterator<Item = String>,
applicability: Applicability,
) -> &mut Self {
@ -589,7 +638,7 @@ impl Diagnostic {
.collect();
self.push_suggestion(CodeSuggestion {
substitutions,
msg: DiagnosticMessage::Str(msg.to_owned()),
msg: msg.into(),
style: SuggestionStyle::ShowCode,
applicability,
tool_metadata: Default::default(),
@ -601,7 +650,7 @@ impl Diagnostic {
/// See also [`Diagnostic::span_suggestion()`].
pub fn multipart_suggestions(
&mut self,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestions: impl Iterator<Item = Vec<(Span, String)>>,
applicability: Applicability,
) -> &mut Self {
@ -614,7 +663,7 @@ impl Diagnostic {
.collect(),
})
.collect(),
msg: DiagnosticMessage::Str(msg.to_owned()),
msg: msg.into(),
style: SuggestionStyle::ShowCode,
applicability,
tool_metadata: Default::default(),
@ -628,7 +677,7 @@ impl Diagnostic {
pub fn span_suggestion_short(
&mut self,
sp: Span,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: String,
applicability: Applicability,
) -> &mut Self {
@ -651,7 +700,7 @@ impl Diagnostic {
pub fn span_suggestion_hidden(
&mut self,
sp: Span,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: String,
applicability: Applicability,
) -> &mut Self {
@ -672,7 +721,7 @@ impl Diagnostic {
pub fn tool_only_span_suggestion(
&mut self,
sp: Span,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: String,
applicability: Applicability,
) -> &mut Self {
@ -690,13 +739,13 @@ impl Diagnostic {
/// the suggestion in a tool-specific way, as it may not even directly involve Rust code.
pub fn tool_only_suggestion_with_metadata(
&mut self,
msg: &str,
msg: impl Into<DiagnosticMessage>,
applicability: Applicability,
tool_metadata: Json,
) {
self.push_suggestion(CodeSuggestion {
substitutions: vec![],
msg: DiagnosticMessage::Str(msg.to_owned()),
msg: msg.into(),
style: SuggestionStyle::CompletelyHidden,
applicability,
tool_metadata: ToolMetadata::new(tool_metadata),
@ -730,13 +779,13 @@ impl Diagnostic {
self.code.clone()
}
pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self {
self.message[0] = (DiagnosticMessage::Str(msg.into()), Style::NoStyle);
pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
self.message[0] = (msg.into(), Style::NoStyle);
self
}
pub fn message(&self) -> DiagnosticMessage {
DiagnosticMessage::Str(self.message.iter().map(|i| i.0.as_str()).collect::<String>())
pub fn args(&self) -> &[DiagnosticArg<'static>] {
&self.args
}
pub fn styled_message(&self) -> &Vec<(DiagnosticMessage, Style)> {
@ -750,13 +799,13 @@ impl Diagnostic {
pub fn sub(
&mut self,
level: Level,
message: &str,
message: impl Into<DiagnosticMessage>,
span: MultiSpan,
render_span: Option<MultiSpan>,
) {
let sub = SubDiagnostic {
level,
message: vec![(DiagnosticMessage::Str(message.to_owned()), Style::NoStyle)],
message: vec![(message.into(), Style::NoStyle)],
span,
render_span,
};
@ -765,14 +814,14 @@ impl Diagnostic {
/// Convenience function for internal use, clients should use one of the
/// public methods above.
fn sub_with_highlights(
fn sub_with_highlights<M: Into<DiagnosticMessage>>(
&mut self,
level: Level,
mut message: Vec<(String, Style)>,
mut message: Vec<(M, Style)>,
span: MultiSpan,
render_span: Option<MultiSpan>,
) {
let message = message.drain(..).map(|m| (DiagnosticMessage::Str(m.0), m.1)).collect();
let message = message.drain(..).map(|m| (m.0.into(), m.1)).collect();
let sub = SubDiagnostic { level, message, span, render_span };
self.children.push(sub);
}
@ -813,13 +862,3 @@ impl PartialEq for Diagnostic {
self.keys() == other.keys()
}
}
impl SubDiagnostic {
pub fn message(&self) -> DiagnosticMessage {
DiagnosticMessage::Str(self.message.iter().map(|i| i.0.as_str()).collect::<String>())
}
pub fn styled_message(&self) -> &Vec<(DiagnosticMessage, Style)> {
&self.message
}
}

View file

@ -1,4 +1,4 @@
use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString, ErrorGuaranteed};
use crate::{Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed};
use crate::{Handler, Level, MultiSpan, StashKey};
use rustc_lint_defs::Applicability;
@ -99,7 +99,10 @@ mod sealed_level_is_error {
impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
/// Convenience function for internal use, clients should use one of the
/// `struct_*` methods on [`Handler`].
crate fn new_guaranteeing_error<const L: Level>(handler: &'a Handler, message: &str) -> Self
crate fn new_guaranteeing_error<M: Into<DiagnosticMessage>, const L: Level>(
handler: &'a Handler,
message: M,
) -> Self
where
(): sealed_level_is_error::IsError<L>,
{
@ -163,7 +166,11 @@ impl EmissionGuarantee for ErrorGuaranteed {
impl<'a> DiagnosticBuilder<'a, ()> {
/// Convenience function for internal use, clients should use one of the
/// `struct_*` methods on [`Handler`].
crate fn new(handler: &'a Handler, level: Level, message: &str) -> Self {
crate fn new<M: Into<DiagnosticMessage>>(
handler: &'a Handler,
level: Level,
message: M,
) -> Self {
let diagnostic = Diagnostic::new_with_code(level, None, message);
Self::new_diagnostic(handler, diagnostic)
}
@ -201,7 +208,7 @@ impl EmissionGuarantee for () {
impl<'a> DiagnosticBuilder<'a, !> {
/// Convenience function for internal use, clients should use one of the
/// `struct_*` methods on [`Handler`].
crate fn new_fatal(handler: &'a Handler, message: &str) -> Self {
crate fn new_fatal(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self {
let diagnostic = Diagnostic::new_with_code(Level::Fatal, None, message);
Self::new_diagnostic_fatal(handler, diagnostic)
}
@ -346,7 +353,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
}
// Take the `Diagnostic` by replacing it with a dummy.
let dummy = Diagnostic::new(Level::Allow, "");
let dummy = Diagnostic::new(Level::Allow, DiagnosticMessage::Str("".to_string()));
let diagnostic = std::mem::replace(&mut *self.inner.diagnostic, dummy);
// Disable the ICE on `Drop`.
@ -434,25 +441,25 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
found: DiagnosticStyledString,
) -> &mut Self);
forward!(pub fn note(&mut self, msg: &str) -> &mut Self);
forward!(pub fn note_once(&mut self, msg: &str) -> &mut Self);
forward!(pub fn note(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self);
forward!(pub fn note_once(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self);
forward!(pub fn span_note(
&mut self,
sp: impl Into<MultiSpan>,
msg: &str,
msg: impl Into<DiagnosticMessage>,
) -> &mut Self);
forward!(pub fn span_note_once(
&mut self,
sp: impl Into<MultiSpan>,
msg: &str,
msg: impl Into<DiagnosticMessage>,
) -> &mut Self);
forward!(pub fn warn(&mut self, msg: &str) -> &mut Self);
forward!(pub fn warn(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self);
forward!(pub fn span_warn(&mut self, sp: impl Into<MultiSpan>, msg: &str) -> &mut Self);
forward!(pub fn help(&mut self, msg: &str) -> &mut Self);
forward!(pub fn help(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self);
forward!(pub fn span_help(
&mut self,
sp: impl Into<MultiSpan>,
msg: &str,
msg: impl Into<DiagnosticMessage>,
) -> &mut Self);
forward!(pub fn help_use_latest_edition(&mut self,) -> &mut Self);
forward!(pub fn set_is_lint(&mut self,) -> &mut Self);
@ -461,67 +468,67 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
forward!(pub fn multipart_suggestion(
&mut self,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
) -> &mut Self);
forward!(pub fn multipart_suggestion_verbose(
&mut self,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
) -> &mut Self);
forward!(pub fn tool_only_multipart_suggestion(
&mut self,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
) -> &mut Self);
forward!(pub fn span_suggestion(
&mut self,
sp: Span,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: String,
applicability: Applicability,
) -> &mut Self);
forward!(pub fn span_suggestions(
&mut self,
sp: Span,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestions: impl Iterator<Item = String>,
applicability: Applicability,
) -> &mut Self);
forward!(pub fn multipart_suggestions(
&mut self,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestions: impl Iterator<Item = Vec<(Span, String)>>,
applicability: Applicability,
) -> &mut Self);
forward!(pub fn span_suggestion_short(
&mut self,
sp: Span,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: String,
applicability: Applicability,
) -> &mut Self);
forward!(pub fn span_suggestion_verbose(
&mut self,
sp: Span,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: String,
applicability: Applicability,
) -> &mut Self);
forward!(pub fn span_suggestion_hidden(
&mut self,
sp: Span,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: String,
applicability: Applicability,
) -> &mut Self);
forward!(pub fn tool_only_span_suggestion(
&mut self,
sp: Span,
msg: &str,
msg: impl Into<DiagnosticMessage>,
suggestion: String,
applicability: Applicability,
) -> &mut Self);
@ -547,7 +554,9 @@ impl Drop for DiagnosticBuilderInner<'_> {
if !panicking() {
handler.emit_diagnostic(&mut Diagnostic::new(
Level::Bug,
"the following error was constructed but not emitted",
DiagnosticMessage::Str(
"the following error was constructed but not emitted".to_string(),
),
));
handler.emit_diagnostic(&mut self.diagnostic);
panic!();

View file

@ -15,14 +15,15 @@ use rustc_span::{SourceFile, Span};
use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString};
use crate::styled_buffer::StyledBuffer;
use crate::{
CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Handler, Level, MultiSpan,
SubDiagnostic, SubstitutionHighlight, SuggestionStyle,
CodeSuggestion, Diagnostic, DiagnosticArg, DiagnosticId, DiagnosticMessage, FluentBundle,
Handler, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, SuggestionStyle,
};
use rustc_lint_defs::pluralize;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use rustc_error_messages::FluentArgs;
use rustc_span::hygiene::{ExpnKind, MacroKind};
use std::borrow::Cow;
use std::cmp::{max, min, Reverse};
@ -58,13 +59,23 @@ impl HumanReadableErrorType {
self,
dst: Box<dyn Write + Send>,
source_map: Option<Lrc<SourceMap>>,
fallback_bundle: Lrc<FluentBundle>,
teach: bool,
terminal_width: Option<usize>,
macro_backtrace: bool,
) -> EmitterWriter {
let (short, color_config) = self.unzip();
let color = color_config.suggests_using_colors();
EmitterWriter::new(dst, source_map, short, teach, color, terminal_width, macro_backtrace)
EmitterWriter::new(
dst,
source_map,
fallback_bundle,
short,
teach,
color,
terminal_width,
macro_backtrace,
)
}
}
@ -212,6 +223,68 @@ pub trait Emitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>>;
/// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no
/// language was requested by the user then this will be `None` and `fallback_fluent_bundle`
/// should be used.
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>>;
/// Return `FluentBundle` with localized diagnostics for the default locale of the compiler.
/// Used when the user has not requested a specific language or when a localized diagnostic is
/// unavailable for the requested locale.
fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle>;
/// Convert diagnostic arguments (a rustc internal type that exists to implement
/// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation.
///
/// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
/// passed around as a reference thereafter.
fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> {
FromIterator::from_iter(args.to_vec().drain(..))
}
/// Convert `DiagnosticMessage`s to a string, performing translation if necessary.
fn translate_messages(
&self,
messages: &[(DiagnosticMessage, Style)],
args: &FluentArgs<'_>,
) -> Cow<'_, str> {
Cow::Owned(
messages.iter().map(|(m, _)| self.translate_message(m, args)).collect::<String>(),
)
}
/// Convert a `DiagnosticMessage` to a string, performing translation if necessary.
fn translate_message<'a>(
&'a self,
message: &'a DiagnosticMessage,
args: &'a FluentArgs<'_>,
) -> Cow<'_, str> {
trace!(?message);
let (identifier, attr) = match message {
DiagnosticMessage::Str(msg) => return Cow::Borrowed(&msg),
DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
};
let bundle = match self.fluent_bundle() {
Some(bundle) if bundle.has_message(&identifier) => bundle,
_ => self.fallback_fluent_bundle(),
};
let message = bundle.get_message(&identifier).expect("missing diagnostic in fluent bundle");
let value = match attr {
Some(attr) => {
message.get_attribute(attr).expect("missing attribute in fluent message").value()
}
None => message.value().expect("missing value in fluent message"),
};
let mut err = vec![];
let translated = bundle.format_pattern(value, Some(&args), &mut err);
trace!(?translated, ?err);
debug_assert!(err.is_empty());
translated
}
/// Formats the substitutions of the primary_span
///
/// There are a lot of conditions to this method, but in short:
@ -225,10 +298,12 @@ pub trait Emitter {
fn primary_span_formatted<'a>(
&mut self,
diag: &'a Diagnostic,
fluent_args: &FluentArgs<'_>,
) -> (MultiSpan, &'a [CodeSuggestion]) {
let mut primary_span = diag.span.clone();
let suggestions = diag.suggestions.as_ref().map_or(&[][..], |suggestions| &suggestions[..]);
if let Some((sugg, rest)) = suggestions.split_first() {
let msg = self.translate_message(&sugg.msg, fluent_args);
if rest.is_empty() &&
// ^ if there is only one suggestion
// don't display multi-suggestions as labels
@ -236,7 +311,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.as_str().split_whitespace().count() < 10 &&
msg.split_whitespace().count() < 10 &&
// don't display multiline suggestions as labels
!sugg.substitutions[0].parts[0].snippet.contains('\n') &&
![
@ -252,12 +327,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.as_str())
format!("help: {}", &msg)
} else {
// Show the default suggestion text with the substitution
format!(
"help: {}{}: `{}`",
sugg.msg.as_str(),
&msg,
if self
.source_map()
.map(|sm| is_case_difference(
@ -492,9 +567,19 @@ impl Emitter for EmitterWriter {
self.sm.as_ref()
}
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
None
}
fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> {
&self.fallback_bundle
}
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
let fluent_args = self.to_fluent_args(diag.args());
let mut children = diag.children.clone();
let (mut primary_span, suggestions) = self.primary_span_formatted(&diag);
let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args);
debug!("emit_diagnostic: suggestions={:?}", suggestions);
self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
@ -507,7 +592,8 @@ impl Emitter for EmitterWriter {
self.emit_messages_default(
&diag.level,
&diag.styled_message(),
&diag.message,
&fluent_args,
&diag.code,
&primary_span,
&children,
@ -536,6 +622,15 @@ impl Emitter for SilentEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
None
}
fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> {
panic!("silent emitter attempted to translate message")
}
fn emit_diagnostic(&mut self, d: &Diagnostic) {
if d.level == Level::Fatal {
let mut d = d.clone();
@ -591,6 +686,7 @@ impl ColorConfig {
pub struct EmitterWriter {
dst: Destination,
sm: Option<Lrc<SourceMap>>,
fallback_bundle: Lrc<FluentBundle>,
short_message: bool,
teach: bool,
ui_testing: bool,
@ -610,6 +706,7 @@ impl EmitterWriter {
pub fn stderr(
color_config: ColorConfig,
source_map: Option<Lrc<SourceMap>>,
fallback_bundle: Lrc<FluentBundle>,
short_message: bool,
teach: bool,
terminal_width: Option<usize>,
@ -619,6 +716,7 @@ impl EmitterWriter {
EmitterWriter {
dst,
sm: source_map,
fallback_bundle,
short_message,
teach,
ui_testing: false,
@ -630,6 +728,7 @@ impl EmitterWriter {
pub fn new(
dst: Box<dyn Write + Send>,
source_map: Option<Lrc<SourceMap>>,
fallback_bundle: Lrc<FluentBundle>,
short_message: bool,
teach: bool,
colored: bool,
@ -639,6 +738,7 @@ impl EmitterWriter {
EmitterWriter {
dst: Raw(dst, colored),
sm: source_map,
fallback_bundle,
short_message,
teach,
ui_testing: false,
@ -1177,6 +1277,7 @@ impl EmitterWriter {
&self,
buffer: &mut StyledBuffer,
msg: &[(DiagnosticMessage, Style)],
args: &FluentArgs<'_>,
padding: usize,
label: &str,
override_style: Option<Style>,
@ -1229,7 +1330,7 @@ impl EmitterWriter {
// very *weird* formats
// see?
for &(ref text, ref style) in msg.iter() {
let text = text.as_str();
let text = self.translate_message(text, args);
let lines = text.split('\n').collect::<Vec<_>>();
if lines.len() > 1 {
for (i, line) in lines.iter().enumerate() {
@ -1240,7 +1341,7 @@ impl EmitterWriter {
buffer.append(line_number, line, style_or_override(*style, override_style));
}
} else {
buffer.append(line_number, text, style_or_override(*style, override_style));
buffer.append(line_number, &text, style_or_override(*style, override_style));
}
}
}
@ -1249,6 +1350,7 @@ impl EmitterWriter {
&mut self,
msp: &MultiSpan,
msg: &[(DiagnosticMessage, Style)],
args: &FluentArgs<'_>,
code: &Option<DiagnosticId>,
level: &Level,
max_line_num_len: usize,
@ -1267,7 +1369,7 @@ impl EmitterWriter {
buffer.append(0, level.to_str(), Style::MainHeaderMsg);
buffer.append(0, ": ", Style::NoStyle);
}
self.msg_to_buffer(&mut buffer, msg, max_line_num_len, "note", None);
self.msg_to_buffer(&mut buffer, msg, args, max_line_num_len, "note", None);
} else {
let mut label_width = 0;
// The failure note level itself does not provide any useful diagnostic information
@ -1288,9 +1390,9 @@ impl EmitterWriter {
label_width += 2;
}
for &(ref text, _) in msg.iter() {
let text = text.as_str();
let text = self.translate_message(text, args);
// Account for newlines to align output to its label.
for (line, text) in normalize_whitespace(text).lines().enumerate() {
for (line, text) in normalize_whitespace(&text).lines().enumerate() {
buffer.append(
0 + line,
&format!(
@ -1304,7 +1406,7 @@ impl EmitterWriter {
}
}
let mut annotated_files = FileWithAnnotatedLines::collect_annotations(msp, &self.sm);
let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp);
// Make sure our primary file comes first
let (primary_lo, sm) = if let (Some(sm), Some(ref primary_span)) =
@ -1588,6 +1690,7 @@ impl EmitterWriter {
fn emit_suggestion_default(
&mut self,
suggestion: &CodeSuggestion,
args: &FluentArgs<'_>,
level: &Level,
max_line_num_len: usize,
) -> io::Result<()> {
@ -1614,6 +1717,7 @@ impl EmitterWriter {
self.msg_to_buffer(
&mut buffer,
&[(suggestion.msg.to_owned(), Style::NoStyle)],
args,
max_line_num_len,
"suggestion",
Some(Style::HeaderMsg),
@ -1855,6 +1959,7 @@ impl EmitterWriter {
&mut self,
level: &Level,
message: &[(DiagnosticMessage, Style)],
args: &FluentArgs<'_>,
code: &Option<DiagnosticId>,
span: &MultiSpan,
children: &[SubDiagnostic],
@ -1867,7 +1972,7 @@ impl EmitterWriter {
num_decimal_digits(n)
};
match self.emit_message_default(span, message, code, level, max_line_num_len, false) {
match self.emit_message_default(span, message, args, code, level, max_line_num_len, false) {
Ok(()) => {
if !children.is_empty()
|| suggestions.iter().any(|s| s.style != SuggestionStyle::CompletelyHidden)
@ -1890,7 +1995,8 @@ impl EmitterWriter {
let span = child.render_span.as_ref().unwrap_or(&child.span);
if let Err(err) = self.emit_message_default(
&span,
&child.styled_message(),
&child.message,
args,
&None,
&child.level,
max_line_num_len,
@ -1906,6 +2012,7 @@ impl EmitterWriter {
if let Err(e) = self.emit_message_default(
&MultiSpan::new(),
&[(sugg.msg.to_owned(), Style::HeaderMsg)],
args,
&None,
&Level::Help,
max_line_num_len,
@ -1914,7 +2021,7 @@ impl EmitterWriter {
panic!("failed to emit error: {}", e);
}
} else if let Err(e) =
self.emit_suggestion_default(sugg, &Level::Help, max_line_num_len)
self.emit_suggestion_default(sugg, args, &Level::Help, max_line_num_len)
{
panic!("failed to emit error: {}", e);
};
@ -1940,8 +2047,9 @@ impl FileWithAnnotatedLines {
/// Preprocess all the annotations so that they are grouped by file and by line number
/// This helps us quickly iterate over the whole message (including secondary file spans)
pub fn collect_annotations(
emitter: &dyn Emitter,
args: &FluentArgs<'_>,
msp: &MultiSpan,
source_map: &Option<Lrc<SourceMap>>,
) -> Vec<FileWithAnnotatedLines> {
fn add_annotation_to_file(
file_vec: &mut Vec<FileWithAnnotatedLines>,
@ -1976,7 +2084,7 @@ impl FileWithAnnotatedLines {
let mut output = vec![];
let mut multiline_annotations = vec![];
if let Some(ref sm) = source_map {
if let Some(ref sm) = emitter.source_map() {
for span_label in msp.span_labels() {
if span_label.span.is_dummy() {
continue;
@ -2003,7 +2111,10 @@ impl FileWithAnnotatedLines {
start_col: lo.col_display,
end_col: hi.col_display,
is_primary: span_label.is_primary,
label: span_label.label.map(|m| m.to_string()),
label: span_label
.label
.as_ref()
.map(|m| emitter.translate_message(m, args).to_string()),
overlaps_exactly: false,
};
multiline_annotations.push((lo.file, ml));
@ -2012,7 +2123,10 @@ impl FileWithAnnotatedLines {
start_col: lo.col_display,
end_col: hi.col_display,
is_primary: span_label.is_primary,
label: span_label.label.map(|m| m.to_string()),
label: span_label
.label
.as_ref()
.map(|m| emitter.translate_message(m, args).to_string()),
annotation_type: AnnotationType::Singleline,
};
add_annotation_to_file(&mut output, lo.file, lo.line, ann);

View file

@ -15,10 +15,11 @@ use crate::emitter::{Emitter, HumanReadableErrorType};
use crate::registry::Registry;
use crate::DiagnosticId;
use crate::ToolMetadata;
use crate::{CodeSuggestion, MultiSpan, SpanLabel, SubDiagnostic};
use crate::{CodeSuggestion, FluentBundle, MultiSpan, SpanLabel, SubDiagnostic};
use rustc_lint_defs::Applicability;
use rustc_data_structures::sync::Lrc;
use rustc_error_messages::FluentArgs;
use rustc_span::hygiene::ExpnData;
use rustc_span::Span;
use std::io::{self, Write};
@ -36,6 +37,7 @@ pub struct JsonEmitter {
dst: Box<dyn Write + Send>,
registry: Option<Registry>,
sm: Lrc<SourceMap>,
fallback_bundle: Lrc<FluentBundle>,
pretty: bool,
ui_testing: bool,
json_rendered: HumanReadableErrorType,
@ -47,6 +49,7 @@ impl JsonEmitter {
pub fn stderr(
registry: Option<Registry>,
source_map: Lrc<SourceMap>,
fallback_bundle: Lrc<FluentBundle>,
pretty: bool,
json_rendered: HumanReadableErrorType,
terminal_width: Option<usize>,
@ -56,6 +59,7 @@ impl JsonEmitter {
dst: Box::new(io::BufWriter::new(io::stderr())),
registry,
sm: source_map,
fallback_bundle,
pretty,
ui_testing: false,
json_rendered,
@ -67,6 +71,7 @@ impl JsonEmitter {
pub fn basic(
pretty: bool,
json_rendered: HumanReadableErrorType,
fallback_bundle: Lrc<FluentBundle>,
terminal_width: Option<usize>,
macro_backtrace: bool,
) -> JsonEmitter {
@ -74,6 +79,7 @@ impl JsonEmitter {
JsonEmitter::stderr(
None,
Lrc::new(SourceMap::new(file_path_mapping)),
fallback_bundle,
pretty,
json_rendered,
terminal_width,
@ -85,6 +91,7 @@ impl JsonEmitter {
dst: Box<dyn Write + Send>,
registry: Option<Registry>,
source_map: Lrc<SourceMap>,
fallback_bundle: Lrc<FluentBundle>,
pretty: bool,
json_rendered: HumanReadableErrorType,
terminal_width: Option<usize>,
@ -94,6 +101,7 @@ impl JsonEmitter {
dst,
registry,
sm: source_map,
fallback_bundle,
pretty,
ui_testing: false,
json_rendered,
@ -173,6 +181,14 @@ impl Emitter for JsonEmitter {
Some(&self.sm)
}
fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
None
}
fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> {
&self.fallback_bundle
}
fn should_show_explain(&self) -> bool {
!matches!(self.json_rendered, HumanReadableErrorType::Short(_))
}
@ -345,14 +361,18 @@ 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().to_string(),
code: None,
level: "help",
spans: DiagnosticSpan::from_suggestion(sugg, je),
children: vec![],
rendered: None,
tool_metadata: sugg.tool_metadata.clone(),
let args = je.to_fluent_args(diag.args());
let sugg = diag.suggestions.iter().flatten().map(|sugg| {
let translated_message = je.translate_message(&sugg.msg, &args);
Diagnostic {
message: translated_message.to_string(),
code: None,
level: "help",
spans: DiagnosticSpan::from_suggestion(sugg, &args, je),
children: vec![],
rendered: None,
tool_metadata: sugg.tool_metadata.clone(),
}
});
// generate regular command line output and store it in the json
@ -375,6 +395,7 @@ impl Diagnostic {
.new_emitter(
Box::new(buf),
Some(je.sm.clone()),
je.fallback_bundle.clone(),
false,
je.terminal_width,
je.macro_backtrace,
@ -384,15 +405,16 @@ impl Diagnostic {
let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
let output = String::from_utf8(output).unwrap();
let translated_message = je.translate_messages(&diag.message, &args);
Diagnostic {
message: diag.message().to_string(),
message: translated_message.to_string(),
code: DiagnosticCode::map_opt_string(diag.code.clone(), je),
level: diag.level.to_str(),
spans: DiagnosticSpan::from_multispan(&diag.span, je),
spans: DiagnosticSpan::from_multispan(&diag.span, &args, je),
children: diag
.children
.iter()
.map(|c| Diagnostic::from_sub_diagnostic(c, je))
.map(|c| Diagnostic::from_sub_diagnostic(c, &args, je))
.chain(sugg)
.collect(),
rendered: Some(output),
@ -400,16 +422,21 @@ impl Diagnostic {
}
}
fn from_sub_diagnostic(diag: &SubDiagnostic, je: &JsonEmitter) -> Diagnostic {
fn from_sub_diagnostic(
diag: &SubDiagnostic,
args: &FluentArgs<'_>,
je: &JsonEmitter,
) -> Diagnostic {
let translated_message = je.translate_messages(&diag.message, args);
Diagnostic {
message: diag.message().to_string(),
message: translated_message.to_string(),
code: None,
level: diag.level.to_str(),
spans: diag
.render_span
.as_ref()
.map(|sp| DiagnosticSpan::from_multispan(sp, je))
.unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, je)),
.map(|sp| DiagnosticSpan::from_multispan(sp, args, je))
.unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, args, je)),
children: vec![],
rendered: None,
tool_metadata: ToolMetadata::default(),
@ -421,12 +448,13 @@ impl DiagnosticSpan {
fn from_span_label(
span: SpanLabel,
suggestion: Option<(&String, Applicability)>,
args: &FluentArgs<'_>,
je: &JsonEmitter,
) -> DiagnosticSpan {
Self::from_span_etc(
span.span,
span.is_primary,
span.label.map(|m| m.to_string()),
span.label.as_ref().map(|m| je.translate_message(m, args)).map(|m| m.to_string()),
suggestion,
je,
)
@ -492,14 +520,22 @@ impl DiagnosticSpan {
}
}
fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
fn from_multispan(
msp: &MultiSpan,
args: &FluentArgs<'_>,
je: &JsonEmitter,
) -> Vec<DiagnosticSpan> {
msp.span_labels()
.into_iter()
.map(|span_str| Self::from_span_label(span_str, None, je))
.map(|span_str| Self::from_span_label(span_str, None, args, je))
.collect()
}
fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter) -> Vec<DiagnosticSpan> {
fn from_suggestion(
suggestion: &CodeSuggestion,
args: &FluentArgs<'_>,
je: &JsonEmitter,
) -> Vec<DiagnosticSpan> {
suggestion
.substitutions
.iter()
@ -510,6 +546,7 @@ impl DiagnosticSpan {
DiagnosticSpan::from_span_label(
span_label,
Some((&suggestion_inner.snippet, suggestion.applicability)),
args,
je,
)
})

View file

@ -39,12 +39,14 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
rustc_span::create_default_session_globals_then(|| {
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned());
let fallback_bundle = crate::fallback_fluent_bundle();
let output = Arc::new(Mutex::new(Vec::new()));
let je = JsonEmitter::new(
Box::new(Shared { data: output.clone() }),
None,
sm,
fallback_bundle,
true,
HumanReadableErrorType::Short(ColorConfig::Never),
None,

View file

@ -31,7 +31,9 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_data_structures::stable_hasher::StableHasher;
use rustc_data_structures::sync::{self, Lock, Lrc};
use rustc_data_structures::AtomicRef;
pub use rustc_error_messages::{DiagnosticMessage, MultiSpan, SpanLabel};
pub use rustc_error_messages::{
fallback_fluent_bundle, DiagnosticMessage, FluentBundle, MultiSpan, SpanLabel,
};
pub use rustc_lint_defs::{pluralize, Applicability};
use rustc_serialize::json::Json;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
@ -402,7 +404,9 @@ impl fmt::Display for ExplicitBug {
impl error::Error for ExplicitBug {}
pub use diagnostic::{Diagnostic, DiagnosticId, DiagnosticStyledString, SubDiagnostic};
pub use diagnostic::{
Diagnostic, DiagnosticArg, DiagnosticId, DiagnosticStyledString, SubDiagnostic,
};
pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee};
use std::backtrace::Backtrace;
@ -540,10 +544,12 @@ impl Handler {
can_emit_warnings: bool,
treat_err_as_bug: Option<NonZeroUsize>,
sm: Option<Lrc<SourceMap>>,
fallback_bundle: Lrc<FluentBundle>,
) -> Self {
Self::with_tty_emitter_and_flags(
color_config,
sm,
fallback_bundle,
HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() },
)
}
@ -551,11 +557,13 @@ impl Handler {
pub fn with_tty_emitter_and_flags(
color_config: ColorConfig,
sm: Option<Lrc<SourceMap>>,
fallback_bundle: Lrc<FluentBundle>,
flags: HandlerFlags,
) -> Self {
let emitter = Box::new(EmitterWriter::stderr(
color_config,
sm,
fallback_bundle,
false,
false,
None,
@ -660,7 +668,7 @@ impl Handler {
pub fn struct_span_warn(
&self,
span: impl Into<MultiSpan>,
msg: &str,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ()> {
let mut result = self.struct_warn(msg);
result.set_span(span);
@ -671,7 +679,7 @@ impl Handler {
pub fn struct_span_allow(
&self,
span: impl Into<MultiSpan>,
msg: &str,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ()> {
let mut result = self.struct_allow(msg);
result.set_span(span);
@ -683,7 +691,7 @@ impl Handler {
pub fn struct_span_warn_with_code(
&self,
span: impl Into<MultiSpan>,
msg: &str,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) -> DiagnosticBuilder<'_, ()> {
let mut result = self.struct_span_warn(span, msg);
@ -696,17 +704,21 @@ impl Handler {
/// Attempting to `.emit()` the builder will only emit if either:
/// * `can_emit_warnings` is `true`
/// * `is_force_warn` was set in `DiagnosticId::Lint`
pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
DiagnosticBuilder::new(self, Level::Warning, msg)
}
/// Construct a builder at the `Allow` level with the `msg`.
pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
DiagnosticBuilder::new(self, Level::Allow, msg)
}
/// Construct a builder at the `Expect` level with the `msg`.
pub fn struct_expect(&self, msg: &str, id: LintExpectationId) -> DiagnosticBuilder<'_, ()> {
pub fn struct_expect(
&self,
msg: impl Into<DiagnosticMessage>,
id: LintExpectationId,
) -> DiagnosticBuilder<'_, ()> {
DiagnosticBuilder::new(self, Level::Expect(id), msg)
}
@ -714,7 +726,7 @@ impl Handler {
pub fn struct_span_err(
&self,
span: impl Into<MultiSpan>,
msg: &str,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
let mut result = self.struct_err(msg);
result.set_span(span);
@ -725,7 +737,7 @@ impl Handler {
pub fn struct_span_err_with_code(
&self,
span: impl Into<MultiSpan>,
msg: &str,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
let mut result = self.struct_span_err(span, msg);
@ -735,20 +747,23 @@ impl Handler {
/// Construct a builder at the `Error` level with the `msg`.
// FIXME: This method should be removed (every error should have an associated error code).
pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
DiagnosticBuilder::new_guaranteeing_error::<{ Level::Error { lint: false } }>(self, msg)
pub fn struct_err(
&self,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>(self, msg)
}
/// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors.
#[doc(hidden)]
pub fn struct_err_lint(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
pub fn struct_err_lint(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
DiagnosticBuilder::new(self, Level::Error { lint: true }, msg)
}
/// Construct a builder at the `Error` level with the `msg` and the `code`.
pub fn struct_err_with_code(
&self,
msg: &str,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
let mut result = self.struct_err(msg);
@ -760,7 +775,7 @@ impl Handler {
pub fn struct_span_fatal(
&self,
span: impl Into<MultiSpan>,
msg: &str,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, !> {
let mut result = self.struct_fatal(msg);
result.set_span(span);
@ -771,7 +786,7 @@ impl Handler {
pub fn struct_span_fatal_with_code(
&self,
span: impl Into<MultiSpan>,
msg: &str,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) -> DiagnosticBuilder<'_, !> {
let mut result = self.struct_span_fatal(span, msg);
@ -780,21 +795,24 @@ impl Handler {
}
/// Construct a builder at the `Error` level with the `msg`.
pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_, !> {
pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
DiagnosticBuilder::new_fatal(self, msg)
}
/// Construct a builder at the `Help` level with the `msg`.
pub fn struct_help(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
pub fn struct_help(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
DiagnosticBuilder::new(self, Level::Help, msg)
}
/// Construct a builder at the `Note` level with the `msg`.
pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
pub fn struct_note_without_error(
&self,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ()> {
DiagnosticBuilder::new(self, Level::Note, msg)
}
pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: &str) -> ! {
pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! {
self.emit_diag_at_span(Diagnostic::new(Fatal, msg), span);
FatalError.raise()
}
@ -802,80 +820,106 @@ impl Handler {
pub fn span_fatal_with_code(
&self,
span: impl Into<MultiSpan>,
msg: &str,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) -> ! {
self.emit_diag_at_span(Diagnostic::new_with_code(Fatal, Some(code), msg), span);
FatalError.raise()
}
pub fn span_err(&self, span: impl Into<MultiSpan>, msg: &str) -> ErrorGuaranteed {
pub fn span_err(
&self,
span: impl Into<MultiSpan>,
msg: impl Into<DiagnosticMessage>,
) -> ErrorGuaranteed {
self.emit_diag_at_span(Diagnostic::new(Error { lint: false }, msg), span).unwrap()
}
pub fn span_err_with_code(&self, span: impl Into<MultiSpan>, msg: &str, code: DiagnosticId) {
pub fn span_err_with_code(
&self,
span: impl Into<MultiSpan>,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) {
self.emit_diag_at_span(
Diagnostic::new_with_code(Error { lint: false }, Some(code), msg),
span,
);
}
pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: &str) {
pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) {
self.emit_diag_at_span(Diagnostic::new(Warning, msg), span);
}
pub fn span_warn_with_code(&self, span: impl Into<MultiSpan>, msg: &str, code: DiagnosticId) {
pub fn span_warn_with_code(
&self,
span: impl Into<MultiSpan>,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) {
self.emit_diag_at_span(Diagnostic::new_with_code(Warning, Some(code), msg), span);
}
pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: &str) -> ! {
pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! {
self.inner.borrow_mut().span_bug(span, msg)
}
#[track_caller]
pub fn delay_span_bug(&self, span: impl Into<MultiSpan>, msg: &str) -> ErrorGuaranteed {
pub fn delay_span_bug(
&self,
span: impl Into<MultiSpan>,
msg: impl Into<DiagnosticMessage>,
) -> ErrorGuaranteed {
self.inner.borrow_mut().delay_span_bug(span, msg)
}
// FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's
// where the explanation of what "good path" is (also, it should be renamed).
pub fn delay_good_path_bug(&self, msg: &str) {
pub fn delay_good_path_bug(&self, msg: impl Into<DiagnosticMessage>) {
self.inner.borrow_mut().delay_good_path_bug(msg)
}
pub fn span_bug_no_panic(&self, span: impl Into<MultiSpan>, msg: &str) {
pub fn span_bug_no_panic(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) {
self.emit_diag_at_span(Diagnostic::new(Bug, msg), span);
}
pub fn span_note_without_error(&self, span: impl Into<MultiSpan>, msg: &str) {
pub fn span_note_without_error(
&self,
span: impl Into<MultiSpan>,
msg: impl Into<DiagnosticMessage>,
) {
self.emit_diag_at_span(Diagnostic::new(Note, msg), span);
}
pub fn span_note_diag(&self, span: Span, msg: &str) -> DiagnosticBuilder<'_, ()> {
pub fn span_note_diag(
&self,
span: Span,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ()> {
let mut db = DiagnosticBuilder::new(self, Note, msg);
db.set_span(span);
db
}
// NOTE: intentionally doesn't raise an error so rustc_codegen_ssa only reports fatal errors in the main thread
pub fn fatal(&self, msg: &str) -> FatalError {
pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> FatalError {
self.inner.borrow_mut().fatal(msg)
}
pub fn err(&self, msg: &str) -> ErrorGuaranteed {
pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
self.inner.borrow_mut().err(msg)
}
pub fn warn(&self, msg: &str) {
pub fn warn(&self, msg: impl Into<DiagnosticMessage>) {
let mut db = DiagnosticBuilder::new(self, Warning, msg);
db.emit();
}
pub fn note_without_error(&self, msg: &str) {
pub fn note_without_error(&self, msg: impl Into<DiagnosticMessage>) {
DiagnosticBuilder::new(self, Note, msg).emit();
}
pub fn bug(&self, msg: &str) -> ! {
pub fn bug(&self, msg: impl Into<DiagnosticMessage>) -> ! {
self.inner.borrow_mut().bug(msg)
}
@ -1145,7 +1189,10 @@ impl HandlerInner {
match (errors.len(), warnings.len()) {
(0, 0) => return,
(0, _) => self.emitter.emit_diagnostic(&Diagnostic::new(Level::Warning, &warnings)),
(0, _) => self.emitter.emit_diagnostic(&Diagnostic::new(
Level::Warning,
DiagnosticMessage::Str(warnings.to_owned()),
)),
(_, 0) => {
let _ = self.fatal(&errors);
}
@ -1220,7 +1267,7 @@ impl HandlerInner {
}
}
fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: &str) -> ! {
fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! {
self.emit_diag_at_span(Diagnostic::new(Bug, msg), sp);
panic::panic_any(ExplicitBug);
}
@ -1230,7 +1277,11 @@ impl HandlerInner {
}
#[track_caller]
fn delay_span_bug(&mut self, sp: impl Into<MultiSpan>, msg: &str) -> ErrorGuaranteed {
fn delay_span_bug(
&mut self,
sp: impl Into<MultiSpan>,
msg: impl Into<DiagnosticMessage>,
) -> ErrorGuaranteed {
// This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before
// incrementing `err_count` by one, so we need to +1 the comparing.
// FIXME: Would be nice to increment err_count in a more coherent way.
@ -1246,7 +1297,7 @@ impl HandlerInner {
// FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's
// where the explanation of what "good path" is (also, it should be renamed).
fn delay_good_path_bug(&mut self, msg: &str) {
fn delay_good_path_bug(&mut self, msg: impl Into<DiagnosticMessage>) {
let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg);
if self.flags.report_delayed_bugs {
self.emit_diagnostic(&mut diagnostic);
@ -1255,33 +1306,37 @@ impl HandlerInner {
self.delayed_good_path_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
}
fn failure(&mut self, msg: &str) {
fn failure(&mut self, msg: impl Into<DiagnosticMessage>) {
self.emit_diagnostic(&mut Diagnostic::new(FailureNote, msg));
}
fn fatal(&mut self, msg: &str) -> FatalError {
fn fatal(&mut self, msg: impl Into<DiagnosticMessage>) -> FatalError {
self.emit(Fatal, msg);
FatalError
}
fn err(&mut self, msg: &str) -> ErrorGuaranteed {
fn err(&mut self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
self.emit(Error { lint: false }, msg)
}
/// Emit an error; level should be `Error` or `Fatal`.
fn emit(&mut self, level: Level, msg: &str) -> ErrorGuaranteed {
fn emit(&mut self, level: Level, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
if self.treat_err_as_bug() {
self.bug(msg);
}
self.emit_diagnostic(&mut Diagnostic::new(level, msg)).unwrap()
}
fn bug(&mut self, msg: &str) -> ! {
fn bug(&mut self, msg: impl Into<DiagnosticMessage>) -> ! {
self.emit_diagnostic(&mut Diagnostic::new(Bug, msg));
panic::panic_any(ExplicitBug);
}
fn flush_delayed(&mut self, bugs: impl IntoIterator<Item = Diagnostic>, explanation: &str) {
fn flush_delayed(
&mut self,
bugs: impl IntoIterator<Item = Diagnostic>,
explanation: impl Into<DiagnosticMessage> + Copy,
) {
let mut no_bugs = true;
for mut bug in bugs {
if no_bugs {