summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_errors/src/emitter.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_errors/src/emitter.rs')
-rw-r--r--compiler/rustc_errors/src/emitter.rs196
1 files changed, 135 insertions, 61 deletions
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