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",
]
[[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]]
name = "fnv"
version = "1.0.7"
@ -1782,6 +1826,26 @@ dependencies = [
"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]]
name = "itertools"
version = "0.10.1"
@ -2812,6 +2876,12 @@ dependencies = [
"version_check",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro2"
version = "1.0.30"
@ -3649,9 +3719,13 @@ version = "0.0.0"
name = "rustc_error_messages"
version = "0.0.0"
dependencies = [
"fluent",
"rustc_data_structures",
"rustc_macros",
"rustc_serialize",
"rustc_span",
"tracing",
"unic-langid",
]
[[package]]
@ -4585,6 +4659,12 @@ dependencies = [
"libc",
]
[[package]]
name = "self_cell"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af"
[[package]]
name = "semver"
version = "1.0.3"
@ -5116,6 +5196,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "tinystr"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29738eedb4388d9ea620eeab9384884fc3f06f586a2eddb56bedc5885126c7c1"
[[package]]
name = "tinyvec"
version = "0.3.4"
@ -5274,6 +5360,15 @@ dependencies = [
"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]]
name = "typenum"
version = "1.12.0"
@ -5328,6 +5423,49 @@ dependencies = [
"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]]
name = "unic-ucd-version"
version = "0.9.0"

View File

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

View File

@ -1707,23 +1707,33 @@ impl SharedEmitter {
impl Emitter for SharedEmitter {
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 {
msg: diag.message().to_string(),
msg: self.translate_messages(&diag.message, &fluent_args).to_string(),
code: diag.code.clone(),
lvl: diag.level(),
})));
for child in &diag.children {
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
msg: child.message().to_string(),
msg: self.translate_messages(&child.message, &fluent_args).to_string(),
code: None,
lvl: child.level,
})));
}
drop(self.sender.send(SharedEmitterMessage::AbortIfErrors));
}
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
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 {
@ -1754,9 +1764,9 @@ impl SharedEmitterMain {
let msg = msg.strip_prefix("error: ").unwrap_or(&msg);
let mut err = match level {
Level::Error { lint: false } => sess.struct_err(&msg).forget_guarantee(),
Level::Warning => sess.struct_warn(&msg),
Level::Note => sess.struct_note_without_error(&msg),
Level::Error { lint: false } => sess.struct_err(msg).forget_guarantee(),
Level::Warning => sess.struct_warn(msg),
Level::Note => sess.struct_note_without_error(msg),
_ => 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
/// hook.
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(
rustc_errors::ColorConfig::Auto,
None,
fallback_bundle,
false,
false,
None,
@ -1209,7 +1211,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
}
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

View File

@ -7,6 +7,10 @@ edition = "2021"
doctest = false
[dependencies]
fluent = "0.16.0"
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_span = { path = "../rustc_span" }
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_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
/// diagnostic messages.
///
/// Intended to be removed once diagnostics are entirely translatable.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
pub enum DiagnosticMessage {
/// Non-translatable diagnostic message.
// FIXME(davidtwco): can a `Cow<'static, str>` be used here?
Str(String),
/// Identifier for a Fluent message corresponding to the diagnostic message.
FluentIdentifier(String),
FluentIdentifier(FluentId, Option<FluentId>),
}
impl DiagnosticMessage {
/// Convert `DiagnosticMessage` to a `&str`.
pub fn as_str(&self) -> &str {
/// Returns the `String` contained within the `DiagnosticMessage::Str` variant, assuming that
/// 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 {
DiagnosticMessage::Str(msg) => msg,
DiagnosticMessage::FluentIdentifier(..) => unimplemented!(),
DiagnosticMessage::Str(s) => s,
_ => panic!("expected non-translatable diagnostic message"),
}
}
/// Convert `DiagnosticMessage` to an owned `String`.
pub fn to_string(self) -> String {
match self {
DiagnosticMessage::Str(msg) => msg,
DiagnosticMessage::FluentIdentifier(..) => unimplemented!(),
}
/// Create a `DiagnosticMessage` for the provided Fluent identifier.
pub fn fluent(id: impl Into<Cow<'static, str>>) -> Self {
DiagnosticMessage::FluentIdentifier(id.into(), None)
}
/// 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![] }
}
pub fn push_span_label(&mut self, span: Span, label: String) {
self.span_labels.push((span, DiagnosticMessage::Str(label)));
}
pub fn push_span_message(&mut self, span: Span, message: DiagnosticMessage) {
self.span_labels.push((span, message));
pub fn push_span_label(&mut self, span: Span, label: impl Into<DiagnosticMessage>) {
self.span_labels.push((span, label.into()));
}
/// Selects the first primary span (if any).

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -250,7 +250,7 @@ fn check_binders(
if let Some(prev_info) = binders.get(&name) {
// 1. The meta-variable is already bound in the current LHS: This is an error.
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");
} else if get_binder_info(macros, binders, name).is_none() {
// 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() {
if i >= occurrence_ops.len() {
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);
buffer_lint(sess, span, node_id, message);
return;
@ -630,8 +630,8 @@ fn ops_is_prefix(
let occurrence = &occurrence_ops[i];
if occurrence.op != binder.op {
let mut span = MultiSpan::from_span(span);
span.push_span_label(binder.span, "expected repetition".into());
span.push_span_label(occurrence.span, "conflicting repetition".into());
span.push_span_label(binder.span, "expected repetition");
span.push_span_label(occurrence.span, "conflicting repetition");
let message = "meta-variable repeats with different Kleene operator";
buffer_lint(sess, span, node_id, message);
return;

View File

@ -69,7 +69,7 @@ fn emit_frag_parse_err(
kind: AstFragmentKind,
) {
// 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() {
// early end of macro arm (#52866)
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] = (
rustc_errors::DiagnosticMessage::Str(format!(
"macro expansion ends with an incomplete expression: {}",
msg.0.as_str().replace(", found `<eof>`", ""),
msg.0.expect_str().replace(", found `<eof>`", ""),
)),
msg.1,
);

View File

@ -128,13 +128,15 @@ fn parse_ident<'sess>(
sess: &'sess ParseSess,
span: Span,
) -> 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((elem, false)) = token.ident() {
return Ok(elem);
}
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(
token.span,
&format!("try removing `{}`", &token_str),
@ -143,7 +145,7 @@ fn parse_ident<'sess>(
);
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

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(|_| {
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()));
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(
Box::new(Shared { data: output.clone() }),
Some(source_map.clone()),
fallback_bundle,
false,
false,
false,

View File

@ -2075,7 +2075,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
{
diag.span_suggestion(
span,
msg,
*msg,
format!("{}.as_ref()", snippet),
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");
// FIXME: we should point at the lifetime
let mut multi_span: MultiSpan = vec![binding_span].into();
multi_span
.push_span_label(binding_span, "introduces a `'static` lifetime requirement".into());
multi_span.push_span_label(binding_span, "introduces a `'static` 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));
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> {
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 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);

View File

@ -1571,7 +1571,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
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, \
and should be removed";
err.multipart_suggestion(&msg, suggestion, Applicability::MachineApplicable);
err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable);
if !suggested_changing_assoc_types {
TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
suggested_changing_assoc_types = true;

View File

@ -37,7 +37,7 @@ fn emit_unfulfilled_expectation_lint(
|diag| {
let mut diag = diag.build("this lint expectation is unfulfilled");
if let Some(rationale) = expectation.reason {
diag.note(&rationale.as_str());
diag.note(rationale.as_str());
}
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);
err.span_suggestion(
parent_expr.span,
&"use an inclusive range instead",
"use an inclusive range instead",
suggestion,
Applicability::MachineApplicable,
);
@ -399,7 +399,7 @@ fn lint_uint_literal<'tcx>(
lint.build("only `u8` can be cast into `char`")
.span_suggestion(
par_e.span,
&"use a `char` literal instead",
"use a `char` literal instead",
format!("'\\u{{{:X}}}'", lit_val),
Applicability::MachineApplicable,
)

View File

@ -18,7 +18,9 @@ use rustc_ast::{
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet;
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::symbol::{kw, Ident};
use rustc_span::{Span, SpanSnippetError, DUMMY_SP};
@ -273,12 +275,12 @@ impl<'a> Parser<'a> {
pub fn struct_span_err<S: Into<MultiSpan>>(
&self,
sp: S,
m: &str,
m: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
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)
}
@ -584,16 +586,22 @@ impl<'a> Parser<'a> {
// field: value,
// } }
err.delay_as_bug();
self.struct_span_err(expr.span, "struct literal body without path")
.multipart_suggestion(
"you might have forgotten to add the struct literal inside the block",
vec![
(expr.span.shrink_to_lo(), "{ SomeStruct ".to_string()),
(expr.span.shrink_to_hi(), " }".to_string()),
],
Applicability::MaybeIncorrect,
)
.emit();
self.struct_span_err(
expr.span,
DiagnosticMessage::fluent("parser-struct-literal-body-without-path"),
)
.multipart_suggestion(
DiagnosticMessage::fluent_attr(
"parser-struct-literal-body-without-path",
"suggestion",
),
vec![
(expr.span.shrink_to_lo(), "{ SomeStruct ".to_string()),
(expr.span.shrink_to_hi(), " }".to_string()),
],
Applicability::MaybeIncorrect,
)
.emit();
self.restore_snapshot(snapshot);
let mut tail = self.mk_block(
vec![self.mk_stmt_err(expr.span)],
@ -1752,7 +1760,7 @@ impl<'a> Parser<'a> {
let mut primary_span: MultiSpan = primary_span.into();
for span_label in err.span.span_labels() {
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);

View File

@ -1703,7 +1703,7 @@ impl<'a> Parser<'a> {
if matches!(expr.kind, ExprKind::Err) {
let mut err = self
.diagnostic()
.struct_span_err(self.token.span, &"invalid interpolated expression");
.struct_span_err(self.token.span, "invalid interpolated expression");
err.downgrade_to_delayed_bug();
return err;
}
@ -1820,7 +1820,7 @@ impl<'a> Parser<'a> {
} else if let Some(fixed) = fix_base_capitalisation(suf) {
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")
.span_suggestion(
span,

View File

@ -1011,7 +1011,7 @@ impl<'a> Parser<'a> {
let current_qual_sp = current_qual_sp.to(sp_start);
if let Ok(current_qual) = self.span_to_snippet(current_qual_sp) {
// 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 = 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 {
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))
.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("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")
.emit();
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 mut note_span = MultiSpan::from_span(def_span);
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);
}

View File

@ -739,7 +739,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
if let Some((_, UnresolvedImportError { note, .. })) = errors.iter().last() {
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(
span,
&"use this syntax instead",
"use this syntax instead",
path_str.to_string(),
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> {
fn encode(&self, s: &mut S) -> Result<(), S::Error> {
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_errors::{emitter::SilentEmitter, ColorConfig, Handler};
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_span::edition::Edition;
@ -173,8 +174,15 @@ pub struct ParseSess {
impl ParseSess {
/// Used for testing.
pub fn new(file_path_mapping: FilePathMapping) -> Self {
let fallback_bundle = fallback_fluent_bundle();
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)
}
@ -203,8 +211,10 @@ impl ParseSess {
}
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 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(
false,
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::json::JsonEmitter;
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;
pub use rustc_span::def_id::StableCrateId;
use rustc_span::edition::Edition;
@ -279,34 +282,34 @@ impl Session {
pub fn struct_span_warn<S: Into<MultiSpan>>(
&self,
sp: S,
msg: &str,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ()> {
self.diagnostic().struct_span_warn(sp, msg)
}
pub fn struct_span_warn_with_code<S: Into<MultiSpan>>(
&self,
sp: S,
msg: &str,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) -> DiagnosticBuilder<'_, ()> {
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)
}
pub fn struct_span_allow<S: Into<MultiSpan>>(
&self,
sp: S,
msg: &str,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ()> {
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)
}
pub fn struct_expect(
&self,
msg: &str,
msg: impl Into<DiagnosticMessage>,
id: lint::LintExpectationId,
) -> DiagnosticBuilder<'_, ()> {
self.diagnostic().struct_expect(msg, id)
@ -314,25 +317,28 @@ impl Session {
pub fn struct_span_err<S: Into<MultiSpan>>(
&self,
sp: S,
msg: &str,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
self.diagnostic().struct_span_err(sp, msg)
}
pub fn struct_span_err_with_code<S: Into<MultiSpan>>(
&self,
sp: S,
msg: &str,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
self.diagnostic().struct_span_err_with_code(sp, msg, 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)
}
pub fn struct_err_with_code(
&self,
msg: &str,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
self.diagnostic().struct_err_with_code(msg, code)
@ -340,50 +346,64 @@ impl Session {
pub fn struct_span_fatal<S: Into<MultiSpan>>(
&self,
sp: S,
msg: &str,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, !> {
self.diagnostic().struct_span_fatal(sp, msg)
}
pub fn struct_span_fatal_with_code<S: Into<MultiSpan>>(
&self,
sp: S,
msg: &str,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) -> DiagnosticBuilder<'_, !> {
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)
}
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)
}
pub fn span_fatal_with_code<S: Into<MultiSpan>>(
&self,
sp: S,
msg: &str,
msg: impl Into<DiagnosticMessage>,
code: DiagnosticId,
) -> ! {
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()
}
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 {
self.span_warn(sp, msg);
} else {
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)
}
pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) {
self.diagnostic().span_err_with_code(sp, &msg, code)
pub fn span_err_with_code<S: Into<MultiSpan>>(
&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)
}
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())
}
}
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)
}
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)
}
pub fn warn(&self, msg: &str) {
pub fn warn(&self, msg: impl Into<DiagnosticMessage>) {
self.diagnostic().warn(msg)
}
/// Delay a span_bug() call until abort_if_errors()
#[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)
}
/// 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
/// 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
|| self.opts.debugging_opts.query_dep_graph
|| self.opts.debugging_opts.dump_mir.is_some()
@ -455,13 +484,20 @@ impl Session {
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)
}
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)
}
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)
}
@ -1033,6 +1069,7 @@ fn default_emitter(
sopts: &config::Options,
registry: rustc_errors::registry::Registry,
source_map: Lrc<SourceMap>,
fallback_bundle: Lrc<FluentBundle>,
emitter_dest: Option<Box<dyn Write + Send>>,
) -> Box<dyn Emitter + sync::Send> {
let macro_backtrace = sopts.debugging_opts.macro_backtrace;
@ -1041,14 +1078,19 @@ fn default_emitter(
let (short, color_config) = kind.unzip();
if let HumanReadableErrorType::AnnotateSnippet(_) = kind {
let emitter =
AnnotateSnippetEmitterWriter::new(Some(source_map), short, macro_backtrace);
let emitter = AnnotateSnippetEmitterWriter::new(
Some(source_map),
fallback_bundle,
short,
macro_backtrace,
);
Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing))
} else {
let emitter = match dst {
None => EmitterWriter::stderr(
color_config,
Some(source_map),
fallback_bundle,
short,
sopts.debugging_opts.teach,
sopts.debugging_opts.terminal_width,
@ -1057,6 +1099,7 @@ fn default_emitter(
Some(dst) => EmitterWriter::new(
dst,
Some(source_map),
fallback_bundle,
short,
false, // no teach messages when writing to a buffer
false, // no colors when writing to a buffer
@ -1071,6 +1114,7 @@ fn default_emitter(
JsonEmitter::stderr(
Some(registry),
source_map,
fallback_bundle,
pretty,
json_rendered,
sopts.debugging_opts.terminal_width,
@ -1083,6 +1127,7 @@ fn default_emitter(
dst,
Some(registry),
source_map,
fallback_bundle,
pretty,
json_rendered,
sopts.debugging_opts.terminal_width,
@ -1152,7 +1197,10 @@ pub fn build_session(
sopts.file_path_mapping(),
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(
emitter,
@ -1385,13 +1433,22 @@ pub enum IncrCompSession {
}
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 {
config::ErrorOutputType::HumanReadable(kind) => {
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 } => {
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)

View File

@ -597,7 +597,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
Some(format!("{}", name))
}
_ => {
err.note(&msg);
err.note(msg);
None
}
}
@ -2481,7 +2481,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
.opt_associated_item(trait_item_def_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);
}
@ -2506,7 +2506,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
.opt_associated_item(trait_item_def_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);
}

View File

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

View File

@ -143,6 +143,7 @@ crate fn new_handler(
source_map: Option<Lrc<source_map::SourceMap>>,
debugging_opts: &DebuggingOptions,
) -> rustc_errors::Handler {
let fallback_bundle = rustc_errors::fallback_fluent_bundle();
let emitter: Box<dyn Emitter + sync::Send> = match error_format {
ErrorOutputType::HumanReadable(kind) => {
let (short, color_config) = kind.unzip();
@ -150,6 +151,7 @@ crate fn new_handler(
EmitterWriter::stderr(
color_config,
source_map.map(|sm| sm as _),
fallback_bundle,
short,
debugging_opts.teach,
debugging_opts.terminal_width,
@ -166,6 +168,7 @@ crate fn new_handler(
JsonEmitter::stderr(
None,
source_map,
fallback_bundle,
pretty,
json_rendered,
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
// send all the errors that librustc_ast emits directly into a `Sink` instead of stderr.
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
supports_color =
EmitterWriter::stderr(ColorConfig::Auto, None, false, false, Some(80), false)
.supports_color();
let fallback_bundle = rustc_errors::fallback_fluent_bundle();
supports_color = EmitterWriter::stderr(
ColorConfig::Auto,
None,
fallback_bundle.clone(),
false,
false,
Some(80),
false,
)
.supports_color();
let emitter =
EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
let emitter = EmitterWriter::new(
box io::sink(),
None,
fallback_bundle,
false,
false,
false,
None,
false,
);
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
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> {
fn check_rust_syntax(&self, item: &clean::Item, dox: &str, code_block: RustCodeBlock) {
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 handler = Handler::with_emitter(false, None, Box::new(emitter));
@ -171,12 +172,14 @@ struct Buffer {
struct BufferEmitter {
buffer: Lrc<Lock<Buffer>>,
fallback_bundle: Lrc<rustc_errors::FluentBundle>,
}
impl Emitter for BufferEmitter {
fn emit_diagnostic(&mut self, diag: &Diagnostic) {
let mut buffer = self.buffer.borrow_mut();
buffer.messages.push(format!("error from rustc: {}", diag.message[0].0.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() {
buffer.has_errors = true;
}
@ -185,4 +188,12 @@ impl Emitter for BufferEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
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,
|diag| {
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(inner_then_pat.span, "with this pattern".into());
help_span.push_span_label(binding_span, "replace this binding");
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");
},
);

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 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 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
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(
cx,
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 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 {
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 {
diag.span_suggestion(
span,
&snippet_opt(cx, span)
snippet_opt(cx, span)
.map_or(
"change the call to".into(),
|x| Cow::from(format!("change `{}` to", x)),
),
)
.as_ref(),
suggestion.into(),
Applicability::Unspecified,
);
@ -264,11 +265,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
for (span, suggestion) in clone_spans {
diag.span_suggestion(
span,
&snippet_opt(cx, span)
snippet_opt(cx, span)
.map_or(
"change the call to".into(),
|x| Cow::from(format!("change `{}` to", x))
),
)
.as_ref(),
suggestion.into(),
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
eprintln!();
let fallback_bundle = rustc_errors::fallback_fluent_bundle();
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
rustc_errors::ColorConfig::Auto,
None,
fallback_bundle,
false,
false,
None,
@ -191,7 +193,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
];
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

View File

@ -33,6 +33,12 @@ impl Emitter for SilentEmitter {
None
}
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> {
@ -82,6 +88,14 @@ impl Emitter for SilentOnIgnoredFilesEmitter {
}
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(
@ -100,9 +114,11 @@ fn default_handler(
let emitter = if hide_parse_errors {
silent_emitter()
} else {
let fallback_bundle = rustc_errors::fallback_fluent_bundle();
Box::new(EmitterWriter::stderr(
color_cfg,
Some(source_map.clone()),
fallback_bundle,
false,
false,
None,
@ -329,6 +345,12 @@ mod tests {
fn emit_diagnostic(&mut self, _db: &Diagnostic) {
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 {