use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString};
use crate::styled_buffer::StyledBuffer;
-use crate::Level::Error;
use crate::{
pluralize, CodeSuggestion, Diagnostic, DiagnosticId, Level, SubDiagnostic, SuggestionStyle,
};
+use log::*;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use rustc_span::hygiene::{ExpnKind, MacroKind};
source_map: Option<Lrc<SourceMap>>,
teach: bool,
terminal_width: Option<usize>,
- external_macro_backtrace: bool,
+ 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,
- external_macro_backtrace,
- )
+ EmitterWriter::new(dst, source_map, short, teach, color, terminal_width, macro_backtrace)
}
}
}
}
- // This does a small "fix" for multispans by looking to see if it can find any that
- // point directly at <*macros>. Since these are often difficult to read, this
- // will change the span to point at the use site.
- fn fix_multispans_in_std_macros(
+ fn fix_multispans_in_extern_macros_and_render_macro_backtrace(
&self,
source_map: &Option<Lrc<SourceMap>>,
span: &mut MultiSpan,
level: &Level,
backtrace: bool,
) {
- let mut spans_updated = self.fix_multispan_in_std_macros(source_map, span, backtrace);
- for child in children.iter_mut() {
- spans_updated |=
- self.fix_multispan_in_std_macros(source_map, &mut child.span, backtrace);
+ let mut external_spans_updated = false;
+ if !backtrace {
+ external_spans_updated =
+ self.fix_multispans_in_extern_macros(source_map, span, children);
}
- let msg = if level == &Error {
- "this error originates in a macro outside of the current crate \
- (in Nightly builds, run with -Z external-macro-backtrace \
- for more info)"
- .to_string()
- } else {
- "this warning originates in a macro outside of the current crate \
- (in Nightly builds, run with -Z external-macro-backtrace \
- for more info)"
- .to_string()
- };
- if spans_updated {
- children.push(SubDiagnostic {
- level: Level::Note,
- message: vec![(msg, Style::NoStyle)],
- span: MultiSpan::new(),
- render_span: None,
- });
+ self.render_multispans_macro_backtrace(span, children, backtrace);
+
+ if !backtrace {
+ if external_spans_updated {
+ let msg = format!(
+ "this {} originates in a macro outside of the current crate \
+ (in Nightly builds, run with -Z macro-backtrace for more info)",
+ level,
+ );
+
+ children.push(SubDiagnostic {
+ level: Level::Note,
+ message: vec![(msg, Style::NoStyle)],
+ span: MultiSpan::new(),
+ render_span: None,
+ });
+ }
}
}
- // This "fixes" MultiSpans that contain Spans that are pointing to locations inside of
- // <*macros>. Since these locations are often difficult to read, we move these Spans from
- // <*macros> to their corresponding use site.
- fn fix_multispan_in_std_macros(
+ fn render_multispans_macro_backtrace(
&self,
- source_map: &Option<Lrc<SourceMap>>,
span: &mut MultiSpan,
- always_backtrace: bool,
- ) -> bool {
- let sm = match source_map {
- Some(ref sm) => sm,
- None => return false,
- };
+ children: &mut Vec<SubDiagnostic>,
+ backtrace: bool,
+ ) {
+ self.render_multispan_macro_backtrace(span, backtrace);
+ for child in children.iter_mut() {
+ self.render_multispan_macro_backtrace(&mut child.span, backtrace);
+ }
+ }
- let mut before_after: Vec<(Span, Span)> = vec![];
+ fn render_multispan_macro_backtrace(&self, span: &mut MultiSpan, always_backtrace: bool) {
let mut new_labels: Vec<(Span, String)> = vec![];
- // First, find all the spans in <*macros> and point instead at their use site
- for sp in span.primary_spans() {
+ for &sp in span.primary_spans() {
if sp.is_dummy() {
continue;
}
- let call_sp = sm.call_span_if_macro(*sp);
- if call_sp != *sp && !always_backtrace {
- before_after.push((*sp, call_sp));
- }
+
+ // FIXME(eddyb) use `retain` on `macro_backtrace` to remove all the
+ // entries we don't want to print, to make sure the indices being
+ // printed are contiguous (or omitted if there's only one entry).
let macro_backtrace: Vec<_> = sp.macro_backtrace().collect();
- let backtrace_len = macro_backtrace.len();
for (i, trace) in macro_backtrace.iter().rev().enumerate() {
- // Only show macro locations that are local
- // and display them like a span_note
if trace.def_site.is_dummy() {
continue;
}
+
if always_backtrace {
new_labels.push((
trace.def_site,
format!(
"in this expansion of `{}`{}",
trace.kind.descr(),
- if backtrace_len > 2 {
- // if backtrace_len == 1 it'll be pointed
- // at by "in this macro invocation"
+ if macro_backtrace.len() > 2 {
+ // if macro_backtrace.len() == 1 it'll be
+ // pointed at by "in this macro invocation"
format!(" (#{})", i + 1)
} else {
String::new()
- }
+ },
),
));
}
- // Check to make sure we're not in any <*macros>
- if !sm.span_to_filename(trace.def_site).is_macros()
- && matches!(trace.kind, ExpnKind::Macro(MacroKind::Bang, _))
+
+ // Don't add a label on the call site if the diagnostic itself
+ // already points to (a part of) that call site, as the label
+ // is meant for showing the relevant invocation when the actual
+ // diagnostic is pointing to some part of macro definition.
+ //
+ // This also handles the case where an external span got replaced
+ // with the call site span by `fix_multispans_in_extern_macros`.
+ //
+ // NB: `-Zmacro-backtrace` overrides this, for uniformity, as the
+ // "in this expansion of" label above is always added in that mode,
+ // and it needs an "in this macro invocation" label to match that.
+ let redundant_span = trace.call_site.contains(sp);
+
+ if !redundant_span && matches!(trace.kind, ExpnKind::Macro(MacroKind::Bang, _))
|| always_backtrace
{
new_labels.push((
trace.call_site,
format!(
"in this macro invocation{}",
- if backtrace_len > 2 && always_backtrace {
+ if macro_backtrace.len() > 2 && always_backtrace {
// only specify order when the macro
// backtrace is multiple levels deep
format!(" (#{})", i + 1)
} else {
String::new()
- }
+ },
),
));
- if !always_backtrace {
- break;
- }
+ }
+ if !always_backtrace {
+ break;
}
}
}
for (label_span, label_text) in new_labels {
span.push_span_label(label_span, label_text);
}
- for sp_label in span.span_labels() {
- if sp_label.span.is_dummy() {
- continue;
- }
- if sm.span_to_filename(sp_label.span.clone()).is_macros() && !always_backtrace {
- if let Some(use_site) = sp_label.span.macro_backtrace().last() {
- before_after.push((sp_label.span.clone(), use_site.call_site.clone()));
- }
- }
+ }
+
+ // This does a small "fix" for multispans by looking to see if it can find any that
+ // point directly at <*macros>. Since these are often difficult to read, 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>,
+ ) -> bool {
+ let mut spans_updated = self.fix_multispan_in_extern_macros(source_map, span);
+ for child in children.iter_mut() {
+ spans_updated |= self.fix_multispan_in_extern_macros(source_map, &mut child.span);
}
+ spans_updated
+ }
+
+ // This "fixes" MultiSpans that contain Spans that are pointing to locations inside of
+ // <*macros>. Since these locations are often difficult to read, we move these Spans from
+ // <*macros> to their corresponding use site.
+ fn fix_multispan_in_extern_macros(
+ &self,
+ source_map: &Option<Lrc<SourceMap>>,
+ span: &mut MultiSpan,
+ ) -> bool {
+ let sm = match source_map {
+ Some(ref sm) => sm,
+ None => return false,
+ };
+
+ // First, find all the spans in <*macros> and point instead at their use site
+ let replacements: Vec<(Span, Span)> = span
+ .primary_spans()
+ .iter()
+ .copied()
+ .chain(span.span_labels().iter().map(|sp_label| sp_label.span))
+ .filter_map(|sp| {
+ if !sp.is_dummy() && sm.span_to_filename(sp).is_macros() {
+ let maybe_callsite = sp.source_callsite();
+ if sp != maybe_callsite {
+ return Some((sp, maybe_callsite));
+ }
+ }
+ None
+ })
+ .collect();
+
// After we have them, make sure we replace these 'bad' def sites with their use sites
- let spans_updated = !before_after.is_empty();
- for (before, after) in before_after {
- span.replace(before, after);
+ let spans_updated = !replacements.is_empty();
+ for (from, to) in replacements {
+ span.replace(from, to);
}
spans_updated
let mut children = diag.children.clone();
let (mut primary_span, suggestions) = self.primary_span_formatted(&diag);
- self.fix_multispans_in_std_macros(
+ self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
&self.sm,
&mut primary_span,
&mut children,
&diag.level,
- self.external_macro_backtrace,
+ self.macro_backtrace,
);
self.emit_messages_default(
ui_testing: bool,
terminal_width: Option<usize>,
- external_macro_backtrace: bool,
+ macro_backtrace: bool,
}
#[derive(Debug)]
short_message: bool,
teach: bool,
terminal_width: Option<usize>,
- external_macro_backtrace: bool,
+ macro_backtrace: bool,
) -> EmitterWriter {
let dst = Destination::from_stderr(color_config);
EmitterWriter {
teach,
ui_testing: false,
terminal_width,
- external_macro_backtrace,
+ macro_backtrace,
}
}
teach: bool,
colored: bool,
terminal_width: Option<usize>,
- external_macro_backtrace: bool,
+ macro_backtrace: bool,
) -> EmitterWriter {
EmitterWriter {
dst: Raw(dst, colored),
teach,
ui_testing: false,
terminal_width,
- external_macro_backtrace,
+ macro_backtrace,
}
}
let level_str = level.to_string();
// The failure note level itself does not provide any useful diagnostic information
if *level != Level::FailureNote && !level_str.is_empty() {
- buffer.append(0, &level_str, Style::Level(level.clone()));
+ buffer.append(0, &level_str, Style::Level(*level));
}
// only render error codes, not lint codes
if let Some(DiagnosticId::Error(ref code)) = *code {
- buffer.append(0, "[", Style::Level(level.clone()));
- buffer.append(0, &code, Style::Level(level.clone()));
- buffer.append(0, "]", Style::Level(level.clone()));
+ buffer.append(0, "[", Style::Level(*level));
+ buffer.append(0, &code, Style::Level(*level));
+ buffer.append(0, "]", Style::Level(*level));
}
if *level != Level::FailureNote && !level_str.is_empty() {
buffer.append(0, ": ", header_style);
} else if self.ui_testing {
140
} else {
- term_size::dimensions()
+ termize::dimensions()
.map(|(w, _)| w.saturating_sub(code_offset))
.unwrap_or(std::usize::MAX)
};
// Render the suggestion message
let level_str = level.to_string();
if !level_str.is_empty() {
- buffer.append(0, &level_str, Style::Level(level.clone()));
+ buffer.append(0, &level_str, Style::Level(*level));
buffer.append(0, ": ", Style::HeaderMsg);
}
self.msg_to_buffer(
/// Whether the original and suggested code are visually similar enough to warrant extra wording.
pub fn is_case_difference(sm: &SourceMap, suggested: &str, sp: Span) -> bool {
// FIXME: this should probably be extended to also account for `FO0` → `FOO` and unicode.
- let found = sm.span_to_snippet(sp).unwrap();
+ let found = match sm.span_to_snippet(sp) {
+ Ok(snippet) => snippet,
+ Err(e) => {
+ warn!("Invalid span {:?}. Err={:?}", sp, e);
+ return false;
+ }
+ };
let ascii_confusables = &['c', 'f', 'i', 'k', 'o', 's', 'u', 'v', 'w', 'x', 'y', 'z'];
// All the chars that differ in capitalization are confusable (above):
let confusable = found