1
Fork 0

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

@ -1238,6 +1238,50 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "fluent"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61f69378194459db76abd2ce3952b790db103ceb003008d3d50d97c41ff847a7"
dependencies = [
"fluent-bundle",
"unic-langid",
]
[[package]]
name = "fluent-bundle"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd"
dependencies = [
"fluent-langneg",
"fluent-syntax",
"intl-memoizer",
"intl_pluralrules",
"rustc-hash",
"self_cell",
"smallvec",
"unic-langid",
]
[[package]]
name = "fluent-langneg"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94"
dependencies = [
"unic-langid",
]
[[package]]
name = "fluent-syntax"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78"
dependencies = [
"thiserror",
]
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@ -1782,6 +1826,26 @@ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
] ]
[[package]]
name = "intl-memoizer"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f"
dependencies = [
"type-map",
"unic-langid",
]
[[package]]
name = "intl_pluralrules"
version = "7.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b18f988384267d7066cc2be425e6faf352900652c046b6971d2e228d3b1c5ecf"
dependencies = [
"tinystr",
"unic-langid",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.10.1" version = "0.10.1"
@ -2812,6 +2876,12 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.30" version = "1.0.30"
@ -3649,9 +3719,13 @@ version = "0.0.0"
name = "rustc_error_messages" name = "rustc_error_messages"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"fluent",
"rustc_data_structures",
"rustc_macros", "rustc_macros",
"rustc_serialize", "rustc_serialize",
"rustc_span", "rustc_span",
"tracing",
"unic-langid",
] ]
[[package]] [[package]]
@ -4585,6 +4659,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "self_cell"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af"
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.3" version = "1.0.3"
@ -5116,6 +5196,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "tinystr"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29738eedb4388d9ea620eeab9384884fc3f06f586a2eddb56bedc5885126c7c1"
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "0.3.4" version = "0.3.4"
@ -5274,6 +5360,15 @@ dependencies = [
"tracing-subscriber", "tracing-subscriber",
] ]
[[package]]
name = "type-map"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46"
dependencies = [
"rustc-hash",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.12.0" version = "1.12.0"
@ -5328,6 +5423,49 @@ dependencies = [
"unic-ucd-version", "unic-ucd-version",
] ]
[[package]]
name = "unic-langid"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73328fcd730a030bdb19ddf23e192187a6b01cd98be6d3140622a89129459ce5"
dependencies = [
"unic-langid-impl",
"unic-langid-macros",
]
[[package]]
name = "unic-langid-impl"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a4a8eeaf0494862c1404c95ec2f4c33a2acff5076f64314b465e3ddae1b934d"
dependencies = [
"tinystr",
]
[[package]]
name = "unic-langid-macros"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18f980d6d87e8805f2836d64b4138cc95aa7986fa63b1f51f67d5fbff64dd6e5"
dependencies = [
"proc-macro-hack",
"tinystr",
"unic-langid-impl",
"unic-langid-macros-impl",
]
[[package]]
name = "unic-langid-macros-impl"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29396ffd97e27574c3e01368b1a64267d3064969e4848e2e130ff668be9daa9f"
dependencies = [
"proc-macro-hack",
"quote",
"syn",
"unic-langid-impl",
]
[[package]] [[package]]
name = "unic-ucd-version" name = "unic-ucd-version"
version = "0.9.0" version = "0.9.0"

View file

@ -109,7 +109,7 @@ impl RegionName {
*span, *span,
format!("lifetime `{}` represents this closure's body", self), format!("lifetime `{}` represents this closure's body", self),
); );
diag.note(&note); diag.note(note);
} }
RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::CannotMatchHirTy( RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::CannotMatchHirTy(
span, span,

View file

@ -1707,23 +1707,33 @@ impl SharedEmitter {
impl Emitter for SharedEmitter { impl Emitter for SharedEmitter {
fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) { fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) {
let fluent_args = self.to_fluent_args(diag.args());
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
msg: diag.message().to_string(), msg: self.translate_messages(&diag.message, &fluent_args).to_string(),
code: diag.code.clone(), code: diag.code.clone(),
lvl: diag.level(), lvl: diag.level(),
}))); })));
for child in &diag.children { for child in &diag.children {
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
msg: child.message().to_string(), msg: self.translate_messages(&child.message, &fluent_args).to_string(),
code: None, code: None,
lvl: child.level, lvl: child.level,
}))); })));
} }
drop(self.sender.send(SharedEmitterMessage::AbortIfErrors)); drop(self.sender.send(SharedEmitterMessage::AbortIfErrors));
} }
fn source_map(&self) -> Option<&Lrc<SourceMap>> { fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None None
} }
fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
None
}
fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> {
panic!("shared emitter attempted to translate a diagnostic");
}
} }
impl SharedEmitterMain { impl SharedEmitterMain {
@ -1754,9 +1764,9 @@ impl SharedEmitterMain {
let msg = msg.strip_prefix("error: ").unwrap_or(&msg); let msg = msg.strip_prefix("error: ").unwrap_or(&msg);
let mut err = match level { let mut err = match level {
Level::Error { lint: false } => sess.struct_err(&msg).forget_guarantee(), Level::Error { lint: false } => sess.struct_err(msg).forget_guarantee(),
Level::Warning => sess.struct_warn(&msg), Level::Warning => sess.struct_warn(msg),
Level::Note => sess.struct_note_without_error(&msg), Level::Note => sess.struct_note_without_error(msg),
_ => bug!("Invalid inline asm diagnostic level"), _ => bug!("Invalid inline asm diagnostic level"),
}; };

View file

@ -1172,9 +1172,11 @@ static DEFAULT_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send +
/// When `install_ice_hook` is called, this function will be called as the panic /// When `install_ice_hook` is called, this function will be called as the panic
/// hook. /// hook.
pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
let fallback_bundle = rustc_errors::fallback_fluent_bundle();
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
rustc_errors::ColorConfig::Auto, rustc_errors::ColorConfig::Auto,
None, None,
fallback_bundle,
false, false,
false, false,
None, None,
@ -1209,7 +1211,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
} }
for note in &xs { for note in &xs {
handler.note_without_error(note); handler.note_without_error(note.as_ref());
} }
// If backtraces are enabled, also print the query stack // If backtraces are enabled, also print the query stack

View file

@ -7,6 +7,10 @@ edition = "2021"
doctest = false doctest = false
[dependencies] [dependencies]
fluent = "0.16.0"
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_serialize = { path = "../rustc_serialize" } rustc_serialize = { path = "../rustc_serialize" }
rustc_span = { path = "../rustc_span" } rustc_span = { path = "../rustc_span" }
rustc_macros = { path = "../rustc_macros" } rustc_macros = { path = "../rustc_macros" }
tracing = "0.1"
unic-langid = { version = "0.9.0", features = ["macros"] }

View file

@ -0,0 +1,2 @@
parser-struct-literal-body-without-path = struct literal body without path
.suggestion = you might have forgotten to add the struct literal inside the block

View file

@ -1,31 +1,75 @@
use rustc_data_structures::sync::Lrc;
use rustc_macros::{Decodable, Encodable}; use rustc_macros::{Decodable, Encodable};
use rustc_span::Span; use rustc_span::Span;
use std::borrow::Cow;
use tracing::debug;
pub use fluent::{FluentArgs, FluentValue};
static FALLBACK_FLUENT_RESOURCE: &'static str = include_str!("../locales/en-US/diagnostics.ftl");
pub type FluentBundle = fluent::FluentBundle<fluent::FluentResource>;
/// Return the default `FluentBundle` with standard en-US diagnostic messages.
pub fn fallback_fluent_bundle() -> Lrc<FluentBundle> {
let fallback_resource = fluent::FluentResource::try_new(FALLBACK_FLUENT_RESOURCE.to_string())
.expect("failed to parse ftl resource");
debug!(?fallback_resource);
let mut fallback_bundle = FluentBundle::new(vec![unic_langid::langid!("en-US")]);
fallback_bundle.add_resource(fallback_resource).expect("failed to add resource to bundle");
let fallback_bundle = Lrc::new(fallback_bundle);
fallback_bundle
}
/// Identifier for the Fluent message/attribute corresponding to a diagnostic message.
type FluentId = Cow<'static, str>;
/// Abstraction over a message in a diagnostic to support both translatable and non-translatable /// Abstraction over a message in a diagnostic to support both translatable and non-translatable
/// diagnostic messages. /// diagnostic messages.
///
/// Intended to be removed once diagnostics are entirely translatable.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
pub enum DiagnosticMessage { pub enum DiagnosticMessage {
/// Non-translatable diagnostic message. /// Non-translatable diagnostic message.
// FIXME(davidtwco): can a `Cow<'static, str>` be used here?
Str(String), Str(String),
/// Identifier for a Fluent message corresponding to the diagnostic message. /// Identifier for a Fluent message corresponding to the diagnostic message.
FluentIdentifier(String), FluentIdentifier(FluentId, Option<FluentId>),
} }
impl DiagnosticMessage { impl DiagnosticMessage {
/// Convert `DiagnosticMessage` to a `&str`. /// Returns the `String` contained within the `DiagnosticMessage::Str` variant, assuming that
pub fn as_str(&self) -> &str { /// this diagnostic message is of the legacy, non-translatable variety. Panics if this
/// assumption does not hold.
///
/// Don't use this - it exists to support some places that do comparison with diagnostic
/// strings.
pub fn expect_str(&self) -> &str {
match self { match self {
DiagnosticMessage::Str(msg) => msg, DiagnosticMessage::Str(s) => s,
DiagnosticMessage::FluentIdentifier(..) => unimplemented!(), _ => panic!("expected non-translatable diagnostic message"),
} }
} }
/// Convert `DiagnosticMessage` to an owned `String`. /// Create a `DiagnosticMessage` for the provided Fluent identifier.
pub fn to_string(self) -> String { pub fn fluent(id: impl Into<Cow<'static, str>>) -> Self {
match self { DiagnosticMessage::FluentIdentifier(id.into(), None)
DiagnosticMessage::Str(msg) => msg,
DiagnosticMessage::FluentIdentifier(..) => unimplemented!(),
} }
/// Create a `DiagnosticMessage` for the provided Fluent identifier and attribute.
pub fn fluent_attr(
id: impl Into<Cow<'static, str>>,
attr: impl Into<Cow<'static, str>>,
) -> Self {
DiagnosticMessage::FluentIdentifier(id.into(), Some(attr.into()))
}
}
/// `From` impl that enables existing diagnostic calls to functions which now take
/// `impl Into<DiagnosticMessage>` to continue to work as before.
impl<S: Into<String>> From<S> for DiagnosticMessage {
fn from(s: S) -> Self {
DiagnosticMessage::Str(s.into())
} }
} }
@ -72,12 +116,8 @@ impl MultiSpan {
MultiSpan { primary_spans: vec, span_labels: vec![] } MultiSpan { primary_spans: vec, span_labels: vec![] }
} }
pub fn push_span_label(&mut self, span: Span, label: String) { pub fn push_span_label(&mut self, span: Span, label: impl Into<DiagnosticMessage>) {
self.span_labels.push((span, DiagnosticMessage::Str(label))); self.span_labels.push((span, label.into()));
}
pub fn push_span_message(&mut self, span: Span, message: DiagnosticMessage) {
self.span_labels.push((span, message));
} }
/// Selects the first primary span (if any). /// Selects the first primary span (if any).

View file

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

View file

@ -4,10 +4,12 @@ use crate::{
SuggestionStyle, ToolMetadata, SuggestionStyle, ToolMetadata,
}; };
use rustc_data_structures::stable_map::FxHashMap; use rustc_data_structures::stable_map::FxHashMap;
use rustc_error_messages::FluentValue;
use rustc_lint_defs::{Applicability, LintExpectationId}; use rustc_lint_defs::{Applicability, LintExpectationId};
use rustc_serialize::json::Json; use rustc_serialize::json::Json;
use rustc_span::edition::LATEST_STABLE_EDITION; use rustc_span::edition::LATEST_STABLE_EDITION;
use rustc_span::{Span, DUMMY_SP}; use rustc_span::{Span, DUMMY_SP};
use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
@ -16,6 +18,28 @@ use std::hash::{Hash, Hasher};
#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
pub struct SuggestionsDisabled; 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] #[must_use]
#[derive(Clone, Debug, Encodable, Decodable)] #[derive(Clone, Debug, Encodable, Decodable)]
pub struct Diagnostic { pub struct Diagnostic {
@ -28,6 +52,7 @@ pub struct Diagnostic {
pub span: MultiSpan, pub span: MultiSpan,
pub children: Vec<SubDiagnostic>, pub children: Vec<SubDiagnostic>,
pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>, 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 /// 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 /// 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 { 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) 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 { Diagnostic {
level, level,
message: vec![(DiagnosticMessage::Str(message.to_owned()), Style::NoStyle)], message: vec![(message.into(), Style::NoStyle)],
code, code,
span: MultiSpan::new(), span: MultiSpan::new(),
children: vec![], children: vec![],
suggestions: Ok(vec![]), suggestions: Ok(vec![]),
args: vec![],
sort_span: DUMMY_SP, sort_span: DUMMY_SP,
is_lint: false, is_lint: false,
} }
@ -232,7 +262,7 @@ impl Diagnostic {
self.set_span(after); self.set_span(after);
for span_label in before.span_labels() { for span_label in before.span_labels() {
if let Some(label) = span_label.label { if let Some(label) = span_label.label {
self.span.push_span_message(after, label); self.span.push_span_label(after, label);
} }
} }
self self
@ -326,52 +356,67 @@ impl Diagnostic {
} }
/// Add a note attached to this 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.sub(Level::Note, msg, MultiSpan::new(), None);
self 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.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
self self
} }
/// Prints the span with a note above it. /// Prints the span with a note above it.
/// This is like [`Diagnostic::note()`], but it gets its own span. /// 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.sub(Level::OnceNote, msg, MultiSpan::new(), None);
self self
} }
/// Prints the span with a note above it. /// Prints the span with a note above it.
/// This is like [`Diagnostic::note()`], but it gets its own span. /// 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.sub(Level::Note, msg, sp.into(), None);
self self
} }
/// Prints the span with a note above it. /// Prints the span with a note above it.
/// This is like [`Diagnostic::note()`], but it gets its own span. /// 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.sub(Level::OnceNote, msg, sp.into(), None);
self self
} }
/// Add a warning attached to this diagnostic. /// 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.sub(Level::Warning, msg, MultiSpan::new(), None);
self self
} }
/// Prints the span with a warning above it. /// Prints the span with a warning above it.
/// This is like [`Diagnostic::warn()`], but it gets its own span. /// 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.sub(Level::Warning, msg, sp.into(), None);
self self
} }
/// Add a help message attached to this diagnostic. /// 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.sub(Level::Help, msg, MultiSpan::new(), None);
self self
} }
@ -384,7 +429,11 @@ impl Diagnostic {
/// Prints the span with some help above it. /// Prints the span with some help above it.
/// This is like [`Diagnostic::help()`], but it gets its own span. /// 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.sub(Level::Help, msg, sp.into(), None);
self self
} }
@ -420,7 +469,7 @@ impl Diagnostic {
/// In other words, multiple changes need to be applied as part of this suggestion. /// In other words, multiple changes need to be applied as part of this suggestion.
pub fn multipart_suggestion( pub fn multipart_suggestion(
&mut self, &mut self,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: Vec<(Span, String)>, suggestion: Vec<(Span, String)>,
applicability: Applicability, applicability: Applicability,
) -> &mut Self { ) -> &mut Self {
@ -436,7 +485,7 @@ impl Diagnostic {
/// In other words, multiple changes need to be applied as part of this suggestion. /// In other words, multiple changes need to be applied as part of this suggestion.
pub fn multipart_suggestion_verbose( pub fn multipart_suggestion_verbose(
&mut self, &mut self,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: Vec<(Span, String)>, suggestion: Vec<(Span, String)>,
applicability: Applicability, applicability: Applicability,
) -> &mut Self { ) -> &mut Self {
@ -450,7 +499,7 @@ impl Diagnostic {
/// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`].
pub fn multipart_suggestion_with_style( pub fn multipart_suggestion_with_style(
&mut self, &mut self,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: Vec<(Span, String)>, suggestion: Vec<(Span, String)>,
applicability: Applicability, applicability: Applicability,
style: SuggestionStyle, style: SuggestionStyle,
@ -463,7 +512,7 @@ impl Diagnostic {
.map(|(span, snippet)| SubstitutionPart { snippet, span }) .map(|(span, snippet)| SubstitutionPart { snippet, span })
.collect(), .collect(),
}], }],
msg: DiagnosticMessage::Str(msg.to_owned()), msg: msg.into(),
style, style,
applicability, applicability,
tool_metadata: Default::default(), tool_metadata: Default::default(),
@ -479,7 +528,7 @@ impl Diagnostic {
/// improve understandability. /// improve understandability.
pub fn tool_only_multipart_suggestion( pub fn tool_only_multipart_suggestion(
&mut self, &mut self,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: Vec<(Span, String)>, suggestion: Vec<(Span, String)>,
applicability: Applicability, applicability: Applicability,
) -> &mut Self { ) -> &mut Self {
@ -491,7 +540,7 @@ impl Diagnostic {
.map(|(span, snippet)| SubstitutionPart { snippet, span }) .map(|(span, snippet)| SubstitutionPart { snippet, span })
.collect(), .collect(),
}], }],
msg: DiagnosticMessage::Str(msg.to_owned()), msg: msg.into(),
style: SuggestionStyle::CompletelyHidden, style: SuggestionStyle::CompletelyHidden,
applicability, applicability,
tool_metadata: Default::default(), tool_metadata: Default::default(),
@ -519,7 +568,7 @@ impl Diagnostic {
pub fn span_suggestion( pub fn span_suggestion(
&mut self, &mut self,
sp: Span, sp: Span,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: String, suggestion: String,
applicability: Applicability, applicability: Applicability,
) -> &mut Self { ) -> &mut Self {
@ -537,7 +586,7 @@ impl Diagnostic {
pub fn span_suggestion_with_style( pub fn span_suggestion_with_style(
&mut self, &mut self,
sp: Span, sp: Span,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: String, suggestion: String,
applicability: Applicability, applicability: Applicability,
style: SuggestionStyle, style: SuggestionStyle,
@ -546,7 +595,7 @@ impl Diagnostic {
substitutions: vec![Substitution { substitutions: vec![Substitution {
parts: vec![SubstitutionPart { snippet: suggestion, span: sp }], parts: vec![SubstitutionPart { snippet: suggestion, span: sp }],
}], }],
msg: DiagnosticMessage::Str(msg.to_owned()), msg: msg.into(),
style, style,
applicability, applicability,
tool_metadata: Default::default(), tool_metadata: Default::default(),
@ -558,7 +607,7 @@ impl Diagnostic {
pub fn span_suggestion_verbose( pub fn span_suggestion_verbose(
&mut self, &mut self,
sp: Span, sp: Span,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: String, suggestion: String,
applicability: Applicability, applicability: Applicability,
) -> &mut Self { ) -> &mut Self {
@ -577,7 +626,7 @@ impl Diagnostic {
pub fn span_suggestions( pub fn span_suggestions(
&mut self, &mut self,
sp: Span, sp: Span,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestions: impl Iterator<Item = String>, suggestions: impl Iterator<Item = String>,
applicability: Applicability, applicability: Applicability,
) -> &mut Self { ) -> &mut Self {
@ -589,7 +638,7 @@ impl Diagnostic {
.collect(); .collect();
self.push_suggestion(CodeSuggestion { self.push_suggestion(CodeSuggestion {
substitutions, substitutions,
msg: DiagnosticMessage::Str(msg.to_owned()), msg: msg.into(),
style: SuggestionStyle::ShowCode, style: SuggestionStyle::ShowCode,
applicability, applicability,
tool_metadata: Default::default(), tool_metadata: Default::default(),
@ -601,7 +650,7 @@ impl Diagnostic {
/// See also [`Diagnostic::span_suggestion()`]. /// See also [`Diagnostic::span_suggestion()`].
pub fn multipart_suggestions( pub fn multipart_suggestions(
&mut self, &mut self,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestions: impl Iterator<Item = Vec<(Span, String)>>, suggestions: impl Iterator<Item = Vec<(Span, String)>>,
applicability: Applicability, applicability: Applicability,
) -> &mut Self { ) -> &mut Self {
@ -614,7 +663,7 @@ impl Diagnostic {
.collect(), .collect(),
}) })
.collect(), .collect(),
msg: DiagnosticMessage::Str(msg.to_owned()), msg: msg.into(),
style: SuggestionStyle::ShowCode, style: SuggestionStyle::ShowCode,
applicability, applicability,
tool_metadata: Default::default(), tool_metadata: Default::default(),
@ -628,7 +677,7 @@ impl Diagnostic {
pub fn span_suggestion_short( pub fn span_suggestion_short(
&mut self, &mut self,
sp: Span, sp: Span,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: String, suggestion: String,
applicability: Applicability, applicability: Applicability,
) -> &mut Self { ) -> &mut Self {
@ -651,7 +700,7 @@ impl Diagnostic {
pub fn span_suggestion_hidden( pub fn span_suggestion_hidden(
&mut self, &mut self,
sp: Span, sp: Span,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: String, suggestion: String,
applicability: Applicability, applicability: Applicability,
) -> &mut Self { ) -> &mut Self {
@ -672,7 +721,7 @@ impl Diagnostic {
pub fn tool_only_span_suggestion( pub fn tool_only_span_suggestion(
&mut self, &mut self,
sp: Span, sp: Span,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: String, suggestion: String,
applicability: Applicability, applicability: Applicability,
) -> &mut Self { ) -> &mut Self {
@ -690,13 +739,13 @@ impl Diagnostic {
/// the suggestion in a tool-specific way, as it may not even directly involve Rust code. /// the suggestion in a tool-specific way, as it may not even directly involve Rust code.
pub fn tool_only_suggestion_with_metadata( pub fn tool_only_suggestion_with_metadata(
&mut self, &mut self,
msg: &str, msg: impl Into<DiagnosticMessage>,
applicability: Applicability, applicability: Applicability,
tool_metadata: Json, tool_metadata: Json,
) { ) {
self.push_suggestion(CodeSuggestion { self.push_suggestion(CodeSuggestion {
substitutions: vec![], substitutions: vec![],
msg: DiagnosticMessage::Str(msg.to_owned()), msg: msg.into(),
style: SuggestionStyle::CompletelyHidden, style: SuggestionStyle::CompletelyHidden,
applicability, applicability,
tool_metadata: ToolMetadata::new(tool_metadata), tool_metadata: ToolMetadata::new(tool_metadata),
@ -730,13 +779,13 @@ impl Diagnostic {
self.code.clone() self.code.clone()
} }
pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self { pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
self.message[0] = (DiagnosticMessage::Str(msg.into()), Style::NoStyle); self.message[0] = (msg.into(), Style::NoStyle);
self self
} }
pub fn message(&self) -> DiagnosticMessage { pub fn args(&self) -> &[DiagnosticArg<'static>] {
DiagnosticMessage::Str(self.message.iter().map(|i| i.0.as_str()).collect::<String>()) &self.args
} }
pub fn styled_message(&self) -> &Vec<(DiagnosticMessage, Style)> { pub fn styled_message(&self) -> &Vec<(DiagnosticMessage, Style)> {
@ -750,13 +799,13 @@ impl Diagnostic {
pub fn sub( pub fn sub(
&mut self, &mut self,
level: Level, level: Level,
message: &str, message: impl Into<DiagnosticMessage>,
span: MultiSpan, span: MultiSpan,
render_span: Option<MultiSpan>, render_span: Option<MultiSpan>,
) { ) {
let sub = SubDiagnostic { let sub = SubDiagnostic {
level, level,
message: vec![(DiagnosticMessage::Str(message.to_owned()), Style::NoStyle)], message: vec![(message.into(), Style::NoStyle)],
span, span,
render_span, render_span,
}; };
@ -765,14 +814,14 @@ impl Diagnostic {
/// Convenience function for internal use, clients should use one of the /// Convenience function for internal use, clients should use one of the
/// public methods above. /// public methods above.
fn sub_with_highlights( fn sub_with_highlights<M: Into<DiagnosticMessage>>(
&mut self, &mut self,
level: Level, level: Level,
mut message: Vec<(String, Style)>, mut message: Vec<(M, Style)>,
span: MultiSpan, span: MultiSpan,
render_span: Option<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 }; let sub = SubDiagnostic { level, message, span, render_span };
self.children.push(sub); self.children.push(sub);
} }
@ -813,13 +862,3 @@ impl PartialEq for Diagnostic {
self.keys() == other.keys() 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 crate::{Handler, Level, MultiSpan, StashKey};
use rustc_lint_defs::Applicability; use rustc_lint_defs::Applicability;
@ -99,7 +99,10 @@ mod sealed_level_is_error {
impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> { impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
/// Convenience function for internal use, clients should use one of the /// Convenience function for internal use, clients should use one of the
/// `struct_*` methods on [`Handler`]. /// `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 where
(): sealed_level_is_error::IsError<L>, (): sealed_level_is_error::IsError<L>,
{ {
@ -163,7 +166,11 @@ impl EmissionGuarantee for ErrorGuaranteed {
impl<'a> DiagnosticBuilder<'a, ()> { impl<'a> DiagnosticBuilder<'a, ()> {
/// Convenience function for internal use, clients should use one of the /// Convenience function for internal use, clients should use one of the
/// `struct_*` methods on [`Handler`]. /// `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); let diagnostic = Diagnostic::new_with_code(level, None, message);
Self::new_diagnostic(handler, diagnostic) Self::new_diagnostic(handler, diagnostic)
} }
@ -201,7 +208,7 @@ impl EmissionGuarantee for () {
impl<'a> DiagnosticBuilder<'a, !> { impl<'a> DiagnosticBuilder<'a, !> {
/// Convenience function for internal use, clients should use one of the /// Convenience function for internal use, clients should use one of the
/// `struct_*` methods on [`Handler`]. /// `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); let diagnostic = Diagnostic::new_with_code(Level::Fatal, None, message);
Self::new_diagnostic_fatal(handler, diagnostic) 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. // 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); let diagnostic = std::mem::replace(&mut *self.inner.diagnostic, dummy);
// Disable the ICE on `Drop`. // Disable the ICE on `Drop`.
@ -434,25 +441,25 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
found: DiagnosticStyledString, found: DiagnosticStyledString,
) -> &mut Self); ) -> &mut Self);
forward!(pub fn note(&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: &str) -> &mut Self); forward!(pub fn note_once(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self);
forward!(pub fn span_note( forward!(pub fn span_note(
&mut self, &mut self,
sp: impl Into<MultiSpan>, sp: impl Into<MultiSpan>,
msg: &str, msg: impl Into<DiagnosticMessage>,
) -> &mut Self); ) -> &mut Self);
forward!(pub fn span_note_once( forward!(pub fn span_note_once(
&mut self, &mut self,
sp: impl Into<MultiSpan>, sp: impl Into<MultiSpan>,
msg: &str, msg: impl Into<DiagnosticMessage>,
) -> &mut Self); ) -> &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 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( forward!(pub fn span_help(
&mut self, &mut self,
sp: impl Into<MultiSpan>, sp: impl Into<MultiSpan>,
msg: &str, msg: impl Into<DiagnosticMessage>,
) -> &mut Self); ) -> &mut Self);
forward!(pub fn help_use_latest_edition(&mut self,) -> &mut Self); forward!(pub fn help_use_latest_edition(&mut self,) -> &mut Self);
forward!(pub fn set_is_lint(&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( forward!(pub fn multipart_suggestion(
&mut self, &mut self,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: Vec<(Span, String)>, suggestion: Vec<(Span, String)>,
applicability: Applicability, applicability: Applicability,
) -> &mut Self); ) -> &mut Self);
forward!(pub fn multipart_suggestion_verbose( forward!(pub fn multipart_suggestion_verbose(
&mut self, &mut self,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: Vec<(Span, String)>, suggestion: Vec<(Span, String)>,
applicability: Applicability, applicability: Applicability,
) -> &mut Self); ) -> &mut Self);
forward!(pub fn tool_only_multipart_suggestion( forward!(pub fn tool_only_multipart_suggestion(
&mut self, &mut self,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: Vec<(Span, String)>, suggestion: Vec<(Span, String)>,
applicability: Applicability, applicability: Applicability,
) -> &mut Self); ) -> &mut Self);
forward!(pub fn span_suggestion( forward!(pub fn span_suggestion(
&mut self, &mut self,
sp: Span, sp: Span,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: String, suggestion: String,
applicability: Applicability, applicability: Applicability,
) -> &mut Self); ) -> &mut Self);
forward!(pub fn span_suggestions( forward!(pub fn span_suggestions(
&mut self, &mut self,
sp: Span, sp: Span,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestions: impl Iterator<Item = String>, suggestions: impl Iterator<Item = String>,
applicability: Applicability, applicability: Applicability,
) -> &mut Self); ) -> &mut Self);
forward!(pub fn multipart_suggestions( forward!(pub fn multipart_suggestions(
&mut self, &mut self,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestions: impl Iterator<Item = Vec<(Span, String)>>, suggestions: impl Iterator<Item = Vec<(Span, String)>>,
applicability: Applicability, applicability: Applicability,
) -> &mut Self); ) -> &mut Self);
forward!(pub fn span_suggestion_short( forward!(pub fn span_suggestion_short(
&mut self, &mut self,
sp: Span, sp: Span,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: String, suggestion: String,
applicability: Applicability, applicability: Applicability,
) -> &mut Self); ) -> &mut Self);
forward!(pub fn span_suggestion_verbose( forward!(pub fn span_suggestion_verbose(
&mut self, &mut self,
sp: Span, sp: Span,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: String, suggestion: String,
applicability: Applicability, applicability: Applicability,
) -> &mut Self); ) -> &mut Self);
forward!(pub fn span_suggestion_hidden( forward!(pub fn span_suggestion_hidden(
&mut self, &mut self,
sp: Span, sp: Span,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: String, suggestion: String,
applicability: Applicability, applicability: Applicability,
) -> &mut Self); ) -> &mut Self);
forward!(pub fn tool_only_span_suggestion( forward!(pub fn tool_only_span_suggestion(
&mut self, &mut self,
sp: Span, sp: Span,
msg: &str, msg: impl Into<DiagnosticMessage>,
suggestion: String, suggestion: String,
applicability: Applicability, applicability: Applicability,
) -> &mut Self); ) -> &mut Self);
@ -547,7 +554,9 @@ impl Drop for DiagnosticBuilderInner<'_> {
if !panicking() { if !panicking() {
handler.emit_diagnostic(&mut Diagnostic::new( handler.emit_diagnostic(&mut Diagnostic::new(
Level::Bug, 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); handler.emit_diagnostic(&mut self.diagnostic);
panic!(); panic!();

View file

@ -15,14 +15,15 @@ use rustc_span::{SourceFile, Span};
use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString}; use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString};
use crate::styled_buffer::StyledBuffer; use crate::styled_buffer::StyledBuffer;
use crate::{ use crate::{
CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Handler, Level, MultiSpan, CodeSuggestion, Diagnostic, DiagnosticArg, DiagnosticId, DiagnosticMessage, FluentBundle,
SubDiagnostic, SubstitutionHighlight, SuggestionStyle, Handler, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, SuggestionStyle,
}; };
use rustc_lint_defs::pluralize; use rustc_lint_defs::pluralize;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
use rustc_error_messages::FluentArgs;
use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::hygiene::{ExpnKind, MacroKind};
use std::borrow::Cow; use std::borrow::Cow;
use std::cmp::{max, min, Reverse}; use std::cmp::{max, min, Reverse};
@ -58,13 +59,23 @@ impl HumanReadableErrorType {
self, self,
dst: Box<dyn Write + Send>, dst: Box<dyn Write + Send>,
source_map: Option<Lrc<SourceMap>>, source_map: Option<Lrc<SourceMap>>,
fallback_bundle: Lrc<FluentBundle>,
teach: bool, teach: bool,
terminal_width: Option<usize>, terminal_width: Option<usize>,
macro_backtrace: bool, macro_backtrace: bool,
) -> EmitterWriter { ) -> EmitterWriter {
let (short, color_config) = self.unzip(); let (short, color_config) = self.unzip();
let color = color_config.suggests_using_colors(); 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>>; 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 /// Formats the substitutions of the primary_span
/// ///
/// There are a lot of conditions to this method, but in short: /// There are a lot of conditions to this method, but in short:
@ -225,10 +298,12 @@ pub trait Emitter {
fn primary_span_formatted<'a>( fn primary_span_formatted<'a>(
&mut self, &mut self,
diag: &'a Diagnostic, diag: &'a Diagnostic,
fluent_args: &FluentArgs<'_>,
) -> (MultiSpan, &'a [CodeSuggestion]) { ) -> (MultiSpan, &'a [CodeSuggestion]) {
let mut primary_span = diag.span.clone(); let mut primary_span = diag.span.clone();
let suggestions = diag.suggestions.as_ref().map_or(&[][..], |suggestions| &suggestions[..]); let suggestions = diag.suggestions.as_ref().map_or(&[][..], |suggestions| &suggestions[..]);
if let Some((sugg, rest)) = suggestions.split_first() { if let Some((sugg, rest)) = suggestions.split_first() {
let msg = self.translate_message(&sugg.msg, fluent_args);
if rest.is_empty() && if rest.is_empty() &&
// ^ if there is only one suggestion // ^ if there is only one suggestion
// don't display multi-suggestions as labels // don't display multi-suggestions as labels
@ -236,7 +311,7 @@ pub trait Emitter {
// don't display multipart suggestions as labels // don't display multipart suggestions as labels
sugg.substitutions[0].parts.len() == 1 && sugg.substitutions[0].parts.len() == 1 &&
// don't display long messages as labels // 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 // don't display multiline suggestions as labels
!sugg.substitutions[0].parts[0].snippet.contains('\n') && !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() { let msg = if substitution.is_empty() || sugg.style.hide_inline() {
// This substitution is only removal OR we explicitly don't want to show the // 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. // code inline (`hide_inline`). Therefore, we don't show the substitution.
format!("help: {}", sugg.msg.as_str()) format!("help: {}", &msg)
} else { } else {
// Show the default suggestion text with the substitution // Show the default suggestion text with the substitution
format!( format!(
"help: {}{}: `{}`", "help: {}{}: `{}`",
sugg.msg.as_str(), &msg,
if self if self
.source_map() .source_map()
.map(|sm| is_case_difference( .map(|sm| is_case_difference(
@ -492,9 +567,19 @@ impl Emitter for EmitterWriter {
self.sm.as_ref() 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) { fn emit_diagnostic(&mut self, diag: &Diagnostic) {
let fluent_args = self.to_fluent_args(diag.args());
let mut children = diag.children.clone(); 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); debug!("emit_diagnostic: suggestions={:?}", suggestions);
self.fix_multispans_in_extern_macros_and_render_macro_backtrace( self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
@ -507,7 +592,8 @@ impl Emitter for EmitterWriter {
self.emit_messages_default( self.emit_messages_default(
&diag.level, &diag.level,
&diag.styled_message(), &diag.message,
&fluent_args,
&diag.code, &diag.code,
&primary_span, &primary_span,
&children, &children,
@ -536,6 +622,15 @@ impl Emitter for SilentEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> { fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None 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) { fn emit_diagnostic(&mut self, d: &Diagnostic) {
if d.level == Level::Fatal { if d.level == Level::Fatal {
let mut d = d.clone(); let mut d = d.clone();
@ -591,6 +686,7 @@ impl ColorConfig {
pub struct EmitterWriter { pub struct EmitterWriter {
dst: Destination, dst: Destination,
sm: Option<Lrc<SourceMap>>, sm: Option<Lrc<SourceMap>>,
fallback_bundle: Lrc<FluentBundle>,
short_message: bool, short_message: bool,
teach: bool, teach: bool,
ui_testing: bool, ui_testing: bool,
@ -610,6 +706,7 @@ impl EmitterWriter {
pub fn stderr( pub fn stderr(
color_config: ColorConfig, color_config: ColorConfig,
source_map: Option<Lrc<SourceMap>>, source_map: Option<Lrc<SourceMap>>,
fallback_bundle: Lrc<FluentBundle>,
short_message: bool, short_message: bool,
teach: bool, teach: bool,
terminal_width: Option<usize>, terminal_width: Option<usize>,
@ -619,6 +716,7 @@ impl EmitterWriter {
EmitterWriter { EmitterWriter {
dst, dst,
sm: source_map, sm: source_map,
fallback_bundle,
short_message, short_message,
teach, teach,
ui_testing: false, ui_testing: false,
@ -630,6 +728,7 @@ impl EmitterWriter {
pub fn new( pub fn new(
dst: Box<dyn Write + Send>, dst: Box<dyn Write + Send>,
source_map: Option<Lrc<SourceMap>>, source_map: Option<Lrc<SourceMap>>,
fallback_bundle: Lrc<FluentBundle>,
short_message: bool, short_message: bool,
teach: bool, teach: bool,
colored: bool, colored: bool,
@ -639,6 +738,7 @@ impl EmitterWriter {
EmitterWriter { EmitterWriter {
dst: Raw(dst, colored), dst: Raw(dst, colored),
sm: source_map, sm: source_map,
fallback_bundle,
short_message, short_message,
teach, teach,
ui_testing: false, ui_testing: false,
@ -1177,6 +1277,7 @@ impl EmitterWriter {
&self, &self,
buffer: &mut StyledBuffer, buffer: &mut StyledBuffer,
msg: &[(DiagnosticMessage, Style)], msg: &[(DiagnosticMessage, Style)],
args: &FluentArgs<'_>,
padding: usize, padding: usize,
label: &str, label: &str,
override_style: Option<Style>, override_style: Option<Style>,
@ -1229,7 +1330,7 @@ impl EmitterWriter {
// very *weird* formats // very *weird* formats
// see? // see?
for &(ref text, ref style) in msg.iter() { 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<_>>(); let lines = text.split('\n').collect::<Vec<_>>();
if lines.len() > 1 { if lines.len() > 1 {
for (i, line) in lines.iter().enumerate() { for (i, line) in lines.iter().enumerate() {
@ -1240,7 +1341,7 @@ impl EmitterWriter {
buffer.append(line_number, line, style_or_override(*style, override_style)); buffer.append(line_number, line, style_or_override(*style, override_style));
} }
} else { } 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, &mut self,
msp: &MultiSpan, msp: &MultiSpan,
msg: &[(DiagnosticMessage, Style)], msg: &[(DiagnosticMessage, Style)],
args: &FluentArgs<'_>,
code: &Option<DiagnosticId>, code: &Option<DiagnosticId>,
level: &Level, level: &Level,
max_line_num_len: usize, max_line_num_len: usize,
@ -1267,7 +1369,7 @@ impl EmitterWriter {
buffer.append(0, level.to_str(), Style::MainHeaderMsg); buffer.append(0, level.to_str(), Style::MainHeaderMsg);
buffer.append(0, ": ", Style::NoStyle); 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 { } else {
let mut label_width = 0; let mut label_width = 0;
// The failure note level itself does not provide any useful diagnostic information // The failure note level itself does not provide any useful diagnostic information
@ -1288,9 +1390,9 @@ impl EmitterWriter {
label_width += 2; label_width += 2;
} }
for &(ref text, _) in msg.iter() { 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. // 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( buffer.append(
0 + line, 0 + line,
&format!( &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 // Make sure our primary file comes first
let (primary_lo, sm) = if let (Some(sm), Some(ref primary_span)) = let (primary_lo, sm) = if let (Some(sm), Some(ref primary_span)) =
@ -1588,6 +1690,7 @@ impl EmitterWriter {
fn emit_suggestion_default( fn emit_suggestion_default(
&mut self, &mut self,
suggestion: &CodeSuggestion, suggestion: &CodeSuggestion,
args: &FluentArgs<'_>,
level: &Level, level: &Level,
max_line_num_len: usize, max_line_num_len: usize,
) -> io::Result<()> { ) -> io::Result<()> {
@ -1614,6 +1717,7 @@ impl EmitterWriter {
self.msg_to_buffer( self.msg_to_buffer(
&mut buffer, &mut buffer,
&[(suggestion.msg.to_owned(), Style::NoStyle)], &[(suggestion.msg.to_owned(), Style::NoStyle)],
args,
max_line_num_len, max_line_num_len,
"suggestion", "suggestion",
Some(Style::HeaderMsg), Some(Style::HeaderMsg),
@ -1855,6 +1959,7 @@ impl EmitterWriter {
&mut self, &mut self,
level: &Level, level: &Level,
message: &[(DiagnosticMessage, Style)], message: &[(DiagnosticMessage, Style)],
args: &FluentArgs<'_>,
code: &Option<DiagnosticId>, code: &Option<DiagnosticId>,
span: &MultiSpan, span: &MultiSpan,
children: &[SubDiagnostic], children: &[SubDiagnostic],
@ -1867,7 +1972,7 @@ impl EmitterWriter {
num_decimal_digits(n) 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(()) => { Ok(()) => {
if !children.is_empty() if !children.is_empty()
|| suggestions.iter().any(|s| s.style != SuggestionStyle::CompletelyHidden) || 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); let span = child.render_span.as_ref().unwrap_or(&child.span);
if let Err(err) = self.emit_message_default( if let Err(err) = self.emit_message_default(
&span, &span,
&child.styled_message(), &child.message,
args,
&None, &None,
&child.level, &child.level,
max_line_num_len, max_line_num_len,
@ -1906,6 +2012,7 @@ impl EmitterWriter {
if let Err(e) = self.emit_message_default( if let Err(e) = self.emit_message_default(
&MultiSpan::new(), &MultiSpan::new(),
&[(sugg.msg.to_owned(), Style::HeaderMsg)], &[(sugg.msg.to_owned(), Style::HeaderMsg)],
args,
&None, &None,
&Level::Help, &Level::Help,
max_line_num_len, max_line_num_len,
@ -1914,7 +2021,7 @@ impl EmitterWriter {
panic!("failed to emit error: {}", e); panic!("failed to emit error: {}", e);
} }
} else if let Err(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); 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 /// 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) /// This helps us quickly iterate over the whole message (including secondary file spans)
pub fn collect_annotations( pub fn collect_annotations(
emitter: &dyn Emitter,
args: &FluentArgs<'_>,
msp: &MultiSpan, msp: &MultiSpan,
source_map: &Option<Lrc<SourceMap>>,
) -> Vec<FileWithAnnotatedLines> { ) -> Vec<FileWithAnnotatedLines> {
fn add_annotation_to_file( fn add_annotation_to_file(
file_vec: &mut Vec<FileWithAnnotatedLines>, file_vec: &mut Vec<FileWithAnnotatedLines>,
@ -1976,7 +2084,7 @@ impl FileWithAnnotatedLines {
let mut output = vec![]; let mut output = vec![];
let mut multiline_annotations = 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() { for span_label in msp.span_labels() {
if span_label.span.is_dummy() { if span_label.span.is_dummy() {
continue; continue;
@ -2003,7 +2111,10 @@ impl FileWithAnnotatedLines {
start_col: lo.col_display, start_col: lo.col_display,
end_col: hi.col_display, end_col: hi.col_display,
is_primary: span_label.is_primary, 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, overlaps_exactly: false,
}; };
multiline_annotations.push((lo.file, ml)); multiline_annotations.push((lo.file, ml));
@ -2012,7 +2123,10 @@ impl FileWithAnnotatedLines {
start_col: lo.col_display, start_col: lo.col_display,
end_col: hi.col_display, end_col: hi.col_display,
is_primary: span_label.is_primary, 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, annotation_type: AnnotationType::Singleline,
}; };
add_annotation_to_file(&mut output, lo.file, lo.line, ann); 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::registry::Registry;
use crate::DiagnosticId; use crate::DiagnosticId;
use crate::ToolMetadata; use crate::ToolMetadata;
use crate::{CodeSuggestion, MultiSpan, SpanLabel, SubDiagnostic}; use crate::{CodeSuggestion, FluentBundle, MultiSpan, SpanLabel, SubDiagnostic};
use rustc_lint_defs::Applicability; use rustc_lint_defs::Applicability;
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
use rustc_error_messages::FluentArgs;
use rustc_span::hygiene::ExpnData; use rustc_span::hygiene::ExpnData;
use rustc_span::Span; use rustc_span::Span;
use std::io::{self, Write}; use std::io::{self, Write};
@ -36,6 +37,7 @@ pub struct JsonEmitter {
dst: Box<dyn Write + Send>, dst: Box<dyn Write + Send>,
registry: Option<Registry>, registry: Option<Registry>,
sm: Lrc<SourceMap>, sm: Lrc<SourceMap>,
fallback_bundle: Lrc<FluentBundle>,
pretty: bool, pretty: bool,
ui_testing: bool, ui_testing: bool,
json_rendered: HumanReadableErrorType, json_rendered: HumanReadableErrorType,
@ -47,6 +49,7 @@ impl JsonEmitter {
pub fn stderr( pub fn stderr(
registry: Option<Registry>, registry: Option<Registry>,
source_map: Lrc<SourceMap>, source_map: Lrc<SourceMap>,
fallback_bundle: Lrc<FluentBundle>,
pretty: bool, pretty: bool,
json_rendered: HumanReadableErrorType, json_rendered: HumanReadableErrorType,
terminal_width: Option<usize>, terminal_width: Option<usize>,
@ -56,6 +59,7 @@ impl JsonEmitter {
dst: Box::new(io::BufWriter::new(io::stderr())), dst: Box::new(io::BufWriter::new(io::stderr())),
registry, registry,
sm: source_map, sm: source_map,
fallback_bundle,
pretty, pretty,
ui_testing: false, ui_testing: false,
json_rendered, json_rendered,
@ -67,6 +71,7 @@ impl JsonEmitter {
pub fn basic( pub fn basic(
pretty: bool, pretty: bool,
json_rendered: HumanReadableErrorType, json_rendered: HumanReadableErrorType,
fallback_bundle: Lrc<FluentBundle>,
terminal_width: Option<usize>, terminal_width: Option<usize>,
macro_backtrace: bool, macro_backtrace: bool,
) -> JsonEmitter { ) -> JsonEmitter {
@ -74,6 +79,7 @@ impl JsonEmitter {
JsonEmitter::stderr( JsonEmitter::stderr(
None, None,
Lrc::new(SourceMap::new(file_path_mapping)), Lrc::new(SourceMap::new(file_path_mapping)),
fallback_bundle,
pretty, pretty,
json_rendered, json_rendered,
terminal_width, terminal_width,
@ -85,6 +91,7 @@ impl JsonEmitter {
dst: Box<dyn Write + Send>, dst: Box<dyn Write + Send>,
registry: Option<Registry>, registry: Option<Registry>,
source_map: Lrc<SourceMap>, source_map: Lrc<SourceMap>,
fallback_bundle: Lrc<FluentBundle>,
pretty: bool, pretty: bool,
json_rendered: HumanReadableErrorType, json_rendered: HumanReadableErrorType,
terminal_width: Option<usize>, terminal_width: Option<usize>,
@ -94,6 +101,7 @@ impl JsonEmitter {
dst, dst,
registry, registry,
sm: source_map, sm: source_map,
fallback_bundle,
pretty, pretty,
ui_testing: false, ui_testing: false,
json_rendered, json_rendered,
@ -173,6 +181,14 @@ impl Emitter for JsonEmitter {
Some(&self.sm) 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 { fn should_show_explain(&self) -> bool {
!matches!(self.json_rendered, HumanReadableErrorType::Short(_)) !matches!(self.json_rendered, HumanReadableErrorType::Short(_))
} }
@ -345,14 +361,18 @@ struct UnusedExterns<'a, 'b, 'c> {
impl Diagnostic { impl Diagnostic {
fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic { fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
let sugg = diag.suggestions.iter().flatten().map(|sugg| Diagnostic { let args = je.to_fluent_args(diag.args());
message: sugg.msg.clone().to_string(), 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, code: None,
level: "help", level: "help",
spans: DiagnosticSpan::from_suggestion(sugg, je), spans: DiagnosticSpan::from_suggestion(sugg, &args, je),
children: vec![], children: vec![],
rendered: None, rendered: None,
tool_metadata: sugg.tool_metadata.clone(), tool_metadata: sugg.tool_metadata.clone(),
}
}); });
// generate regular command line output and store it in the json // generate regular command line output and store it in the json
@ -375,6 +395,7 @@ impl Diagnostic {
.new_emitter( .new_emitter(
Box::new(buf), Box::new(buf),
Some(je.sm.clone()), Some(je.sm.clone()),
je.fallback_bundle.clone(),
false, false,
je.terminal_width, je.terminal_width,
je.macro_backtrace, je.macro_backtrace,
@ -384,15 +405,16 @@ impl Diagnostic {
let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap(); let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap();
let output = String::from_utf8(output).unwrap(); let output = String::from_utf8(output).unwrap();
let translated_message = je.translate_messages(&diag.message, &args);
Diagnostic { Diagnostic {
message: diag.message().to_string(), message: translated_message.to_string(),
code: DiagnosticCode::map_opt_string(diag.code.clone(), je), code: DiagnosticCode::map_opt_string(diag.code.clone(), je),
level: diag.level.to_str(), level: diag.level.to_str(),
spans: DiagnosticSpan::from_multispan(&diag.span, je), spans: DiagnosticSpan::from_multispan(&diag.span, &args, je),
children: diag children: diag
.children .children
.iter() .iter()
.map(|c| Diagnostic::from_sub_diagnostic(c, je)) .map(|c| Diagnostic::from_sub_diagnostic(c, &args, je))
.chain(sugg) .chain(sugg)
.collect(), .collect(),
rendered: Some(output), 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 { Diagnostic {
message: diag.message().to_string(), message: translated_message.to_string(),
code: None, code: None,
level: diag.level.to_str(), level: diag.level.to_str(),
spans: diag spans: diag
.render_span .render_span
.as_ref() .as_ref()
.map(|sp| DiagnosticSpan::from_multispan(sp, je)) .map(|sp| DiagnosticSpan::from_multispan(sp, args, je))
.unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, je)), .unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, args, je)),
children: vec![], children: vec![],
rendered: None, rendered: None,
tool_metadata: ToolMetadata::default(), tool_metadata: ToolMetadata::default(),
@ -421,12 +448,13 @@ impl DiagnosticSpan {
fn from_span_label( fn from_span_label(
span: SpanLabel, span: SpanLabel,
suggestion: Option<(&String, Applicability)>, suggestion: Option<(&String, Applicability)>,
args: &FluentArgs<'_>,
je: &JsonEmitter, je: &JsonEmitter,
) -> DiagnosticSpan { ) -> DiagnosticSpan {
Self::from_span_etc( Self::from_span_etc(
span.span, span.span,
span.is_primary, 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, suggestion,
je, 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() msp.span_labels()
.into_iter() .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() .collect()
} }
fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter) -> Vec<DiagnosticSpan> { fn from_suggestion(
suggestion: &CodeSuggestion,
args: &FluentArgs<'_>,
je: &JsonEmitter,
) -> Vec<DiagnosticSpan> {
suggestion suggestion
.substitutions .substitutions
.iter() .iter()
@ -510,6 +546,7 @@ impl DiagnosticSpan {
DiagnosticSpan::from_span_label( DiagnosticSpan::from_span_label(
span_label, span_label,
Some((&suggestion_inner.snippet, suggestion.applicability)), Some((&suggestion_inner.snippet, suggestion.applicability)),
args,
je, 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(|| { rustc_span::create_default_session_globals_then(|| {
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned()); 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 output = Arc::new(Mutex::new(Vec::new()));
let je = JsonEmitter::new( let je = JsonEmitter::new(
Box::new(Shared { data: output.clone() }), Box::new(Shared { data: output.clone() }),
None, None,
sm, sm,
fallback_bundle,
true, true,
HumanReadableErrorType::Short(ColorConfig::Never), HumanReadableErrorType::Short(ColorConfig::Never),
None, 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::stable_hasher::StableHasher;
use rustc_data_structures::sync::{self, Lock, Lrc}; use rustc_data_structures::sync::{self, Lock, Lrc};
use rustc_data_structures::AtomicRef; 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}; pub use rustc_lint_defs::{pluralize, Applicability};
use rustc_serialize::json::Json; use rustc_serialize::json::Json;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
@ -402,7 +404,9 @@ impl fmt::Display for ExplicitBug {
impl error::Error 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}; pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee};
use std::backtrace::Backtrace; use std::backtrace::Backtrace;
@ -540,10 +544,12 @@ impl Handler {
can_emit_warnings: bool, can_emit_warnings: bool,
treat_err_as_bug: Option<NonZeroUsize>, treat_err_as_bug: Option<NonZeroUsize>,
sm: Option<Lrc<SourceMap>>, sm: Option<Lrc<SourceMap>>,
fallback_bundle: Lrc<FluentBundle>,
) -> Self { ) -> Self {
Self::with_tty_emitter_and_flags( Self::with_tty_emitter_and_flags(
color_config, color_config,
sm, sm,
fallback_bundle,
HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() },
) )
} }
@ -551,11 +557,13 @@ impl Handler {
pub fn with_tty_emitter_and_flags( pub fn with_tty_emitter_and_flags(
color_config: ColorConfig, color_config: ColorConfig,
sm: Option<Lrc<SourceMap>>, sm: Option<Lrc<SourceMap>>,
fallback_bundle: Lrc<FluentBundle>,
flags: HandlerFlags, flags: HandlerFlags,
) -> Self { ) -> Self {
let emitter = Box::new(EmitterWriter::stderr( let emitter = Box::new(EmitterWriter::stderr(
color_config, color_config,
sm, sm,
fallback_bundle,
false, false,
false, false,
None, None,
@ -660,7 +668,7 @@ impl Handler {
pub fn struct_span_warn( pub fn struct_span_warn(
&self, &self,
span: impl Into<MultiSpan>, span: impl Into<MultiSpan>,
msg: &str, msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ()> { ) -> DiagnosticBuilder<'_, ()> {
let mut result = self.struct_warn(msg); let mut result = self.struct_warn(msg);
result.set_span(span); result.set_span(span);
@ -671,7 +679,7 @@ impl Handler {
pub fn struct_span_allow( pub fn struct_span_allow(
&self, &self,
span: impl Into<MultiSpan>, span: impl Into<MultiSpan>,
msg: &str, msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ()> { ) -> DiagnosticBuilder<'_, ()> {
let mut result = self.struct_allow(msg); let mut result = self.struct_allow(msg);
result.set_span(span); result.set_span(span);
@ -683,7 +691,7 @@ impl Handler {
pub fn struct_span_warn_with_code( pub fn struct_span_warn_with_code(
&self, &self,
span: impl Into<MultiSpan>, span: impl Into<MultiSpan>,
msg: &str, msg: impl Into<DiagnosticMessage>,
code: DiagnosticId, code: DiagnosticId,
) -> DiagnosticBuilder<'_, ()> { ) -> DiagnosticBuilder<'_, ()> {
let mut result = self.struct_span_warn(span, msg); 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: /// Attempting to `.emit()` the builder will only emit if either:
/// * `can_emit_warnings` is `true` /// * `can_emit_warnings` is `true`
/// * `is_force_warn` was set in `DiagnosticId::Lint` /// * `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) DiagnosticBuilder::new(self, Level::Warning, msg)
} }
/// Construct a builder at the `Allow` level with the `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) DiagnosticBuilder::new(self, Level::Allow, msg)
} }
/// Construct a builder at the `Expect` level with the `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) DiagnosticBuilder::new(self, Level::Expect(id), msg)
} }
@ -714,7 +726,7 @@ impl Handler {
pub fn struct_span_err( pub fn struct_span_err(
&self, &self,
span: impl Into<MultiSpan>, span: impl Into<MultiSpan>,
msg: &str, msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> { ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
let mut result = self.struct_err(msg); let mut result = self.struct_err(msg);
result.set_span(span); result.set_span(span);
@ -725,7 +737,7 @@ impl Handler {
pub fn struct_span_err_with_code( pub fn struct_span_err_with_code(
&self, &self,
span: impl Into<MultiSpan>, span: impl Into<MultiSpan>,
msg: &str, msg: impl Into<DiagnosticMessage>,
code: DiagnosticId, code: DiagnosticId,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> { ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
let mut result = self.struct_span_err(span, msg); 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`. /// Construct a builder at the `Error` level with the `msg`.
// FIXME: This method should be removed (every error should have an associated error code). // FIXME: This method should be removed (every error should have an associated error code).
pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_, ErrorGuaranteed> { pub fn struct_err(
DiagnosticBuilder::new_guaranteeing_error::<{ Level::Error { lint: false } }>(self, msg) &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. /// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors.
#[doc(hidden)] #[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) DiagnosticBuilder::new(self, Level::Error { lint: true }, msg)
} }
/// Construct a builder at the `Error` level with the `msg` and the `code`. /// Construct a builder at the `Error` level with the `msg` and the `code`.
pub fn struct_err_with_code( pub fn struct_err_with_code(
&self, &self,
msg: &str, msg: impl Into<DiagnosticMessage>,
code: DiagnosticId, code: DiagnosticId,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> { ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
let mut result = self.struct_err(msg); let mut result = self.struct_err(msg);
@ -760,7 +775,7 @@ impl Handler {
pub fn struct_span_fatal( pub fn struct_span_fatal(
&self, &self,
span: impl Into<MultiSpan>, span: impl Into<MultiSpan>,
msg: &str, msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, !> { ) -> DiagnosticBuilder<'_, !> {
let mut result = self.struct_fatal(msg); let mut result = self.struct_fatal(msg);
result.set_span(span); result.set_span(span);
@ -771,7 +786,7 @@ impl Handler {
pub fn struct_span_fatal_with_code( pub fn struct_span_fatal_with_code(
&self, &self,
span: impl Into<MultiSpan>, span: impl Into<MultiSpan>,
msg: &str, msg: impl Into<DiagnosticMessage>,
code: DiagnosticId, code: DiagnosticId,
) -> DiagnosticBuilder<'_, !> { ) -> DiagnosticBuilder<'_, !> {
let mut result = self.struct_span_fatal(span, msg); 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`. /// 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) DiagnosticBuilder::new_fatal(self, msg)
} }
/// Construct a builder at the `Help` level with the `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) DiagnosticBuilder::new(self, Level::Help, msg)
} }
/// Construct a builder at the `Note` level with the `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) 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); self.emit_diag_at_span(Diagnostic::new(Fatal, msg), span);
FatalError.raise() FatalError.raise()
} }
@ -802,80 +820,106 @@ impl Handler {
pub fn span_fatal_with_code( pub fn span_fatal_with_code(
&self, &self,
span: impl Into<MultiSpan>, span: impl Into<MultiSpan>,
msg: &str, msg: impl Into<DiagnosticMessage>,
code: DiagnosticId, code: DiagnosticId,
) -> ! { ) -> ! {
self.emit_diag_at_span(Diagnostic::new_with_code(Fatal, Some(code), msg), span); self.emit_diag_at_span(Diagnostic::new_with_code(Fatal, Some(code), msg), span);
FatalError.raise() 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() 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( self.emit_diag_at_span(
Diagnostic::new_with_code(Error { lint: false }, Some(code), msg), Diagnostic::new_with_code(Error { lint: false }, Some(code), msg),
span, 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); 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); 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) self.inner.borrow_mut().span_bug(span, msg)
} }
#[track_caller] #[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) self.inner.borrow_mut().delay_span_bug(span, msg)
} }
// FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's // 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). // 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) 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); 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); 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); let mut db = DiagnosticBuilder::new(self, Note, msg);
db.set_span(span); db.set_span(span);
db db
} }
// NOTE: intentionally doesn't raise an error so rustc_codegen_ssa only reports fatal errors in the main thread // 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) 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) 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); let mut db = DiagnosticBuilder::new(self, Warning, msg);
db.emit(); 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(); 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) self.inner.borrow_mut().bug(msg)
} }
@ -1145,7 +1189,10 @@ impl HandlerInner {
match (errors.len(), warnings.len()) { match (errors.len(), warnings.len()) {
(0, 0) => return, (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) => { (_, 0) => {
let _ = self.fatal(&errors); 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); self.emit_diag_at_span(Diagnostic::new(Bug, msg), sp);
panic::panic_any(ExplicitBug); panic::panic_any(ExplicitBug);
} }
@ -1230,7 +1277,11 @@ impl HandlerInner {
} }
#[track_caller] #[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 // 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. // 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. // 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 // 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). // 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); let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg);
if self.flags.report_delayed_bugs { if self.flags.report_delayed_bugs {
self.emit_diagnostic(&mut diagnostic); self.emit_diagnostic(&mut diagnostic);
@ -1255,33 +1306,37 @@ impl HandlerInner {
self.delayed_good_path_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace)); 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)); 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); self.emit(Fatal, msg);
FatalError FatalError
} }
fn err(&mut self, msg: &str) -> ErrorGuaranteed { fn err(&mut self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
self.emit(Error { lint: false }, msg) self.emit(Error { lint: false }, msg)
} }
/// Emit an error; level should be `Error` or `Fatal`. /// 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() { if self.treat_err_as_bug() {
self.bug(msg); self.bug(msg);
} }
self.emit_diagnostic(&mut Diagnostic::new(level, msg)).unwrap() 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)); self.emit_diagnostic(&mut Diagnostic::new(Bug, msg));
panic::panic_any(ExplicitBug); 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; let mut no_bugs = true;
for mut bug in bugs { for mut bug in bugs {
if no_bugs { if no_bugs {

View file

@ -250,7 +250,7 @@ fn check_binders(
if let Some(prev_info) = binders.get(&name) { if let Some(prev_info) = binders.get(&name) {
// 1. The meta-variable is already bound in the current LHS: This is an error. // 1. The meta-variable is already bound in the current LHS: This is an error.
let mut span = MultiSpan::from_span(span); let mut span = MultiSpan::from_span(span);
span.push_span_label(prev_info.span, "previous declaration".into()); span.push_span_label(prev_info.span, "previous declaration");
buffer_lint(sess, span, node_id, "duplicate matcher binding"); buffer_lint(sess, span, node_id, "duplicate matcher binding");
} else if get_binder_info(macros, binders, name).is_none() { } else if get_binder_info(macros, binders, name).is_none() {
// 2. The meta-variable is free: This is a binder. // 2. The meta-variable is free: This is a binder.
@ -622,7 +622,7 @@ fn ops_is_prefix(
for (i, binder) in binder_ops.iter().enumerate() { for (i, binder) in binder_ops.iter().enumerate() {
if i >= occurrence_ops.len() { if i >= occurrence_ops.len() {
let mut span = MultiSpan::from_span(span); let mut span = MultiSpan::from_span(span);
span.push_span_label(binder.span, "expected repetition".into()); span.push_span_label(binder.span, "expected repetition");
let message = &format!("variable '{}' is still repeating at this depth", name); let message = &format!("variable '{}' is still repeating at this depth", name);
buffer_lint(sess, span, node_id, message); buffer_lint(sess, span, node_id, message);
return; return;
@ -630,8 +630,8 @@ fn ops_is_prefix(
let occurrence = &occurrence_ops[i]; let occurrence = &occurrence_ops[i];
if occurrence.op != binder.op { if occurrence.op != binder.op {
let mut span = MultiSpan::from_span(span); let mut span = MultiSpan::from_span(span);
span.push_span_label(binder.span, "expected repetition".into()); span.push_span_label(binder.span, "expected repetition");
span.push_span_label(occurrence.span, "conflicting repetition".into()); span.push_span_label(occurrence.span, "conflicting repetition");
let message = "meta-variable repeats with different Kleene operator"; let message = "meta-variable repeats with different Kleene operator";
buffer_lint(sess, span, node_id, message); buffer_lint(sess, span, node_id, message);
return; return;

View file

@ -69,7 +69,7 @@ fn emit_frag_parse_err(
kind: AstFragmentKind, kind: AstFragmentKind,
) { ) {
// FIXME(davidtwco): avoid depending on the error message text // FIXME(davidtwco): avoid depending on the error message text
if parser.token == token::Eof && e.message().as_str().ends_with(", found `<eof>`") { if parser.token == token::Eof && e.message[0].0.expect_str().ends_with(", found `<eof>`") {
if !e.span.is_dummy() { if !e.span.is_dummy() {
// early end of macro arm (#52866) // early end of macro arm (#52866)
e.replace_span_with(parser.sess.source_map().next_point(parser.token.span)); e.replace_span_with(parser.sess.source_map().next_point(parser.token.span));
@ -78,7 +78,7 @@ fn emit_frag_parse_err(
e.message[0] = ( e.message[0] = (
rustc_errors::DiagnosticMessage::Str(format!( rustc_errors::DiagnosticMessage::Str(format!(
"macro expansion ends with an incomplete expression: {}", "macro expansion ends with an incomplete expression: {}",
msg.0.as_str().replace(", found `<eof>`", ""), msg.0.expect_str().replace(", found `<eof>`", ""),
)), )),
msg.1, msg.1,
); );

View file

@ -128,13 +128,15 @@ fn parse_ident<'sess>(
sess: &'sess ParseSess, sess: &'sess ParseSess,
span: Span, span: Span,
) -> PResult<'sess, Ident> { ) -> PResult<'sess, Ident> {
let err_fn = |msg| sess.span_diagnostic.struct_span_err(span, msg);
if let Some(tt) = iter.next() && let TokenTree::Token(token) = tt { if let Some(tt) = iter.next() && let TokenTree::Token(token) = tt {
if let Some((elem, false)) = token.ident() { if let Some((elem, false)) = token.ident() {
return Ok(elem); return Ok(elem);
} }
let token_str = pprust::token_to_string(&token); let token_str = pprust::token_to_string(&token);
let mut err = err_fn(&format!("expected identifier, found `{}`", &token_str)); let mut err = sess.span_diagnostic.struct_span_err(
span,
&format!("expected identifier, found `{}`", &token_str)
);
err.span_suggestion( err.span_suggestion(
token.span, token.span,
&format!("try removing `{}`", &token_str), &format!("try removing `{}`", &token_str),
@ -143,7 +145,7 @@ fn parse_ident<'sess>(
); );
return Err(err); return Err(err);
} }
Err(err_fn("expected identifier")) Err(sess.span_diagnostic.struct_span_err(span, "expected identifier"))
} }
/// Tries to move the iterator forward returning `true` if there is a comma. If not, then the /// Tries to move the iterator forward returning `true` if there is a comma. If not, then the

View file

@ -127,6 +127,7 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &
create_default_session_if_not_set_then(|_| { create_default_session_if_not_set_then(|_| {
let output = Arc::new(Mutex::new(Vec::new())); let output = Arc::new(Mutex::new(Vec::new()));
let fallback_bundle = rustc_errors::fallback_fluent_bundle();
let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned()); source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned());
@ -142,6 +143,7 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &
let emitter = EmitterWriter::new( let emitter = EmitterWriter::new(
Box::new(Shared { data: output.clone() }), Box::new(Shared { data: output.clone() }),
Some(source_map.clone()), Some(source_map.clone()),
fallback_bundle,
false, false,
false, false,
false, false,

View file

@ -2075,7 +2075,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
{ {
diag.span_suggestion( diag.span_suggestion(
span, span,
msg, *msg,
format!("{}.as_ref()", snippet), format!("{}.as_ref()", snippet),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );

View file

@ -41,8 +41,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
let mut err = self.tcx().sess.struct_span_err(cause.span, "incompatible lifetime on type"); let mut err = self.tcx().sess.struct_span_err(cause.span, "incompatible lifetime on type");
// FIXME: we should point at the lifetime // FIXME: we should point at the lifetime
let mut multi_span: MultiSpan = vec![binding_span].into(); let mut multi_span: MultiSpan = vec![binding_span].into();
multi_span multi_span.push_span_label(binding_span, "introduces a `'static` lifetime requirement");
.push_span_label(binding_span, "introduces a `'static` lifetime requirement".into());
err.span_note(multi_span, "because this has an unmet lifetime requirement"); err.span_note(multi_span, "because this has an unmet lifetime requirement");
note_and_explain_region(self.tcx(), &mut err, "", sup, "...", Some(binding_span)); note_and_explain_region(self.tcx(), &mut err, "", sup, "...", Some(binding_span));
if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) { if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) {

View file

@ -7,7 +7,7 @@ use rustc_middle::ty::{self, Region};
impl<'a, 'tcx> InferCtxt<'a, 'tcx> { impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) { pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) {
let mut label_or_note = |span, msg| { let mut label_or_note = |span, msg: &str| {
let sub_count = err.children.iter().filter(|d| d.span.is_dummy()).count(); let sub_count = err.children.iter().filter(|d| d.span.is_dummy()).count();
let expanded_sub_count = err.children.iter().filter(|d| !d.span.is_dummy()).count(); let expanded_sub_count = err.children.iter().filter(|d| !d.span.is_dummy()).count();
let span_is_primary = err.span.primary_spans().iter().all(|&sp| sp == span); let span_is_primary = err.span.primary_spans().iter().all(|&sp| sp == span);

View file

@ -1571,7 +1571,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
lint.build("bounds on generic parameters are not enforced in type aliases"); lint.build("bounds on generic parameters are not enforced in type aliases");
let msg = "the bound will not be checked when the type alias is used, \ let msg = "the bound will not be checked when the type alias is used, \
and should be removed"; and should be removed";
err.multipart_suggestion(&msg, suggestion, Applicability::MachineApplicable); err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable);
if !suggested_changing_assoc_types { if !suggested_changing_assoc_types {
TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
suggested_changing_assoc_types = true; suggested_changing_assoc_types = true;

View file

@ -37,7 +37,7 @@ fn emit_unfulfilled_expectation_lint(
|diag| { |diag| {
let mut diag = diag.build("this lint expectation is unfulfilled"); let mut diag = diag.build("this lint expectation is unfulfilled");
if let Some(rationale) = expectation.reason { if let Some(rationale) = expectation.reason {
diag.note(&rationale.as_str()); diag.note(rationale.as_str());
} }
if expectation.is_unfulfilled_lint_expectations { if expectation.is_unfulfilled_lint_expectations {

View file

@ -154,7 +154,7 @@ fn lint_overflowing_range_endpoint<'tcx>(
let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix);
err.span_suggestion( err.span_suggestion(
parent_expr.span, parent_expr.span,
&"use an inclusive range instead", "use an inclusive range instead",
suggestion, suggestion,
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
@ -399,7 +399,7 @@ fn lint_uint_literal<'tcx>(
lint.build("only `u8` can be cast into `char`") lint.build("only `u8` can be cast into `char`")
.span_suggestion( .span_suggestion(
par_e.span, par_e.span,
&"use a `char` literal instead", "use a `char` literal instead",
format!("'\\u{{{:X}}}'", lit_val), format!("'\\u{{{:X}}}'", lit_val),
Applicability::MachineApplicable, Applicability::MachineApplicable,
) )

View file

@ -18,7 +18,9 @@ use rustc_ast::{
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{pluralize, struct_span_err, Diagnostic, EmissionGuarantee, ErrorGuaranteed}; use rustc_errors::{pluralize, struct_span_err, Diagnostic, EmissionGuarantee, ErrorGuaranteed};
use rustc_errors::{Applicability, DiagnosticBuilder, Handler, MultiSpan, PResult}; use rustc_errors::{
Applicability, DiagnosticBuilder, DiagnosticMessage, Handler, MultiSpan, PResult,
};
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, Ident}; use rustc_span::symbol::{kw, Ident};
use rustc_span::{Span, SpanSnippetError, DUMMY_SP}; use rustc_span::{Span, SpanSnippetError, DUMMY_SP};
@ -273,12 +275,12 @@ impl<'a> Parser<'a> {
pub fn struct_span_err<S: Into<MultiSpan>>( pub fn struct_span_err<S: Into<MultiSpan>>(
&self, &self,
sp: S, sp: S,
m: &str, m: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'a, ErrorGuaranteed> { ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
self.sess.span_diagnostic.struct_span_err(sp, m) self.sess.span_diagnostic.struct_span_err(sp, m)
} }
pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> ! { pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: impl Into<DiagnosticMessage>) -> ! {
self.sess.span_diagnostic.span_bug(sp, m) self.sess.span_diagnostic.span_bug(sp, m)
} }
@ -584,9 +586,15 @@ impl<'a> Parser<'a> {
// field: value, // field: value,
// } } // } }
err.delay_as_bug(); err.delay_as_bug();
self.struct_span_err(expr.span, "struct literal body without path") self.struct_span_err(
expr.span,
DiagnosticMessage::fluent("parser-struct-literal-body-without-path"),
)
.multipart_suggestion( .multipart_suggestion(
"you might have forgotten to add the struct literal inside the block", DiagnosticMessage::fluent_attr(
"parser-struct-literal-body-without-path",
"suggestion",
),
vec![ vec![
(expr.span.shrink_to_lo(), "{ SomeStruct ".to_string()), (expr.span.shrink_to_lo(), "{ SomeStruct ".to_string()),
(expr.span.shrink_to_hi(), " }".to_string()), (expr.span.shrink_to_hi(), " }".to_string()),
@ -1752,7 +1760,7 @@ impl<'a> Parser<'a> {
let mut primary_span: MultiSpan = primary_span.into(); let mut primary_span: MultiSpan = primary_span.into();
for span_label in err.span.span_labels() { for span_label in err.span.span_labels() {
if let Some(label) = span_label.label { if let Some(label) = span_label.label {
primary_span.push_span_message(span_label.span, label); primary_span.push_span_label(span_label.span, label);
} }
} }
err.set_span(primary_span); err.set_span(primary_span);

View file

@ -1703,7 +1703,7 @@ impl<'a> Parser<'a> {
if matches!(expr.kind, ExprKind::Err) { if matches!(expr.kind, ExprKind::Err) {
let mut err = self let mut err = self
.diagnostic() .diagnostic()
.struct_span_err(self.token.span, &"invalid interpolated expression"); .struct_span_err(self.token.span, "invalid interpolated expression");
err.downgrade_to_delayed_bug(); err.downgrade_to_delayed_bug();
return err; return err;
} }
@ -1820,7 +1820,7 @@ impl<'a> Parser<'a> {
} else if let Some(fixed) = fix_base_capitalisation(suf) { } else if let Some(fixed) = fix_base_capitalisation(suf) {
let msg = "invalid base prefix for number literal"; let msg = "invalid base prefix for number literal";
self.struct_span_err(span, &msg) self.struct_span_err(span, msg)
.note("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase") .note("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase")
.span_suggestion( .span_suggestion(
span, span,

View file

@ -1011,7 +1011,7 @@ impl<'a> Parser<'a> {
let current_qual_sp = current_qual_sp.to(sp_start); let current_qual_sp = current_qual_sp.to(sp_start);
if let Ok(current_qual) = self.span_to_snippet(current_qual_sp) { if let Ok(current_qual) = self.span_to_snippet(current_qual_sp) {
// FIXME(davidtwco): avoid depending on the error message text // FIXME(davidtwco): avoid depending on the error message text
if err.message().as_str() == "expected `{`, found keyword `unsafe`" { if err.message[0].0.expect_str() == "expected `{`, found keyword `unsafe`" {
let invalid_qual_sp = self.token.uninterpolated_span(); let invalid_qual_sp = self.token.uninterpolated_span();
let invalid_qual = self.span_to_snippet(invalid_qual_sp).unwrap(); let invalid_qual = self.span_to_snippet(invalid_qual_sp).unwrap();

View file

@ -620,8 +620,8 @@ fn incremental_verify_ich_cold(sess: &Session, dep_node: DebugArg<'_>, result: D
} else { } else {
sess.struct_err(&format!("internal compiler error: encountered incremental compilation error with {:?}", dep_node)) sess.struct_err(&format!("internal compiler error: encountered incremental compilation error with {:?}", dep_node))
.help(&format!("This is a known issue with the compiler. Run {} to allow your project to compile", run_cmd)) .help(&format!("This is a known issue with the compiler. Run {} to allow your project to compile", run_cmd))
.note(&"Please follow the instructions below to create a bug report with the provided information") .note("Please follow the instructions below to create a bug report with the provided information")
.note(&"See <https://github.com/rust-lang/rust/issues/84970> for more information") .note("See <https://github.com/rust-lang/rust/issues/84970> for more information")
.emit(); .emit();
panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result); panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result);
} }

View file

@ -1341,7 +1341,7 @@ impl<'a> Resolver<'a> {
let def_span = self.session.source_map().guess_head_span(binding.span); let def_span = self.session.source_map().guess_head_span(binding.span);
let mut note_span = MultiSpan::from_span(def_span); let mut note_span = MultiSpan::from_span(def_span);
if !first && binding.vis.is_public() { if !first && binding.vis.is_public() {
note_span.push_span_label(def_span, "consider importing it directly".into()); note_span.push_span_label(def_span, "consider importing it directly");
} }
err.span_note(note_span, &msg); err.span_note(note_span, &msg);
} }

View file

@ -739,7 +739,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
if let Some((_, UnresolvedImportError { note, .. })) = errors.iter().last() { if let Some((_, UnresolvedImportError { note, .. })) = errors.iter().last() {
for message in note { for message in note {
diag.note(&message); diag.note(message);
} }
} }

View file

@ -1140,7 +1140,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
} }
err.span_suggestion( err.span_suggestion(
span, span,
&"use this syntax instead", "use this syntax instead",
path_str.to_string(), path_str.to_string(),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );

View file

@ -431,6 +431,20 @@ where
} }
} }
impl<'a, S: Encoder> Encodable<S> for Cow<'a, str> {
fn encode(&self, s: &mut S) -> Result<(), S::Error> {
let val: &str = self;
val.encode(s)
}
}
impl<'a, D: Decoder> Decodable<D> for Cow<'a, str> {
fn decode(d: &mut D) -> Cow<'static, str> {
let v: String = Decodable::decode(d);
Cow::Owned(v)
}
}
impl<S: Encoder, T: Encodable<S>> Encodable<S> for Option<T> { impl<S: Encoder, T: Encodable<S>> Encodable<S> for Option<T> {
fn encode(&self, s: &mut S) -> Result<(), S::Error> { fn encode(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_option(|s| match *self { s.emit_option(|s| match *self {

View file

@ -8,7 +8,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::{Lock, Lrc}; use rustc_data_structures::sync::{Lock, Lrc};
use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler}; use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
use rustc_errors::{ use rustc_errors::{
error_code, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, error_code, fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder,
ErrorGuaranteed, MultiSpan,
}; };
use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures}; use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
use rustc_span::edition::Edition; use rustc_span::edition::Edition;
@ -173,8 +174,15 @@ pub struct ParseSess {
impl ParseSess { impl ParseSess {
/// Used for testing. /// Used for testing.
pub fn new(file_path_mapping: FilePathMapping) -> Self { pub fn new(file_path_mapping: FilePathMapping) -> Self {
let fallback_bundle = fallback_fluent_bundle();
let sm = Lrc::new(SourceMap::new(file_path_mapping)); let sm = Lrc::new(SourceMap::new(file_path_mapping));
let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, None, Some(sm.clone())); let handler = Handler::with_tty_emitter(
ColorConfig::Auto,
true,
None,
Some(sm.clone()),
fallback_bundle,
);
ParseSess::with_span_handler(handler, sm) ParseSess::with_span_handler(handler, sm)
} }
@ -203,8 +211,10 @@ impl ParseSess {
} }
pub fn with_silent_emitter(fatal_note: Option<String>) -> Self { pub fn with_silent_emitter(fatal_note: Option<String>) -> Self {
let fallback_bundle = fallback_fluent_bundle();
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
let fatal_handler = Handler::with_tty_emitter(ColorConfig::Auto, false, None, None); let fatal_handler =
Handler::with_tty_emitter(ColorConfig::Auto, false, None, None, fallback_bundle);
let handler = Handler::with_emitter( let handler = Handler::with_emitter(
false, false,
None, None,

View file

@ -19,7 +19,10 @@ use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitterWriter;
use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType}; use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType};
use rustc_errors::json::JsonEmitter; use rustc_errors::json::JsonEmitter;
use rustc_errors::registry::Registry; use rustc_errors::registry::Registry;
use rustc_errors::{DiagnosticBuilder, DiagnosticId, ErrorGuaranteed, MultiSpan}; use rustc_errors::{
fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, ErrorGuaranteed,
FluentBundle, MultiSpan,
};
use rustc_macros::HashStable_Generic; use rustc_macros::HashStable_Generic;
pub use rustc_span::def_id::StableCrateId; pub use rustc_span::def_id::StableCrateId;
use rustc_span::edition::Edition; use rustc_span::edition::Edition;
@ -279,34 +282,34 @@ impl Session {
pub fn struct_span_warn<S: Into<MultiSpan>>( pub fn struct_span_warn<S: Into<MultiSpan>>(
&self, &self,
sp: S, sp: S,
msg: &str, msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ()> { ) -> DiagnosticBuilder<'_, ()> {
self.diagnostic().struct_span_warn(sp, msg) self.diagnostic().struct_span_warn(sp, msg)
} }
pub fn struct_span_warn_with_code<S: Into<MultiSpan>>( pub fn struct_span_warn_with_code<S: Into<MultiSpan>>(
&self, &self,
sp: S, sp: S,
msg: &str, msg: impl Into<DiagnosticMessage>,
code: DiagnosticId, code: DiagnosticId,
) -> DiagnosticBuilder<'_, ()> { ) -> DiagnosticBuilder<'_, ()> {
self.diagnostic().struct_span_warn_with_code(sp, msg, code) self.diagnostic().struct_span_warn_with_code(sp, msg, code)
} }
pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
self.diagnostic().struct_warn(msg) self.diagnostic().struct_warn(msg)
} }
pub fn struct_span_allow<S: Into<MultiSpan>>( pub fn struct_span_allow<S: Into<MultiSpan>>(
&self, &self,
sp: S, sp: S,
msg: &str, msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ()> { ) -> DiagnosticBuilder<'_, ()> {
self.diagnostic().struct_span_allow(sp, msg) self.diagnostic().struct_span_allow(sp, msg)
} }
pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
self.diagnostic().struct_allow(msg) self.diagnostic().struct_allow(msg)
} }
pub fn struct_expect( pub fn struct_expect(
&self, &self,
msg: &str, msg: impl Into<DiagnosticMessage>,
id: lint::LintExpectationId, id: lint::LintExpectationId,
) -> DiagnosticBuilder<'_, ()> { ) -> DiagnosticBuilder<'_, ()> {
self.diagnostic().struct_expect(msg, id) self.diagnostic().struct_expect(msg, id)
@ -314,25 +317,28 @@ impl Session {
pub fn struct_span_err<S: Into<MultiSpan>>( pub fn struct_span_err<S: Into<MultiSpan>>(
&self, &self,
sp: S, sp: S,
msg: &str, msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> { ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
self.diagnostic().struct_span_err(sp, msg) self.diagnostic().struct_span_err(sp, msg)
} }
pub fn struct_span_err_with_code<S: Into<MultiSpan>>( pub fn struct_span_err_with_code<S: Into<MultiSpan>>(
&self, &self,
sp: S, sp: S,
msg: &str, msg: impl Into<DiagnosticMessage>,
code: DiagnosticId, code: DiagnosticId,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> { ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
self.diagnostic().struct_span_err_with_code(sp, msg, code) self.diagnostic().struct_span_err_with_code(sp, msg, code)
} }
// FIXME: This method should be removed (every error should have an associated error code). // FIXME: This method should be removed (every error should have an associated error code).
pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_, ErrorGuaranteed> { pub fn struct_err(
&self,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
self.diagnostic().struct_err(msg) self.diagnostic().struct_err(msg)
} }
pub fn struct_err_with_code( pub fn struct_err_with_code(
&self, &self,
msg: &str, msg: impl Into<DiagnosticMessage>,
code: DiagnosticId, code: DiagnosticId,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> { ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
self.diagnostic().struct_err_with_code(msg, code) self.diagnostic().struct_err_with_code(msg, code)
@ -340,50 +346,64 @@ impl Session {
pub fn struct_span_fatal<S: Into<MultiSpan>>( pub fn struct_span_fatal<S: Into<MultiSpan>>(
&self, &self,
sp: S, sp: S,
msg: &str, msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, !> { ) -> DiagnosticBuilder<'_, !> {
self.diagnostic().struct_span_fatal(sp, msg) self.diagnostic().struct_span_fatal(sp, msg)
} }
pub fn struct_span_fatal_with_code<S: Into<MultiSpan>>( pub fn struct_span_fatal_with_code<S: Into<MultiSpan>>(
&self, &self,
sp: S, sp: S,
msg: &str, msg: impl Into<DiagnosticMessage>,
code: DiagnosticId, code: DiagnosticId,
) -> DiagnosticBuilder<'_, !> { ) -> DiagnosticBuilder<'_, !> {
self.diagnostic().struct_span_fatal_with_code(sp, msg, code) self.diagnostic().struct_span_fatal_with_code(sp, msg, code)
} }
pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_, !> { pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
self.diagnostic().struct_fatal(msg) self.diagnostic().struct_fatal(msg)
} }
pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! { pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) -> ! {
self.diagnostic().span_fatal(sp, msg) self.diagnostic().span_fatal(sp, msg)
} }
pub fn span_fatal_with_code<S: Into<MultiSpan>>( pub fn span_fatal_with_code<S: Into<MultiSpan>>(
&self, &self,
sp: S, sp: S,
msg: &str, msg: impl Into<DiagnosticMessage>,
code: DiagnosticId, code: DiagnosticId,
) -> ! { ) -> ! {
self.diagnostic().span_fatal_with_code(sp, msg, code) self.diagnostic().span_fatal_with_code(sp, msg, code)
} }
pub fn fatal(&self, msg: &str) -> ! { pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> ! {
self.diagnostic().fatal(msg).raise() self.diagnostic().fatal(msg).raise()
} }
pub fn span_err_or_warn<S: Into<MultiSpan>>(&self, is_warning: bool, sp: S, msg: &str) { pub fn span_err_or_warn<S: Into<MultiSpan>>(
&self,
is_warning: bool,
sp: S,
msg: impl Into<DiagnosticMessage>,
) {
if is_warning { if is_warning {
self.span_warn(sp, msg); self.span_warn(sp, msg);
} else { } else {
self.span_err(sp, msg); self.span_err(sp, msg);
} }
} }
pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ErrorGuaranteed { pub fn span_err<S: Into<MultiSpan>>(
&self,
sp: S,
msg: impl Into<DiagnosticMessage>,
) -> ErrorGuaranteed {
self.diagnostic().span_err(sp, msg) self.diagnostic().span_err(sp, msg)
} }
pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) { pub fn span_err_with_code<S: Into<MultiSpan>>(
self.diagnostic().span_err_with_code(sp, &msg, code) &self,
sp: S,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) {
self.diagnostic().span_err_with_code(sp, msg, code)
} }
pub fn err(&self, msg: &str) -> ErrorGuaranteed { pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
self.diagnostic().err(msg) self.diagnostic().err(msg)
} }
pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed { pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed {
@ -423,25 +443,34 @@ impl Session {
Err(ErrorGuaranteed::unchecked_claim_error_was_emitted()) Err(ErrorGuaranteed::unchecked_claim_error_was_emitted())
} }
} }
pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) {
self.diagnostic().span_warn(sp, msg) self.diagnostic().span_warn(sp, msg)
} }
pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) { pub fn span_warn_with_code<S: Into<MultiSpan>>(
&self,
sp: S,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) {
self.diagnostic().span_warn_with_code(sp, msg, code) self.diagnostic().span_warn_with_code(sp, msg, code)
} }
pub fn warn(&self, msg: &str) { pub fn warn(&self, msg: impl Into<DiagnosticMessage>) {
self.diagnostic().warn(msg) self.diagnostic().warn(msg)
} }
/// Delay a span_bug() call until abort_if_errors() /// Delay a span_bug() call until abort_if_errors()
#[track_caller] #[track_caller]
pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ErrorGuaranteed { pub fn delay_span_bug<S: Into<MultiSpan>>(
&self,
sp: S,
msg: impl Into<DiagnosticMessage>,
) -> ErrorGuaranteed {
self.diagnostic().delay_span_bug(sp, msg) self.diagnostic().delay_span_bug(sp, msg)
} }
/// Used for code paths of expensive computations that should only take place when /// Used for code paths of expensive computations that should only take place when
/// warnings or errors are emitted. If no messages are emitted ("good path"), then /// warnings or errors are emitted. If no messages are emitted ("good path"), then
/// it's likely a bug. /// it's likely a bug.
pub fn delay_good_path_bug(&self, msg: &str) { pub fn delay_good_path_bug(&self, msg: impl Into<DiagnosticMessage>) {
if self.opts.debugging_opts.print_type_sizes if self.opts.debugging_opts.print_type_sizes
|| self.opts.debugging_opts.query_dep_graph || self.opts.debugging_opts.query_dep_graph
|| self.opts.debugging_opts.dump_mir.is_some() || self.opts.debugging_opts.dump_mir.is_some()
@ -455,13 +484,20 @@ impl Session {
self.diagnostic().delay_good_path_bug(msg) self.diagnostic().delay_good_path_bug(msg)
} }
pub fn note_without_error(&self, msg: &str) { pub fn note_without_error(&self, msg: impl Into<DiagnosticMessage>) {
self.diagnostic().note_without_error(msg) self.diagnostic().note_without_error(msg)
} }
pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { pub fn span_note_without_error<S: Into<MultiSpan>>(
&self,
sp: S,
msg: impl Into<DiagnosticMessage>,
) {
self.diagnostic().span_note_without_error(sp, msg) self.diagnostic().span_note_without_error(sp, msg)
} }
pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { pub fn struct_note_without_error(
&self,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ()> {
self.diagnostic().struct_note_without_error(msg) self.diagnostic().struct_note_without_error(msg)
} }
@ -1033,6 +1069,7 @@ fn default_emitter(
sopts: &config::Options, sopts: &config::Options,
registry: rustc_errors::registry::Registry, registry: rustc_errors::registry::Registry,
source_map: Lrc<SourceMap>, source_map: Lrc<SourceMap>,
fallback_bundle: Lrc<FluentBundle>,
emitter_dest: Option<Box<dyn Write + Send>>, emitter_dest: Option<Box<dyn Write + Send>>,
) -> Box<dyn Emitter + sync::Send> { ) -> Box<dyn Emitter + sync::Send> {
let macro_backtrace = sopts.debugging_opts.macro_backtrace; let macro_backtrace = sopts.debugging_opts.macro_backtrace;
@ -1041,14 +1078,19 @@ fn default_emitter(
let (short, color_config) = kind.unzip(); let (short, color_config) = kind.unzip();
if let HumanReadableErrorType::AnnotateSnippet(_) = kind { if let HumanReadableErrorType::AnnotateSnippet(_) = kind {
let emitter = let emitter = AnnotateSnippetEmitterWriter::new(
AnnotateSnippetEmitterWriter::new(Some(source_map), short, macro_backtrace); Some(source_map),
fallback_bundle,
short,
macro_backtrace,
);
Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing)) Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing))
} else { } else {
let emitter = match dst { let emitter = match dst {
None => EmitterWriter::stderr( None => EmitterWriter::stderr(
color_config, color_config,
Some(source_map), Some(source_map),
fallback_bundle,
short, short,
sopts.debugging_opts.teach, sopts.debugging_opts.teach,
sopts.debugging_opts.terminal_width, sopts.debugging_opts.terminal_width,
@ -1057,6 +1099,7 @@ fn default_emitter(
Some(dst) => EmitterWriter::new( Some(dst) => EmitterWriter::new(
dst, dst,
Some(source_map), Some(source_map),
fallback_bundle,
short, short,
false, // no teach messages when writing to a buffer false, // no teach messages when writing to a buffer
false, // no colors when writing to a buffer false, // no colors when writing to a buffer
@ -1071,6 +1114,7 @@ fn default_emitter(
JsonEmitter::stderr( JsonEmitter::stderr(
Some(registry), Some(registry),
source_map, source_map,
fallback_bundle,
pretty, pretty,
json_rendered, json_rendered,
sopts.debugging_opts.terminal_width, sopts.debugging_opts.terminal_width,
@ -1083,6 +1127,7 @@ fn default_emitter(
dst, dst,
Some(registry), Some(registry),
source_map, source_map,
fallback_bundle,
pretty, pretty,
json_rendered, json_rendered,
sopts.debugging_opts.terminal_width, sopts.debugging_opts.terminal_width,
@ -1152,7 +1197,10 @@ pub fn build_session(
sopts.file_path_mapping(), sopts.file_path_mapping(),
hash_kind, hash_kind,
)); ));
let emitter = default_emitter(&sopts, registry, source_map.clone(), write_dest);
let fallback_bundle = fallback_fluent_bundle();
let emitter =
default_emitter(&sopts, registry, source_map.clone(), fallback_bundle.clone(), write_dest);
let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags( let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags(
emitter, emitter,
@ -1385,13 +1433,22 @@ pub enum IncrCompSession {
} }
fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler { fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler {
let fallback_bundle = fallback_fluent_bundle();
let emitter: Box<dyn Emitter + sync::Send> = match output { let emitter: Box<dyn Emitter + sync::Send> = match output {
config::ErrorOutputType::HumanReadable(kind) => { config::ErrorOutputType::HumanReadable(kind) => {
let (short, color_config) = kind.unzip(); let (short, color_config) = kind.unzip();
Box::new(EmitterWriter::stderr(color_config, None, short, false, None, false)) Box::new(EmitterWriter::stderr(
color_config,
None,
fallback_bundle,
short,
false,
None,
false,
))
} }
config::ErrorOutputType::Json { pretty, json_rendered } => { config::ErrorOutputType::Json { pretty, json_rendered } => {
Box::new(JsonEmitter::basic(pretty, json_rendered, None, false)) Box::new(JsonEmitter::basic(pretty, json_rendered, fallback_bundle, None, false))
} }
}; };
rustc_errors::Handler::with_emitter(true, None, emitter) rustc_errors::Handler::with_emitter(true, None, emitter)

View file

@ -597,7 +597,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
Some(format!("{}", name)) Some(format!("{}", name))
} }
_ => { _ => {
err.note(&msg); err.note(msg);
None None
} }
} }
@ -2481,7 +2481,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
.opt_associated_item(trait_item_def_id) .opt_associated_item(trait_item_def_id)
.and_then(|i| self.tcx.opt_item_name(i.container.id())) .and_then(|i| self.tcx.opt_item_name(i.container.id()))
{ {
assoc_span.push_span_label(ident.span, "in this trait".into()); assoc_span.push_span_label(ident.span, "in this trait");
} }
err.span_note(assoc_span, &msg); err.span_note(assoc_span, &msg);
} }
@ -2506,7 +2506,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
.opt_associated_item(trait_item_def_id) .opt_associated_item(trait_item_def_id)
.and_then(|i| self.tcx.opt_item_name(i.container.id())) .and_then(|i| self.tcx.opt_item_name(i.container.id()))
{ {
assoc_span.push_span_label(ident.span, "in this trait".into()); assoc_span.push_span_label(ident.span, "in this trait");
} }
err.span_note(assoc_span, &msg); err.span_note(assoc_span, &msg);
} }

View file

@ -169,10 +169,7 @@ fn lint_object_unsafe_trait(
let node = tcx.hir().get_if_local(trait_def_id); let node = tcx.hir().get_if_local(trait_def_id);
let mut spans = MultiSpan::from_span(span); let mut spans = MultiSpan::from_span(span);
if let Some(hir::Node::Item(item)) = node { if let Some(hir::Node::Item(item)) = node {
spans.push_span_label( spans.push_span_label(item.ident.span, "this trait cannot be made into an object...");
item.ident.span,
"this trait cannot be made into an object...".into(),
);
spans.push_span_label(span, format!("...because {}", violation.error_msg())); spans.push_span_label(span, format!("...because {}", violation.error_msg()));
} else { } else {
spans.push_span_label( spans.push_span_label(

View file

@ -143,6 +143,7 @@ crate fn new_handler(
source_map: Option<Lrc<source_map::SourceMap>>, source_map: Option<Lrc<source_map::SourceMap>>,
debugging_opts: &DebuggingOptions, debugging_opts: &DebuggingOptions,
) -> rustc_errors::Handler { ) -> rustc_errors::Handler {
let fallback_bundle = rustc_errors::fallback_fluent_bundle();
let emitter: Box<dyn Emitter + sync::Send> = match error_format { let emitter: Box<dyn Emitter + sync::Send> = match error_format {
ErrorOutputType::HumanReadable(kind) => { ErrorOutputType::HumanReadable(kind) => {
let (short, color_config) = kind.unzip(); let (short, color_config) = kind.unzip();
@ -150,6 +151,7 @@ crate fn new_handler(
EmitterWriter::stderr( EmitterWriter::stderr(
color_config, color_config,
source_map.map(|sm| sm as _), source_map.map(|sm| sm as _),
fallback_bundle,
short, short,
debugging_opts.teach, debugging_opts.teach,
debugging_opts.terminal_width, debugging_opts.terminal_width,
@ -166,6 +168,7 @@ crate fn new_handler(
JsonEmitter::stderr( JsonEmitter::stderr(
None, None,
source_map, source_map,
fallback_bundle,
pretty, pretty,
json_rendered, json_rendered,
debugging_opts.terminal_width, debugging_opts.terminal_width,

View file

@ -537,12 +537,28 @@ crate fn make_test(
// Any errors in parsing should also appear when the doctest is compiled for real, so just // Any errors in parsing should also appear when the doctest is compiled for real, so just
// send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr.
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
supports_color = let fallback_bundle = rustc_errors::fallback_fluent_bundle();
EmitterWriter::stderr(ColorConfig::Auto, None, false, false, Some(80), false) supports_color = EmitterWriter::stderr(
ColorConfig::Auto,
None,
fallback_bundle.clone(),
false,
false,
Some(80),
false,
)
.supports_color(); .supports_color();
let emitter = let emitter = EmitterWriter::new(
EmitterWriter::new(box io::sink(), None, false, false, false, None, false); box io::sink(),
None,
fallback_bundle,
false,
false,
false,
None,
false,
);
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
let handler = Handler::with_emitter(false, None, box emitter); let handler = Handler::with_emitter(false, None, box emitter);

View file

@ -32,7 +32,8 @@ struct SyntaxChecker<'a, 'tcx> {
impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> {
fn check_rust_syntax(&self, item: &clean::Item, dox: &str, code_block: RustCodeBlock) { fn check_rust_syntax(&self, item: &clean::Item, dox: &str, code_block: RustCodeBlock) {
let buffer = Lrc::new(Lock::new(Buffer::default())); let buffer = Lrc::new(Lock::new(Buffer::default()));
let emitter = BufferEmitter { buffer: Lrc::clone(&buffer) }; let fallback_bundle = rustc_errors::fallback_fluent_bundle();
let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle };
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
let handler = Handler::with_emitter(false, None, Box::new(emitter)); let handler = Handler::with_emitter(false, None, Box::new(emitter));
@ -171,12 +172,14 @@ struct Buffer {
struct BufferEmitter { struct BufferEmitter {
buffer: Lrc<Lock<Buffer>>, buffer: Lrc<Lock<Buffer>>,
fallback_bundle: Lrc<rustc_errors::FluentBundle>,
} }
impl Emitter for BufferEmitter { impl Emitter for BufferEmitter {
fn emit_diagnostic(&mut self, diag: &Diagnostic) { fn emit_diagnostic(&mut self, diag: &Diagnostic) {
let mut buffer = self.buffer.borrow_mut(); let mut buffer = self.buffer.borrow_mut();
buffer.messages.push(format!("error from rustc: {}", diag.message[0].0.as_str())); // FIXME(davidtwco): need to support translation here eventually
buffer.messages.push(format!("error from rustc: {}", diag.message[0].0.expect_str()));
if diag.is_error() { if diag.is_error() {
buffer.has_errors = true; buffer.has_errors = true;
} }
@ -185,4 +188,12 @@ impl Emitter for BufferEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> { fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None None
} }
fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
None
}
fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> {
&self.fallback_bundle
}
} }

View file

@ -130,8 +130,8 @@ fn check_arm<'tcx>(
&msg, &msg,
|diag| { |diag| {
let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]);
help_span.push_span_label(binding_span, "replace this binding".into()); help_span.push_span_label(binding_span, "replace this binding");
help_span.push_span_label(inner_then_pat.span, "with this pattern".into()); help_span.push_span_label(inner_then_pat.span, "with this pattern");
diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern");
}, },
); );

View file

@ -621,7 +621,17 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
let filename = FileName::anon_source_code(&code); let filename = FileName::anon_source_code(&code);
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
let emitter = EmitterWriter::new(Box::new(io::sink()), None, false, false, false, None, false); let fallback_bundle = rustc_errors::fallback_fluent_bundle();
let emitter = EmitterWriter::new(
Box::new(io::sink()),
None,
fallback_bundle,
false,
false,
false,
None,
false,
);
let handler = Handler::with_emitter(false, None, Box::new(emitter)); let handler = Handler::with_emitter(false, None, Box::new(emitter));
let sess = ParseSess::with_span_handler(handler, sm); let sess = ParseSess::with_span_handler(handler, sm);

View file

@ -102,7 +102,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo
// Suggest replacing iter_call with iter_replacement, and removing stmt // Suggest replacing iter_call with iter_replacement, and removing stmt
let mut span = MultiSpan::from_span(method_name.ident.span); let mut span = MultiSpan::from_span(method_name.ident.span);
span.push_span_label(iter_call.span, "the iterator could be used here instead".into()); span.push_span_label(iter_call.span, "the iterator could be used here instead");
span_lint_hir_and_then( span_lint_hir_and_then(
cx, cx,
super::NEEDLESS_COLLECT, super::NEEDLESS_COLLECT,

View file

@ -148,7 +148,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) { if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) {
if cx.tcx.is_const_fn_raw(def_id.to_def_id()) { if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
cx.tcx.sess.span_err(span, &err); cx.tcx.sess.span_err(span, err.as_ref());
} }
} else { } else {
span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`"); span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");

View file

@ -235,11 +235,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
for (span, suggestion) in clone_spans { for (span, suggestion) in clone_spans {
diag.span_suggestion( diag.span_suggestion(
span, span,
&snippet_opt(cx, span) snippet_opt(cx, span)
.map_or( .map_or(
"change the call to".into(), "change the call to".into(),
|x| Cow::from(format!("change `{}` to", x)), |x| Cow::from(format!("change `{}` to", x)),
), )
.as_ref(),
suggestion.into(), suggestion.into(),
Applicability::Unspecified, Applicability::Unspecified,
); );
@ -264,11 +265,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
for (span, suggestion) in clone_spans { for (span, suggestion) in clone_spans {
diag.span_suggestion( diag.span_suggestion(
span, span,
&snippet_opt(cx, span) snippet_opt(cx, span)
.map_or( .map_or(
"change the call to".into(), "change the call to".into(),
|x| Cow::from(format!("change `{}` to", x)) |x| Cow::from(format!("change `{}` to", x))
), )
.as_ref(),
suggestion.into(), suggestion.into(),
Applicability::Unspecified, Applicability::Unspecified,
); );

View file

@ -165,9 +165,11 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
// Separate the output with an empty line // Separate the output with an empty line
eprintln!(); eprintln!();
let fallback_bundle = rustc_errors::fallback_fluent_bundle();
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
rustc_errors::ColorConfig::Auto, rustc_errors::ColorConfig::Auto,
None, None,
fallback_bundle,
false, false,
false, false,
None, None,
@ -191,7 +193,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
]; ];
for note in &xs { for note in &xs {
handler.note_without_error(note); handler.note_without_error(note.as_ref());
} }
// If backtraces are enabled, also print the query stack // If backtraces are enabled, also print the query stack

View file

@ -33,6 +33,12 @@ impl Emitter for SilentEmitter {
None None
} }
fn emit_diagnostic(&mut self, _db: &Diagnostic) {} fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
None
}
fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> {
panic!("silent emitter attempted to translate a diagnostic");
}
} }
fn silent_emitter() -> Box<dyn Emitter + Send> { fn silent_emitter() -> Box<dyn Emitter + Send> {
@ -82,6 +88,14 @@ impl Emitter for SilentOnIgnoredFilesEmitter {
} }
self.handle_non_ignoreable_error(db); self.handle_non_ignoreable_error(db);
} }
fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
self.emitter.fluent_bundle()
}
fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> {
self.emitter.fallback_fluent_bundle()
}
} }
fn default_handler( fn default_handler(
@ -100,9 +114,11 @@ fn default_handler(
let emitter = if hide_parse_errors { let emitter = if hide_parse_errors {
silent_emitter() silent_emitter()
} else { } else {
let fallback_bundle = rustc_errors::fallback_fluent_bundle();
Box::new(EmitterWriter::stderr( Box::new(EmitterWriter::stderr(
color_cfg, color_cfg,
Some(source_map.clone()), Some(source_map.clone()),
fallback_bundle,
false, false,
false, false,
None, None,
@ -329,6 +345,12 @@ mod tests {
fn emit_diagnostic(&mut self, _db: &Diagnostic) { fn emit_diagnostic(&mut self, _db: &Diagnostic) {
self.num_emitted_errors.fetch_add(1, Ordering::Release); self.num_emitted_errors.fetch_add(1, Ordering::Release);
} }
fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
None
}
fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> {
panic!("test emitter attempted to translate a diagnostic");
}
} }
fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic { fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {