summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_errors
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_errors')
-rw-r--r--compiler/rustc_errors/Cargo.toml4
-rw-r--r--compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs7
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs82
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs16
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs34
-rw-r--r--compiler/rustc_errors/src/emitter.rs196
-rw-r--r--compiler/rustc_errors/src/json.rs10
-rw-r--r--compiler/rustc_errors/src/json/tests.rs1
-rw-r--r--compiler/rustc_errors/src/lib.rs71
-rw-r--r--compiler/rustc_errors/src/translation.rs44
10 files changed, 364 insertions, 101 deletions
diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml
index 7803a0792..dee7a31ec 100644
--- a/compiler/rustc_errors/Cargo.toml
+++ b/compiler/rustc_errors/Cargo.toml
@@ -18,7 +18,6 @@ rustc_target = { path = "../rustc_target" }
rustc_hir = { path = "../rustc_hir" }
rustc_lint_defs = { path = "../rustc_lint_defs" }
unicode-width = "0.1.4"
-atty = "0.2"
termcolor = "1.0"
annotate-snippets = "0.9"
termize = "0.1.1"
@@ -27,3 +26,6 @@ serde_json = "1.0.59"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = [ "handleapi", "synchapi", "winbase" ] }
+
+[features]
+rustc_use_parallel_compiler = ['rustc_error_messages/rustc_use_parallel_compiler']
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index f14b8ee32..d8879bf70 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -39,7 +39,7 @@ impl Translate for AnnotateSnippetEmitterWriter {
}
fn fallback_fluent_bundle(&self) -> &FluentBundle {
- &**self.fallback_bundle
+ &self.fallback_bundle
}
}
@@ -49,10 +49,9 @@ impl Emitter for AnnotateSnippetEmitterWriter {
let fluent_args = to_fluent_args(diag.args());
let mut children = diag.children.clone();
- let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args);
+ 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,
&mut primary_span,
&mut children,
&diag.level,
@@ -66,7 +65,7 @@ impl Emitter for AnnotateSnippetEmitterWriter {
&diag.code,
&primary_span,
&children,
- &suggestions,
+ suggestions,
);
}
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 23f29a24f..06bb5edc0 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -4,6 +4,7 @@ use crate::{
SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle,
};
use rustc_data_structures::fx::FxHashMap;
+use rustc_error_messages::fluent_value_from_str_list_sep_by_and;
use rustc_error_messages::FluentValue;
use rustc_lint_defs::{Applicability, LintExpectationId};
use rustc_span::edition::LATEST_STABLE_EDITION;
@@ -12,6 +13,7 @@ use rustc_span::{Span, DUMMY_SP};
use std::borrow::Cow;
use std::fmt;
use std::hash::{Hash, Hasher};
+use std::panic::Location;
/// Error type for `Diagnostic`'s `suggestions` field, indicating that
/// `.disable_suggestions()` was called on the `Diagnostic`.
@@ -33,6 +35,7 @@ pub type DiagnosticArgName<'source> = Cow<'source, str>;
pub enum DiagnosticArgValue<'source> {
Str(Cow<'source, str>),
Number(usize),
+ StrListSepByAnd(Vec<Cow<'source, str>>),
}
/// Converts a value of a type into a `DiagnosticArg` (typically a field of an `IntoDiagnostic`
@@ -43,19 +46,31 @@ pub trait IntoDiagnosticArg {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static>;
}
+impl<'source> IntoDiagnosticArg for DiagnosticArgValue<'source> {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ match self {
+ DiagnosticArgValue::Str(s) => DiagnosticArgValue::Str(Cow::Owned(s.into_owned())),
+ DiagnosticArgValue::Number(n) => DiagnosticArgValue::Number(n),
+ DiagnosticArgValue::StrListSepByAnd(l) => DiagnosticArgValue::StrListSepByAnd(
+ l.into_iter().map(|s| Cow::Owned(s.into_owned())).collect(),
+ ),
+ }
+ }
+}
+
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),
+ DiagnosticArgValue::StrListSepByAnd(l) => fluent_value_from_str_list_sep_by_and(l),
}
}
}
/// Trait implemented by error types. This should not be implemented manually. Instead, use
/// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic].
-#[cfg_attr(bootstrap, rustc_diagnostic_item = "AddSubdiagnostic")]
-#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "AddToDiagnostic")]
+#[rustc_diagnostic_item = "AddToDiagnostic"]
pub trait AddToDiagnostic
where
Self: Sized,
@@ -107,6 +122,31 @@ pub struct Diagnostic {
/// If diagnostic is from Lint, custom hash function ignores notes
/// otherwise hash is based on the all the fields
pub is_lint: bool,
+
+ /// With `-Ztrack_diagnostics` enabled,
+ /// we print where in rustc this error was emitted.
+ pub emitted_at: DiagnosticLocation,
+}
+
+#[derive(Clone, Debug, Encodable, Decodable)]
+pub struct DiagnosticLocation {
+ file: Cow<'static, str>,
+ line: u32,
+ col: u32,
+}
+
+impl DiagnosticLocation {
+ #[track_caller]
+ fn caller() -> Self {
+ let loc = Location::caller();
+ DiagnosticLocation { file: loc.file().into(), line: loc.line(), col: loc.column() }
+ }
+}
+
+impl fmt::Display for DiagnosticLocation {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}:{}:{}", self.file, self.line, self.col)
+ }
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
@@ -173,10 +213,28 @@ impl StringPart {
}
impl Diagnostic {
+ #[track_caller]
pub fn new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self {
Diagnostic::new_with_code(level, None, message)
}
+ #[track_caller]
+ pub fn new_with_messages(level: Level, messages: Vec<(DiagnosticMessage, Style)>) -> Self {
+ Diagnostic {
+ level,
+ message: messages,
+ code: None,
+ span: MultiSpan::new(),
+ children: vec![],
+ suggestions: Ok(vec![]),
+ args: Default::default(),
+ sort_span: DUMMY_SP,
+ is_lint: false,
+ emitted_at: DiagnosticLocation::caller(),
+ }
+ }
+
+ #[track_caller]
pub fn new_with_code<M: Into<DiagnosticMessage>>(
level: Level,
code: Option<DiagnosticId>,
@@ -192,6 +250,7 @@ impl Diagnostic {
args: Default::default(),
sort_span: DUMMY_SP,
is_lint: false,
+ emitted_at: DiagnosticLocation::caller(),
}
}
@@ -233,7 +292,7 @@ impl Diagnostic {
let lint_index = expectation_id.get_lint_index();
expectation_id.set_lint_index(None);
let mut stable_id = unstable_to_stable
- .get(&expectation_id)
+ .get(expectation_id)
.expect("each unstable `LintExpectationId` must have a matching stable id")
.normalize();
@@ -688,7 +747,7 @@ impl Diagnostic {
&mut self,
sp: Span,
msg: impl Into<SubdiagnosticMessage>,
- suggestions: impl Iterator<Item = String>,
+ suggestions: impl IntoIterator<Item = String>,
applicability: Applicability,
) -> &mut Self {
self.span_suggestions_with_style(
@@ -705,11 +764,11 @@ impl Diagnostic {
&mut self,
sp: Span,
msg: impl Into<SubdiagnosticMessage>,
- suggestions: impl Iterator<Item = String>,
+ suggestions: impl IntoIterator<Item = String>,
applicability: Applicability,
style: SuggestionStyle,
) -> &mut Self {
- let mut suggestions: Vec<_> = suggestions.collect();
+ let mut suggestions: Vec<_> = suggestions.into_iter().collect();
suggestions.sort();
debug_assert!(
@@ -736,10 +795,10 @@ impl Diagnostic {
pub fn multipart_suggestions(
&mut self,
msg: impl Into<SubdiagnosticMessage>,
- suggestions: impl Iterator<Item = Vec<(Span, String)>>,
+ suggestions: impl IntoIterator<Item = Vec<(Span, String)>>,
applicability: Applicability,
) -> &mut Self {
- let suggestions: Vec<_> = suggestions.collect();
+ let suggestions: Vec<_> = suggestions.into_iter().collect();
debug_assert!(
!(suggestions
.iter()
@@ -902,6 +961,13 @@ impl Diagnostic {
self
}
+ pub fn replace_args(
+ &mut self,
+ args: FxHashMap<DiagnosticArgName<'static>, DiagnosticArgValue<'static>>,
+ ) {
+ self.args = args;
+ }
+
pub fn styled_message(&self) -> &[(DiagnosticMessage, Style)] {
&self.message
}
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index 9b41234dc..a2ed98864 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -16,8 +16,7 @@ use std::thread::panicking;
/// Trait implemented by error types. This should not be implemented manually. Instead, use
/// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic].
-#[cfg_attr(bootstrap, rustc_diagnostic_item = "SessionDiagnostic")]
-#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "IntoDiagnostic")]
+#[rustc_diagnostic_item = "IntoDiagnostic"]
pub trait IntoDiagnostic<'a, T: EmissionGuarantee = ErrorGuaranteed> {
/// Write out as a diagnostic out of `Handler`.
#[must_use]
@@ -133,6 +132,7 @@ 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`].
+ #[track_caller]
pub(crate) fn new_guaranteeing_error<M: Into<DiagnosticMessage>, const L: Level>(
handler: &'a Handler,
message: M,
@@ -196,6 +196,7 @@ impl EmissionGuarantee for ErrorGuaranteed {
}
}
+ #[track_caller]
fn make_diagnostic_builder(
handler: &Handler,
msg: impl Into<DiagnosticMessage>,
@@ -209,6 +210,7 @@ impl EmissionGuarantee for ErrorGuaranteed {
impl<'a> DiagnosticBuilder<'a, ()> {
/// Convenience function for internal use, clients should use one of the
/// `struct_*` methods on [`Handler`].
+ #[track_caller]
pub(crate) fn new<M: Into<DiagnosticMessage>>(
handler: &'a Handler,
level: Level,
@@ -220,6 +222,7 @@ impl<'a> DiagnosticBuilder<'a, ()> {
/// Creates a new `DiagnosticBuilder` with an already constructed
/// diagnostic.
+ #[track_caller]
pub(crate) fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
debug!("Created new diagnostic");
Self {
@@ -308,6 +311,7 @@ impl EmissionGuarantee for Noted {
impl<'a> DiagnosticBuilder<'a, !> {
/// Convenience function for internal use, clients should use one of the
/// `struct_*` methods on [`Handler`].
+ #[track_caller]
pub(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)
@@ -477,9 +481,9 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
/// In the meantime, though, callsites are required to deal with the "bug"
/// locally in whichever way makes the most sense.
#[track_caller]
- pub fn delay_as_bug(&mut self) {
+ pub fn delay_as_bug(&mut self) -> G {
self.downgrade_to_delayed_bug();
- self.emit();
+ self.emit()
}
forward!(
@@ -594,13 +598,13 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
&mut self,
sp: Span,
msg: impl Into<SubdiagnosticMessage>,
- suggestions: impl Iterator<Item = String>,
+ suggestions: impl IntoIterator<Item = String>,
applicability: Applicability,
) -> &mut Self);
forward!(pub fn multipart_suggestions(
&mut self,
msg: impl Into<SubdiagnosticMessage>,
- suggestions: impl Iterator<Item = Vec<(Span, String)>>,
+ suggestions: impl IntoIterator<Item = Vec<(Span, String)>>,
applicability: Applicability,
) -> &mut Self);
forward!(pub fn span_suggestion_short(
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index 7640b2919..7155db32e 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -13,6 +13,7 @@ use std::borrow::Cow;
use std::fmt;
use std::num::ParseIntError;
use std::path::{Path, PathBuf};
+use std::process::ExitStatus;
pub struct DiagnosticArgFromDisplay<'a>(pub &'a dyn fmt::Display);
@@ -58,6 +59,7 @@ into_diagnostic_arg_using_display!(
i128,
u128,
std::io::Error,
+ std::boxed::Box<dyn std::error::Error>,
std::num::NonZeroU32,
hir::Target,
Edition,
@@ -66,7 +68,8 @@ into_diagnostic_arg_using_display!(
ParseIntError,
StackProtector,
&TargetTriple,
- SplitDebuginfo
+ SplitDebuginfo,
+ ExitStatus,
);
impl IntoDiagnosticArg for bool {
@@ -103,6 +106,12 @@ impl IntoDiagnosticArg for String {
}
}
+impl<'a> IntoDiagnosticArg for Cow<'a, str> {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Str(Cow::Owned(self.into_owned()))
+ }
+}
+
impl<'a> IntoDiagnosticArg for &'a Path {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(self.display().to_string()))
@@ -170,6 +179,29 @@ impl IntoDiagnosticArg for Level {
}
}
+#[derive(Clone)]
+pub struct DiagnosticSymbolList(Vec<Symbol>);
+
+impl From<Vec<Symbol>> for DiagnosticSymbolList {
+ fn from(v: Vec<Symbol>) -> Self {
+ DiagnosticSymbolList(v)
+ }
+}
+
+impl IntoDiagnosticArg for DiagnosticSymbolList {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::StrListSepByAnd(
+ self.0.into_iter().map(|sym| Cow::Owned(format!("`{sym}`"))).collect(),
+ )
+ }
+}
+
+impl<Id> IntoDiagnosticArg for hir::def::Res<Id> {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Str(Cow::Borrowed(self.descr()))
+ }
+}
+
impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> {
fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
let mut diag;
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index cd6413bc3..4df2198fb 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -16,20 +16,20 @@ use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Styl
use crate::styled_buffer::StyledBuffer;
use crate::translation::{to_fluent_args, Translate};
use crate::{
- CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, FluentBundle, Handler,
- LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, SuggestionStyle,
+ diagnostic::DiagnosticLocation, CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage,
+ FluentBundle, Handler, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic,
+ SubstitutionHighlight, SuggestionStyle,
};
-
use rustc_lint_defs::pluralize;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_data_structures::sync::Lrc;
-use rustc_error_messages::FluentArgs;
+use rustc_error_messages::{FluentArgs, SpanLabel};
use rustc_span::hygiene::{ExpnKind, MacroKind};
use std::borrow::Cow;
use std::cmp::{max, min, Reverse};
-use std::io;
use std::io::prelude::*;
+use std::io::{self, IsTerminal};
use std::iter;
use std::path::Path;
use termcolor::{Ansi, BufferWriter, ColorChoice, ColorSpec, StandardStream};
@@ -64,6 +64,7 @@ impl HumanReadableErrorType {
teach: bool,
diagnostic_width: Option<usize>,
macro_backtrace: bool,
+ track_diagnostics: bool,
) -> EmitterWriter {
let (short, color_config) = self.unzip();
let color = color_config.suggests_using_colors();
@@ -77,6 +78,7 @@ impl HumanReadableErrorType {
color,
diagnostic_width,
macro_backtrace,
+ track_diagnostics,
)
}
}
@@ -246,7 +248,7 @@ pub trait Emitter: Translate {
fluent_args: &FluentArgs<'_>,
) -> (MultiSpan, &'a [CodeSuggestion]) {
let mut primary_span = diag.span.clone();
- let suggestions = diag.suggestions.as_ref().map_or(&[][..], |suggestions| &suggestions[..]);
+ let suggestions = diag.suggestions.as_deref().unwrap_or(&[]);
if let Some((sugg, rest)) = suggestions.split_first() {
let msg = self.translate_message(&sugg.msg, fluent_args);
if rest.is_empty() &&
@@ -281,7 +283,7 @@ pub trait Emitter: Translate {
if self
.source_map()
.map(|sm| is_case_difference(
- &**sm,
+ sm,
substitution,
sugg.substitutions[0].parts[0].span,
))
@@ -312,7 +314,6 @@ pub trait Emitter: Translate {
fn fix_multispans_in_extern_macros_and_render_macro_backtrace(
&self,
- source_map: &Option<Lrc<SourceMap>>,
span: &mut MultiSpan,
children: &mut Vec<SubDiagnostic>,
level: &Level,
@@ -338,7 +339,7 @@ pub trait Emitter: Translate {
.collect();
if !backtrace {
- self.fix_multispans_in_extern_macros(source_map, span, children);
+ self.fix_multispans_in_extern_macros(span, children);
}
self.render_multispans_macro_backtrace(span, children, backtrace);
@@ -478,15 +479,13 @@ pub trait Emitter: Translate {
// this will change the span to point at the use site.
fn fix_multispans_in_extern_macros(
&self,
- source_map: &Option<Lrc<SourceMap>>,
span: &mut MultiSpan,
children: &mut Vec<SubDiagnostic>,
) {
- let Some(source_map) = source_map else { return };
debug!("fix_multispans_in_extern_macros: before: span={:?} children={:?}", span, children);
- self.fix_multispan_in_extern_macros(source_map, span);
+ self.fix_multispan_in_extern_macros(span);
for child in children.iter_mut() {
- self.fix_multispan_in_extern_macros(source_map, &mut child.span);
+ self.fix_multispan_in_extern_macros(&mut child.span);
}
debug!("fix_multispans_in_extern_macros: after: span={:?} children={:?}", span, children);
}
@@ -494,7 +493,8 @@ pub trait Emitter: Translate {
// This "fixes" MultiSpans that contain `Span`s pointing to locations inside of external macros.
// Since these locations are often difficult to read,
// we move these spans from the external macros to their corresponding use site.
- fn fix_multispan_in_extern_macros(&self, source_map: &Lrc<SourceMap>, span: &mut MultiSpan) {
+ fn fix_multispan_in_extern_macros(&self, span: &mut MultiSpan) {
+ let Some(source_map) = self.source_map() else { return };
// First, find all the spans in external macros and point instead at their use site.
let replacements: Vec<(Span, Span)> = span
.primary_spans()
@@ -525,7 +525,7 @@ impl Translate for EmitterWriter {
}
fn fallback_fluent_bundle(&self) -> &FluentBundle {
- &**self.fallback_bundle
+ &self.fallback_bundle
}
}
@@ -538,11 +538,10 @@ impl Emitter for EmitterWriter {
let fluent_args = to_fluent_args(diag.args());
let mut children = diag.children.clone();
- let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args);
+ 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(
- &self.sm,
&mut primary_span,
&mut children,
&diag.level,
@@ -556,7 +555,8 @@ impl Emitter for EmitterWriter {
&diag.code,
&primary_span,
&children,
- &suggestions,
+ suggestions,
+ self.track_diagnostics.then_some(&diag.emitted_at),
);
}
@@ -619,14 +619,14 @@ impl ColorConfig {
fn to_color_choice(self) -> ColorChoice {
match self {
ColorConfig::Always => {
- if atty::is(atty::Stream::Stderr) {
+ if io::stderr().is_terminal() {
ColorChoice::Always
} else {
ColorChoice::AlwaysAnsi
}
}
ColorConfig::Never => ColorChoice::Never,
- ColorConfig::Auto if atty::is(atty::Stream::Stderr) => ColorChoice::Auto,
+ ColorConfig::Auto if io::stderr().is_terminal() => ColorChoice::Auto,
ColorConfig::Auto => ColorChoice::Never,
}
}
@@ -650,6 +650,7 @@ pub struct EmitterWriter {
diagnostic_width: Option<usize>,
macro_backtrace: bool,
+ track_diagnostics: bool,
}
#[derive(Debug)]
@@ -669,6 +670,7 @@ impl EmitterWriter {
teach: bool,
diagnostic_width: Option<usize>,
macro_backtrace: bool,
+ track_diagnostics: bool,
) -> EmitterWriter {
let dst = Destination::from_stderr(color_config);
EmitterWriter {
@@ -681,6 +683,7 @@ impl EmitterWriter {
ui_testing: false,
diagnostic_width,
macro_backtrace,
+ track_diagnostics,
}
}
@@ -694,6 +697,7 @@ impl EmitterWriter {
colored: bool,
diagnostic_width: Option<usize>,
macro_backtrace: bool,
+ track_diagnostics: bool,
) -> EmitterWriter {
EmitterWriter {
dst: Raw(dst, colored),
@@ -705,6 +709,7 @@ impl EmitterWriter {
ui_testing: false,
diagnostic_width,
macro_backtrace,
+ track_diagnostics,
}
}
@@ -768,6 +773,7 @@ impl EmitterWriter {
draw_col_separator_no_space(buffer, line_offset, width_offset - 2);
}
+ #[instrument(level = "trace", skip(self), ret)]
fn render_source_line(
&self,
buffer: &mut StyledBuffer,
@@ -796,9 +802,10 @@ impl EmitterWriter {
}
let source_string = match file.get_line(line.line_index - 1) {
- Some(s) => normalize_whitespace(&*s),
+ Some(s) => normalize_whitespace(&s),
None => return Vec::new(),
};
+ trace!(?source_string);
let line_offset = buffer.num_lines();
@@ -1143,7 +1150,7 @@ impl EmitterWriter {
(pos + 2, annotation.start_col.saturating_sub(left))
};
if let Some(ref label) = annotation.label {
- buffer.puts(line_offset + pos, code_offset + col, &label, style);
+ buffer.puts(line_offset + pos, code_offset + col, label, style);
}
}
@@ -1318,6 +1325,7 @@ impl EmitterWriter {
}
}
+ #[instrument(level = "trace", skip(self, args), ret)]
fn emit_message_default(
&mut self,
msp: &MultiSpan,
@@ -1327,6 +1335,7 @@ impl EmitterWriter {
level: &Level,
max_line_num_len: usize,
is_secondary: bool,
+ emitted_at: Option<&DiagnosticLocation>,
) -> io::Result<()> {
let mut buffer = StyledBuffer::new();
@@ -1352,7 +1361,7 @@ impl EmitterWriter {
// only render error codes, not lint codes
if let Some(DiagnosticId::Error(ref code)) = *code {
buffer.append(0, "[", Style::Level(*level));
- buffer.append(0, &code, Style::Level(*level));
+ buffer.append(0, code, Style::Level(*level));
buffer.append(0, "]", Style::Level(*level));
label_width += 2 + code.len();
}
@@ -1377,24 +1386,16 @@ impl EmitterWriter {
}
}
}
-
let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp);
+ trace!("{annotated_files:#?}");
// Make sure our primary file comes first
- let (primary_lo, sm) = if let (Some(sm), Some(ref primary_span)) =
- (self.sm.as_ref(), msp.primary_span().as_ref())
- {
- if !primary_span.is_dummy() {
- (sm.lookup_char_pos(primary_span.lo()), sm)
- } else {
- emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;
- return Ok(());
- }
- } else {
+ let primary_span = msp.primary_span().unwrap_or_default();
+ let (Some(sm), false) = (self.sm.as_ref(), primary_span.is_dummy()) else {
// If we don't have span information, emit and exit
- emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;
- return Ok(());
+ return emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message);
};
+ let primary_lo = sm.lookup_char_pos(primary_span.lo());
if let Ok(pos) =
annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name))
{
@@ -1405,6 +1406,54 @@ impl EmitterWriter {
for annotated_file in annotated_files {
// we can't annotate anything if the source is unavailable.
if !sm.ensure_source_file_source_present(annotated_file.file.clone()) {
+ if !self.short_message {
+ // We'll just print an unannotated message.
+ for (annotation_id, line) in annotated_file.lines.into_iter().enumerate() {
+ let mut annotations = line.annotations.clone();
+ annotations.sort_by_key(|a| Reverse(a.start_col));
+ let mut line_idx = buffer.num_lines();
+ buffer.append(
+ line_idx,
+ &format!(
+ "{}:{}:{}",
+ sm.filename_for_diagnostics(&annotated_file.file.name),
+ sm.doctest_offset_line(&annotated_file.file.name, line.line_index),
+ annotations[0].start_col + 1,
+ ),
+ Style::LineAndColumn,
+ );
+ if annotation_id == 0 {
+ buffer.prepend(line_idx, "--> ", Style::LineNumber);
+ for _ in 0..max_line_num_len {
+ buffer.prepend(line_idx, " ", Style::NoStyle);
+ }
+ line_idx += 1;
+ };
+ for (i, annotation) in annotations.into_iter().enumerate() {
+ if let Some(label) = &annotation.label {
+ let style = if annotation.is_primary {
+ Style::LabelPrimary
+ } else {
+ Style::LabelSecondary
+ };
+ if annotation_id == 0 {
+ buffer.prepend(line_idx, " |", Style::LineNumber);
+ for _ in 0..max_line_num_len {
+ buffer.prepend(line_idx, " ", Style::NoStyle);
+ }
+ line_idx += 1;
+ buffer.append(line_idx + i, " = note: ", style);
+ for _ in 0..max_line_num_len {
+ buffer.prepend(line_idx, " ", Style::NoStyle);
+ }
+ } else {
+ buffer.append(line_idx + i, ": ", style);
+ }
+ buffer.append(line_idx + i, label, style);
+ }
+ }
+ }
+ }
continue;
}
@@ -1651,6 +1700,13 @@ impl EmitterWriter {
multilines.extend(&to_add);
}
}
+ trace!("buffer: {:#?}", buffer.render());
+ }
+
+ if let Some(tracked) = emitted_at {
+ let track = format!("-Ztrack-diagnostics: created at {tracked}");
+ let len = buffer.num_lines();
+ buffer.append(len, &track, Style::NoStyle);
}
// final step: take our styled buffer, render it, then output it
@@ -1672,7 +1728,7 @@ impl EmitterWriter {
};
// Render the replacements for each suggestion
- let suggestions = suggestion.splice_lines(&**sm);
+ let suggestions = suggestion.splice_lines(sm);
debug!("emit_suggestion_default: suggestions={:?}", suggestions);
if suggestions.is_empty() {
@@ -1773,7 +1829,7 @@ impl EmitterWriter {
buffer.puts(
row_num - 1 + line - line_start,
max_line_num_len + 3,
- &normalize_whitespace(&*file_lines.file.get_line(line - 1).unwrap()),
+ &normalize_whitespace(&file_lines.file.get_line(line - 1).unwrap()),
Style::Removal,
);
}
@@ -1915,7 +1971,7 @@ impl EmitterWriter {
buffer.putc(
row_num,
(padding as isize + p) as usize,
- if part.is_addition(&sm) { '+' } else { '~' },
+ if part.is_addition(sm) { '+' } else { '~' },
Style::Addition,
);
}
@@ -1962,12 +2018,13 @@ impl EmitterWriter {
buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle);
} else if notice_capitalization {
let msg = "notice the capitalization difference";
- buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle);
+ buffer.puts(row_num, max_line_num_len + 3, msg, Style::NoStyle);
}
emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;
Ok(())
}
+ #[instrument(level = "trace", skip(self, args, code, children, suggestions))]
fn emit_messages_default(
&mut self,
level: &Level,
@@ -1977,6 +2034,7 @@ impl EmitterWriter {
span: &MultiSpan,
children: &[SubDiagnostic],
suggestions: &[CodeSuggestion],
+ emitted_at: Option<&DiagnosticLocation>,
) {
let max_line_num_len = if self.ui_testing {
ANONYMIZED_LINE_NUM.len()
@@ -1985,7 +2043,16 @@ impl EmitterWriter {
num_decimal_digits(n)
};
- match self.emit_message_default(span, message, args, code, level, max_line_num_len, false) {
+ match self.emit_message_default(
+ span,
+ message,
+ args,
+ code,
+ level,
+ max_line_num_len,
+ false,
+ emitted_at,
+ ) {
Ok(()) => {
if !children.is_empty()
|| suggestions.iter().any(|s| s.style != SuggestionStyle::CompletelyHidden)
@@ -2007,13 +2074,14 @@ impl EmitterWriter {
for child in children {
let span = child.render_span.as_ref().unwrap_or(&child.span);
if let Err(err) = self.emit_message_default(
- &span,
+ span,
&child.message,
args,
&None,
&child.level,
max_line_num_len,
true,
+ None,
) {
panic!("failed to emit error: {}", err);
}
@@ -2030,6 +2098,7 @@ impl EmitterWriter {
&Level::Help,
max_line_num_len,
true,
+ None,
) {
panic!("failed to emit error: {}", e);
}
@@ -2090,7 +2159,7 @@ impl EmitterWriter {
*row_num - 1,
max_line_num_len + 3,
&normalize_whitespace(
- &*file_lines.file.get_line(file_lines.lines[line_pos].line_index).unwrap(),
+ &file_lines.file.get_line(file_lines.lines[line_pos].line_index).unwrap(),
),
Style::NoStyle,
);
@@ -2186,13 +2255,16 @@ impl FileWithAnnotatedLines {
let mut multiline_annotations = vec![];
if let Some(ref sm) = emitter.source_map() {
- for span_label in msp.span_labels() {
- if span_label.span.is_dummy() {
- continue;
- }
+ for SpanLabel { span, is_primary, label } in msp.span_labels() {
+ // If we don't have a useful span, pick the primary span if that exists.
+ // Worst case we'll just print an error at the top of the main file.
+ let span = match (span.is_dummy(), msp.primary_span()) {
+ (_, None) | (false, _) => span,
+ (true, Some(span)) => span,
+ };
- let lo = sm.lookup_char_pos(span_label.span.lo());
- let mut hi = sm.lookup_char_pos(span_label.span.hi());
+ let lo = sm.lookup_char_pos(span.lo());
+ let mut hi = sm.lookup_char_pos(span.hi());
// Watch out for "empty spans". If we get a span like 6..6, we
// want to just display a `^` at 6, so convert that to
@@ -2204,6 +2276,8 @@ impl FileWithAnnotatedLines {
hi.col_display += 1;
}
+ let label = label.as_ref().map(|m| emitter.translate_message(m, args).to_string());
+
if lo.line != hi.line {
let ml = MultilineAnnotation {
depth: 1,
@@ -2211,11 +2285,8 @@ impl FileWithAnnotatedLines {
line_end: hi.line,
start_col: lo.col_display,
end_col: hi.col_display,
- is_primary: span_label.is_primary,
- label: span_label
- .label
- .as_ref()
- .map(|m| emitter.translate_message(m, args).to_string()),
+ is_primary,
+ label,
overlaps_exactly: false,
};
multiline_annotations.push((lo.file, ml));
@@ -2223,11 +2294,8 @@ impl FileWithAnnotatedLines {
let ann = Annotation {
start_col: lo.col_display,
end_col: hi.col_display,
- is_primary: span_label.is_primary,
- label: span_label
- .label
- .as_ref()
- .map(|m| emitter.translate_message(m, args).to_string()),
+ is_primary,
+ label,
annotation_type: AnnotationType::Singleline,
};
add_annotation_to_file(&mut output, lo.file, lo.line, ann);
@@ -2236,7 +2304,7 @@ impl FileWithAnnotatedLines {
}
// Find overlapping multiline annotations, put them at different depths
- multiline_annotations.sort_by_key(|&(_, ref ml)| (ml.line_start, ml.line_end));
+ multiline_annotations.sort_by_key(|&(_, ref ml)| (ml.line_start, usize::MAX - ml.line_end));
for (_, ann) in multiline_annotations.clone() {
for (_, a) in multiline_annotations.iter_mut() {
// Move all other multiline annotations overlapping with this one
@@ -2254,8 +2322,14 @@ impl FileWithAnnotatedLines {
}
let mut max_depth = 0; // max overlapping multiline spans
- for (file, ann) in multiline_annotations {
+ for (_, ann) in &multiline_annotations {
max_depth = max(max_depth, ann.depth);
+ }
+ // Change order of multispan depth to minimize the number of overlaps in the ASCII art.
+ for (_, a) in multiline_annotations.iter_mut() {
+ a.depth = max_depth - a.depth + 1;
+ }
+ for (file, ann) in multiline_annotations {
let mut end_ann = ann.as_end();
if !ann.overlaps_exactly {
// avoid output like
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index 4cc7be47f..a37073d8f 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -45,6 +45,7 @@ pub struct JsonEmitter {
json_rendered: HumanReadableErrorType,
diagnostic_width: Option<usize>,
macro_backtrace: bool,
+ track_diagnostics: bool,
}
impl JsonEmitter {
@@ -57,6 +58,7 @@ impl JsonEmitter {
json_rendered: HumanReadableErrorType,
diagnostic_width: Option<usize>,
macro_backtrace: bool,
+ track_diagnostics: bool,
) -> JsonEmitter {
JsonEmitter {
dst: Box::new(io::BufWriter::new(io::stderr())),
@@ -69,6 +71,7 @@ impl JsonEmitter {
json_rendered,
diagnostic_width,
macro_backtrace,
+ track_diagnostics,
}
}
@@ -79,6 +82,7 @@ impl JsonEmitter {
fallback_bundle: LazyFallbackBundle,
diagnostic_width: Option<usize>,
macro_backtrace: bool,
+ track_diagnostics: bool,
) -> JsonEmitter {
let file_path_mapping = FilePathMapping::empty();
JsonEmitter::stderr(
@@ -90,6 +94,7 @@ impl JsonEmitter {
json_rendered,
diagnostic_width,
macro_backtrace,
+ track_diagnostics,
)
}
@@ -103,6 +108,7 @@ impl JsonEmitter {
json_rendered: HumanReadableErrorType,
diagnostic_width: Option<usize>,
macro_backtrace: bool,
+ track_diagnostics: bool,
) -> JsonEmitter {
JsonEmitter {
dst,
@@ -115,6 +121,7 @@ impl JsonEmitter {
json_rendered,
diagnostic_width,
macro_backtrace,
+ track_diagnostics,
}
}
@@ -129,7 +136,7 @@ impl Translate for JsonEmitter {
}
fn fallback_fluent_bundle(&self) -> &FluentBundle {
- &**self.fallback_bundle
+ &self.fallback_bundle
}
}
@@ -350,6 +357,7 @@ impl Diagnostic {
false,
je.diagnostic_width,
je.macro_backtrace,
+ je.track_diagnostics,
)
.ui_testing(je.ui_testing)
.emit_diagnostic(diag);
diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs
index d940d14e1..f13146897 100644
--- a/compiler/rustc_errors/src/json/tests.rs
+++ b/compiler/rustc_errors/src/json/tests.rs
@@ -59,6 +59,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
HumanReadableErrorType::Short(ColorConfig::Never),
None,
false,
+ false,
);
let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1));
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 0963ea71f..eb0506c45 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -5,6 +5,7 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(drain_filter)]
#![feature(if_let_guard)]
+#![feature(is_terminal)]
#![feature(adt_const_params)]
#![feature(let_chains)]
#![feature(never_type)]
@@ -376,7 +377,7 @@ pub use diagnostic::{
DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic,
};
pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted};
-pub use diagnostic_impls::DiagnosticArgFromDisplay;
+pub use diagnostic_impls::{DiagnosticArgFromDisplay, DiagnosticSymbolList};
use std::backtrace::Backtrace;
/// A handler deals with errors and other compiler output.
@@ -466,6 +467,10 @@ pub enum StashKey {
/// When an invalid lifetime e.g. `'2` should be reinterpreted
/// as a char literal in the parser
LifetimeIsChar,
+ /// Maybe there was a typo where a comma was forgotten before
+ /// FRU syntax
+ MaybeFruTypo,
+ CallAssocMethod,
}
fn default_track_diagnostic(_: &Diagnostic) {}
@@ -492,6 +497,8 @@ pub struct HandlerFlags {
pub macro_backtrace: bool,
/// If true, identical diagnostics are reported only once.
pub deduplicate_diagnostics: bool,
+ /// Track where errors are created. Enabled with `-Ztrack-diagnostics`.
+ pub track_diagnostics: bool,
}
impl Drop for HandlerInner {
@@ -559,6 +566,7 @@ impl Handler {
false,
None,
flags.macro_backtrace,
+ flags.track_diagnostics,
));
Self::with_emitter_and_flags(emitter, flags)
}
@@ -641,13 +649,14 @@ impl Handler {
inner.stashed_diagnostics = Default::default();
}
- /// Stash a given diagnostic with the given `Span` and `StashKey` as the key for later stealing.
+ /// Stash a given diagnostic with the given `Span` and [`StashKey`] as the key.
+ /// Retrieve a stashed diagnostic with `steal_diagnostic`.
pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: Diagnostic) {
let mut inner = self.inner.borrow_mut();
inner.stash((span, key), diag);
}
- /// Steal a previously stashed diagnostic with the given `Span` and `StashKey` as the key.
+ /// Steal a previously stashed diagnostic with the given `Span` and [`StashKey`] as the key.
pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_, ()>> {
let mut inner = self.inner.borrow_mut();
inner.steal((span, key)).map(|diag| DiagnosticBuilder::new_diagnostic(self, diag))
@@ -664,6 +673,7 @@ impl Handler {
/// Construct a builder with the `msg` at the level appropriate for the specific `EmissionGuarantee`.
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_diagnostic<G: EmissionGuarantee>(
&self,
msg: impl Into<DiagnosticMessage>,
@@ -677,6 +687,7 @@ impl Handler {
/// * `can_emit_warnings` is `true`
/// * `is_force_warn` was set in `DiagnosticId::Lint`
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_span_warn(
&self,
span: impl Into<MultiSpan>,
@@ -693,6 +704,7 @@ 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`
+ #[track_caller]
pub fn struct_span_warn_with_expectation(
&self,
span: impl Into<MultiSpan>,
@@ -706,6 +718,7 @@ impl Handler {
/// Construct a builder at the `Allow` level at the given `span` and with the `msg`.
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_span_allow(
&self,
span: impl Into<MultiSpan>,
@@ -719,6 +732,7 @@ impl Handler {
/// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
/// Also include a code.
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_span_warn_with_code(
&self,
span: impl Into<MultiSpan>,
@@ -736,6 +750,7 @@ impl Handler {
/// * `can_emit_warnings` is `true`
/// * `is_force_warn` was set in `DiagnosticId::Lint`
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
DiagnosticBuilder::new(self, Level::Warning(None), msg)
}
@@ -746,6 +761,7 @@ 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`
+ #[track_caller]
pub fn struct_warn_with_expectation(
&self,
msg: impl Into<DiagnosticMessage>,
@@ -756,12 +772,14 @@ impl Handler {
/// Construct a builder at the `Allow` level with the `msg`.
#[rustc_lint_diagnostics]
+ #[track_caller]
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`.
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_expect(
&self,
msg: impl Into<DiagnosticMessage>,
@@ -772,6 +790,7 @@ impl Handler {
/// Construct a builder at the `Error` level at the given `span` and with the `msg`.
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_span_err(
&self,
span: impl Into<MultiSpan>,
@@ -784,6 +803,7 @@ impl Handler {
/// Construct a builder at the `Error` level at the given `span`, with the `msg`, and `code`.
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_span_err_with_code(
&self,
span: impl Into<MultiSpan>,
@@ -798,6 +818,7 @@ 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).
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_err(
&self,
msg: impl Into<DiagnosticMessage>,
@@ -807,12 +828,14 @@ impl Handler {
/// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors.
#[doc(hidden)]
+ #[track_caller]
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`.
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_err_with_code(
&self,
msg: impl Into<DiagnosticMessage>,
@@ -825,6 +848,7 @@ impl Handler {
/// Construct a builder at the `Warn` level with the `msg` and the `code`.
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_warn_with_code(
&self,
msg: impl Into<DiagnosticMessage>,
@@ -837,6 +861,7 @@ impl Handler {
/// Construct a builder at the `Fatal` level at the given `span` and with the `msg`.
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_span_fatal(
&self,
span: impl Into<MultiSpan>,
@@ -849,6 +874,7 @@ impl Handler {
/// Construct a builder at the `Fatal` level at the given `span`, with the `msg`, and `code`.
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_span_fatal_with_code(
&self,
span: impl Into<MultiSpan>,
@@ -862,6 +888,7 @@ impl Handler {
/// Construct a builder at the `Error` level with the `msg`.
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
DiagnosticBuilder::new_fatal(self, msg)
}
@@ -874,6 +901,7 @@ impl Handler {
/// Construct a builder at the `Note` level with the `msg`.
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_note_without_error(
&self,
msg: impl Into<DiagnosticMessage>,
@@ -882,12 +910,14 @@ impl Handler {
}
#[rustc_lint_diagnostics]
+ #[track_caller]
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()
}
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn span_fatal_with_code(
&self,
span: impl Into<MultiSpan>,
@@ -899,6 +929,7 @@ impl Handler {
}
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn span_err(
&self,
span: impl Into<MultiSpan>,
@@ -908,6 +939,7 @@ impl Handler {
}
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn span_err_with_code(
&self,
span: impl Into<MultiSpan>,
@@ -921,11 +953,13 @@ impl Handler {
}
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) {
self.emit_diag_at_span(Diagnostic::new(Warning(None), msg), span);
}
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn span_warn_with_code(
&self,
span: impl Into<MultiSpan>,
@@ -954,10 +988,12 @@ impl Handler {
self.inner.borrow_mut().delay_good_path_bug(msg)
}
+ #[track_caller]
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);
}
+ #[track_caller]
pub fn span_note_without_error(
&self,
span: impl Into<MultiSpan>,
@@ -966,6 +1002,7 @@ impl Handler {
self.emit_diag_at_span(Diagnostic::new(Note, msg), span);
}
+ #[track_caller]
pub fn span_note_diag(
&self,
span: Span,
@@ -1008,13 +1045,24 @@ impl Handler {
}
pub fn has_errors_or_lint_errors(&self) -> Option<ErrorGuaranteed> {
if self.inner.borrow().has_errors_or_lint_errors() {
- Some(ErrorGuaranteed(()))
+ Some(ErrorGuaranteed::unchecked_claim_error_was_emitted())
} else {
None
}
}
- pub fn has_errors_or_delayed_span_bugs(&self) -> bool {
- self.inner.borrow().has_errors_or_delayed_span_bugs()
+ pub fn has_errors_or_delayed_span_bugs(&self) -> Option<ErrorGuaranteed> {
+ if self.inner.borrow().has_errors_or_delayed_span_bugs() {
+ Some(ErrorGuaranteed::unchecked_claim_error_was_emitted())
+ } else {
+ None
+ }
+ }
+ pub fn is_compilation_going_to_fail(&self) -> Option<ErrorGuaranteed> {
+ if self.inner.borrow().is_compilation_going_to_fail() {
+ Some(ErrorGuaranteed::unchecked_claim_error_was_emitted())
+ } else {
+ None
+ }
}
pub fn print_error_count(&self, registry: &Registry) {
@@ -1223,6 +1271,10 @@ impl HandlerInner {
}
if diagnostic.has_future_breakage() {
+ // Future breakages aren't emitted if they're Level::Allowed,
+ // but they still need to be constructed and stashed below,
+ // so they'll trigger the good-path bug check.
+ self.suppressed_expected_diag = true;
self.future_breakage_diagnostics.push(diagnostic.clone());
}
@@ -1277,7 +1329,7 @@ impl HandlerInner {
diagnostic.children.drain_filter(already_emitted_sub).for_each(|_| {});
- self.emitter.emit_diagnostic(&diagnostic);
+ self.emitter.emit_diagnostic(diagnostic);
if diagnostic.is_error() {
self.deduplicated_err_count += 1;
} else if let Warning(_) = diagnostic.level {
@@ -1444,6 +1496,10 @@ impl HandlerInner {
self.err_count() > 0 || self.lint_err_count > 0 || self.warn_count > 0
}
+ fn is_compilation_going_to_fail(&self) -> bool {
+ self.has_errors() || self.lint_err_count > 0 || !self.delayed_span_bugs.is_empty()
+ }
+
fn abort_if_errors(&mut self) {
self.emit_stashed_diagnostics();
@@ -1452,6 +1508,7 @@ impl HandlerInner {
}
}
+ #[track_caller]
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);
diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs
index a7737b467..afd660ff1 100644
--- a/compiler/rustc_errors/src/translation.rs
+++ b/compiler/rustc_errors/src/translation.rs
@@ -1,7 +1,10 @@
use crate::snippet::Style;
use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle};
use rustc_data_structures::sync::Lrc;
-use rustc_error_messages::FluentArgs;
+use rustc_error_messages::{
+ fluent_bundle::resolver::errors::{ReferenceKind, ResolverError},
+ FluentArgs, FluentError,
+};
use std::borrow::Cow;
/// Convert diagnostic arguments (a rustc internal type that exists to implement
@@ -56,13 +59,13 @@ pub trait Translate {
trace!(?message, ?args);
let (identifier, attr) = match message {
DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => {
- return Cow::Borrowed(&msg);
+ return Cow::Borrowed(msg);
}
DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr),
};
let translate_with_bundle = |bundle: &'a FluentBundle| -> Option<(Cow<'_, str>, Vec<_>)> {
- let message = bundle.get_message(&identifier)?;
+ let message = bundle.get_message(identifier)?;
let value = match attr {
Some(attr) => message.get_attribute(attr)?.value(),
None => message.value()?,
@@ -70,7 +73,7 @@ pub trait Translate {
debug!(?message, ?value);
let mut errs = vec![];
- let translated = bundle.format_pattern(value, Some(&args), &mut errs);
+ let translated = bundle.format_pattern(value, Some(args), &mut errs);
debug!(?translated, ?errs);
Some((translated, errs))
};
@@ -102,14 +105,31 @@ pub trait Translate {
.or_else(|| translate_with_bundle(self.fallback_fluent_bundle()))
.map(|(translated, errs)| {
// Always bail out for errors with the fallback bundle.
- assert!(
- errs.is_empty(),
- "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}",
- identifier,
- attr,
- args,
- errs
- );
+
+ let mut help_messages = vec![];
+
+ if !errs.is_empty() {
+ for error in &errs {
+ match error {
+ FluentError::ResolverError(ResolverError::Reference(
+ ReferenceKind::Message { id, .. },
+ )) if args.iter().any(|(arg_id, _)| arg_id == id) => {
+ help_messages.push(format!("Argument `{id}` exists but was not referenced correctly. Try using `{{${id}}}` instead"));
+ }
+ _ => {}
+ }
+ }
+
+ panic!(
+ "Encountered errors while formatting message for `{identifier}`\n\
+ help: {}\n\
+ attr: `{attr:?}`\n\
+ args: `{args:?}`\n\
+ errors: `{errs:?}`",
+ help_messages.join("\nhelp: ")
+ );
+ }
+
translated
})
.expect("failed to find message in primary or fallback fluent bundles")