1 //! The current rustc diagnostics emitter.
3 //! An `Emitter` takes care of generating the output from a `DiagnosticBuilder` struct.
5 //! There are various `Emitter` implementations that generate different output formats such as
6 //! JSON and human readable output.
8 //! The output types are defined in `librustc::session::config::ErrorOutputType`.
12 use syntax_pos
::source_map
::SourceMap
;
13 use syntax_pos
::{MultiSpan, SourceFile, Span}
;
15 use crate::snippet
::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString}
;
16 use crate::styled_buffer
::StyledBuffer
;
17 use crate::Level
::Error
;
19 pluralize
, CodeSuggestion
, Diagnostic
, DiagnosticId
, Level
, SubDiagnostic
, SuggestionStyle
,
23 use rustc_data_structures
::fx
::FxHashMap
;
24 use rustc_data_structures
::sync
::Lrc
;
26 use std
::cmp
::{max, min, Reverse}
;
28 use std
::io
::prelude
::*;
30 use termcolor
::{Ansi, BufferWriter, ColorChoice, ColorSpec, StandardStream}
;
31 use termcolor
::{Buffer, Color, WriteColor}
;
33 /// Describes the way the content of the `rendered` field of the json output is generated
34 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
35 pub enum HumanReadableErrorType
{
37 AnnotateSnippet(ColorConfig
),
41 impl HumanReadableErrorType
{
42 /// Returns a (`short`, `color`) tuple
43 pub fn unzip(self) -> (bool
, ColorConfig
) {
45 HumanReadableErrorType
::Default(cc
) => (false, cc
),
46 HumanReadableErrorType
::Short(cc
) => (true, cc
),
47 HumanReadableErrorType
::AnnotateSnippet(cc
) => (false, cc
),
52 dst
: Box
<dyn Write
+ Send
>,
53 source_map
: Option
<Lrc
<SourceMap
>>,
55 terminal_width
: Option
<usize>,
56 external_macro_backtrace
: bool
,
58 let (short
, color_config
) = self.unzip();
59 let color
= color_config
.suggests_using_colors();
67 external_macro_backtrace
,
72 #[derive(Clone, Copy, Debug)]
74 /// The available whitespace in the left that can be consumed when centering.
75 pub whitespace_left
: usize,
76 /// The column of the beginning of left-most span.
78 /// The column of the end of right-most span.
79 pub span_right
: usize,
80 /// The beginning of the line to be displayed.
81 pub computed_left
: usize,
82 /// The end of the line to be displayed.
83 pub computed_right
: usize,
84 /// The current width of the terminal. 140 by default and in tests.
85 pub column_width
: usize,
86 /// The end column of a span label, including the span. Doesn't account for labels not in the
87 /// same line as the span.
88 pub label_right
: usize,
93 whitespace_left
: usize,
100 // The 6 is padding to give a bit of room for `...` when displaying:
105 // 16 | ... fn foo(self) -> Self::Bar {
110 whitespace_left
: whitespace_left
.saturating_sub(6),
111 span_left
: span_left
.saturating_sub(6),
112 span_right
: span_right
+ 6,
116 label_right
: label_right
+ 6,
118 m
.compute(max_line_len
);
122 fn was_cut_left(&self) -> bool
{
123 self.computed_left
> 0
126 fn was_cut_right(&self, line_len
: usize) -> bool
{
128 if self.computed_right
== self.span_right
|| self.computed_right
== self.label_right
{
129 // Account for the "..." padding given above. Otherwise we end
130 // up with code lines that do fit but end in "..." as if they
132 self.computed_right
- 6
136 right
< line_len
&& self.computed_left
+ self.column_width
< line_len
139 fn compute(&mut self, max_line_len
: usize) {
140 // When there's a lot of whitespace (>20), we want to trim it as it is useless.
141 self.computed_left
= if self.whitespace_left
> 20 {
142 self.whitespace_left
- 16 // We want some padding.
146 // We want to show as much as possible, max_line_len is the right-most boundary for the
148 self.computed_right
= max(max_line_len
, self.computed_left
);
150 if self.computed_right
- self.computed_left
> self.column_width
{
151 // Trimming only whitespace isn't enough, let's get craftier.
152 if self.label_right
- self.whitespace_left
<= self.column_width
{
153 // Attempt to fit the code window only trimming whitespace.
154 self.computed_left
= self.whitespace_left
;
155 self.computed_right
= self.computed_left
+ self.column_width
;
156 } else if self.label_right
- self.span_left
<= self.column_width
{
157 // Attempt to fit the code window considering only the spans and labels.
158 let padding_left
= (self.column_width
- (self.label_right
- self.span_left
)) / 2;
159 self.computed_left
= self.span_left
.saturating_sub(padding_left
);
160 self.computed_right
= self.computed_left
+ self.column_width
;
161 } else if self.span_right
- self.span_left
<= self.column_width
{
162 // Attempt to fit the code window considering the spans and labels plus padding.
163 let padding_left
= (self.column_width
- (self.span_right
- self.span_left
)) / 5 * 2;
164 self.computed_left
= self.span_left
.saturating_sub(padding_left
);
165 self.computed_right
= self.computed_left
+ self.column_width
;
167 // Mostly give up but still don't show the full line.
168 self.computed_left
= self.span_left
;
169 self.computed_right
= self.span_right
;
174 fn left(&self, line_len
: usize) -> usize {
175 min(self.computed_left
, line_len
)
178 fn right(&self, line_len
: usize) -> usize {
179 if line_len
.saturating_sub(self.computed_left
) <= self.column_width
{
182 min(line_len
, self.computed_right
)
187 const ANONYMIZED_LINE_NUM
: &str = "LL";
189 /// Emitter trait for emitting errors.
191 /// Emit a structured diagnostic.
192 fn emit_diagnostic(&mut self, diag
: &Diagnostic
);
194 /// Emit a notification that an artifact has been output.
195 /// This is currently only supported for the JSON format,
196 /// other formats can, and will, simply ignore it.
197 fn emit_artifact_notification(&mut self, _path
: &Path
, _artifact_type
: &str) {}
199 /// Checks if should show explanations about "rustc --explain"
200 fn should_show_explain(&self) -> bool
{
204 fn source_map(&self) -> Option
<&Lrc
<SourceMap
>>;
206 /// Formats the substitutions of the primary_span
208 /// The are a lot of conditions to this method, but in short:
210 /// * If the current `Diagnostic` has only one visible `CodeSuggestion`,
211 /// we format the `help` suggestion depending on the content of the
212 /// substitutions. In that case, we return the modified span only.
214 /// * If the current `Diagnostic` has multiple suggestions,
215 /// we return the original `primary_span` and the original suggestions.
216 fn primary_span_formatted
<'a
>(
218 diag
: &'a Diagnostic
,
219 ) -> (MultiSpan
, &'a
[CodeSuggestion
]) {
220 let mut primary_span
= diag
.span
.clone();
221 if let Some((sugg
, rest
)) = diag
.suggestions
.split_first() {
222 if rest
.is_empty() &&
223 // ^ if there is only one suggestion
224 // don't display multi-suggestions as labels
225 sugg
.substitutions
.len() == 1 &&
226 // don't display multipart suggestions as labels
227 sugg
.substitutions
[0].parts
.len() == 1 &&
228 // don't display long messages as labels
229 sugg
.msg
.split_whitespace().count() < 10 &&
230 // don't display multiline suggestions as labels
231 !sugg
.substitutions
[0].parts
[0].snippet
.contains('
\n'
) &&
233 // when this style is set we want the suggestion to be a message, not inline
234 SuggestionStyle
::HideCodeAlways
,
235 // trivial suggestion for tooling's sake, never shown
236 SuggestionStyle
::CompletelyHidden
,
237 // subtle suggestion, never shown inline
238 SuggestionStyle
::ShowAlways
,
239 ].contains(&sugg
.style
)
241 let substitution
= &sugg
.substitutions
[0].parts
[0].snippet
.trim();
242 let msg
= if substitution
.len() == 0 || sugg
.style
.hide_inline() {
243 // This substitution is only removal OR we explicitly don't want to show the
244 // code inline (`hide_inline`). Therefore, we don't show the substitution.
245 format
!("help: {}", sugg
.msg
)
247 // Show the default suggestion text with the substitution
253 .map(|sm
| is_case_difference(
256 sugg
.substitutions
[0].parts
[0].span
,
260 " (notice the capitalization)"
267 primary_span
.push_span_label(sugg
.substitutions
[0].parts
[0].span
, msg
);
269 // We return only the modified primary_span
272 // if there are multiple suggestions, print them all in full
273 // to be consistent. We could try to figure out if we can
274 // make one (or the first one) inline, but that would give
275 // undue importance to a semi-random suggestion
276 (primary_span
, &diag
.suggestions
)
279 (primary_span
, &diag
.suggestions
)
283 // This does a small "fix" for multispans by looking to see if it can find any that
284 // point directly at <*macros>. Since these are often difficult to read, this
285 // will change the span to point at the use site.
286 fn fix_multispans_in_std_macros(
288 source_map
: &Option
<Lrc
<SourceMap
>>,
289 span
: &mut MultiSpan
,
290 children
: &mut Vec
<SubDiagnostic
>,
294 let mut spans_updated
= self.fix_multispan_in_std_macros(source_map
, span
, backtrace
);
295 for child
in children
.iter_mut() {
297 self.fix_multispan_in_std_macros(source_map
, &mut child
.span
, backtrace
);
299 let msg
= if level
== &Error
{
300 "this error originates in a macro outside of the current crate \
301 (in Nightly builds, run with -Z external-macro-backtrace \
305 "this warning originates in a macro outside of the current crate \
306 (in Nightly builds, run with -Z external-macro-backtrace \
312 children
.push(SubDiagnostic
{
314 message
: vec
![(msg
, Style
::NoStyle
)],
315 span
: MultiSpan
::new(),
321 // This "fixes" MultiSpans that contain Spans that are pointing to locations inside of
322 // <*macros>. Since these locations are often difficult to read, we move these Spans from
323 // <*macros> to their corresponding use site.
324 fn fix_multispan_in_std_macros(
326 source_map
: &Option
<Lrc
<SourceMap
>>,
327 span
: &mut MultiSpan
,
328 always_backtrace
: bool
,
330 let sm
= match source_map
{
332 None
=> return false,
335 let mut before_after
: Vec
<(Span
, Span
)> = vec
![];
336 let mut new_labels
: Vec
<(Span
, String
)> = vec
![];
338 // First, find all the spans in <*macros> and point instead at their use site
339 for sp
in span
.primary_spans() {
343 let call_sp
= sm
.call_span_if_macro(*sp
);
344 if call_sp
!= *sp
&& !always_backtrace
{
345 before_after
.push((*sp
, call_sp
));
347 let backtrace_len
= sp
.macro_backtrace().len();
348 for (i
, trace
) in sp
.macro_backtrace().iter().rev().enumerate() {
349 // Only show macro locations that are local
350 // and display them like a span_note
351 if trace
.def_site_span
.is_dummy() {
354 if always_backtrace
{
358 "in this expansion of `{}`{}",
359 trace
.macro_decl_name
,
360 if backtrace_len
> 2 {
361 // if backtrace_len == 1 it'll be pointed
362 // at by "in this macro invocation"
363 format
!(" (#{})", i
+ 1)
370 // Check to make sure we're not in any <*macros>
371 if !sm
.span_to_filename(trace
.def_site_span
).is_macros()
372 && !trace
.macro_decl_name
.starts_with("desugaring of ")
373 && !trace
.macro_decl_name
.starts_with("#[")
379 "in this macro invocation{}",
380 if backtrace_len
> 2 && always_backtrace
{
381 // only specify order when the macro
382 // backtrace is multiple levels deep
383 format
!(" (#{})", i
+ 1)
389 if !always_backtrace
{
395 for (label_span
, label_text
) in new_labels
{
396 span
.push_span_label(label_span
, label_text
);
398 for sp_label
in span
.span_labels() {
399 if sp_label
.span
.is_dummy() {
402 if sm
.span_to_filename(sp_label
.span
.clone()).is_macros() && !always_backtrace
{
403 let v
= sp_label
.span
.macro_backtrace();
404 if let Some(use_site
) = v
.last() {
405 before_after
.push((sp_label
.span
.clone(), use_site
.call_site
.clone()));
409 // After we have them, make sure we replace these 'bad' def sites with their use sites
410 let spans_updated
= !before_after
.is_empty();
411 for (before
, after
) in before_after
{
412 span
.replace(before
, after
);
419 impl Emitter
for EmitterWriter
{
420 fn source_map(&self) -> Option
<&Lrc
<SourceMap
>> {
424 fn emit_diagnostic(&mut self, diag
: &Diagnostic
) {
425 let mut children
= diag
.children
.clone();
426 let (mut primary_span
, suggestions
) = self.primary_span_formatted(&diag
);
428 self.fix_multispans_in_std_macros(
433 self.external_macro_backtrace
,
436 self.emit_messages_default(
438 &diag
.styled_message(),
446 fn should_show_explain(&self) -> bool
{
451 /// An emitter that does nothing when emitting a diagnostic.
452 pub struct SilentEmitter
;
454 impl Emitter
for SilentEmitter
{
455 fn source_map(&self) -> Option
<&Lrc
<SourceMap
>> {
458 fn emit_diagnostic(&mut self, _
: &Diagnostic
) {}
461 /// maximum number of lines we will print for each error; arbitrary.
462 pub const MAX_HIGHLIGHT_LINES
: usize = 6;
463 /// maximum number of suggestions to be shown
465 /// Arbitrary, but taken from trait import suggestion limit
466 pub const MAX_SUGGESTIONS
: usize = 4;
468 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
469 pub enum ColorConfig
{
476 fn to_color_choice(self) -> ColorChoice
{
478 ColorConfig
::Always
=> {
479 if atty
::is(atty
::Stream
::Stderr
) {
482 ColorChoice
::AlwaysAnsi
485 ColorConfig
::Never
=> ColorChoice
::Never
,
486 ColorConfig
::Auto
if atty
::is(atty
::Stream
::Stderr
) => ColorChoice
::Auto
,
487 ColorConfig
::Auto
=> ColorChoice
::Never
,
490 fn suggests_using_colors(self) -> bool
{
492 ColorConfig
::Always
| ColorConfig
::Auto
=> true,
493 ColorConfig
::Never
=> false,
498 /// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short`
499 pub struct EmitterWriter
{
501 sm
: Option
<Lrc
<SourceMap
>>,
505 terminal_width
: Option
<usize>,
507 external_macro_backtrace
: bool
,
511 pub struct FileWithAnnotatedLines
{
512 pub file
: Lrc
<SourceFile
>,
513 pub lines
: Vec
<Line
>,
514 multiline_depth
: usize,
519 color_config
: ColorConfig
,
520 source_map
: Option
<Lrc
<SourceMap
>>,
523 terminal_width
: Option
<usize>,
524 external_macro_backtrace
: bool
,
526 let dst
= Destination
::from_stderr(color_config
);
534 external_macro_backtrace
,
539 dst
: Box
<dyn Write
+ Send
>,
540 source_map
: Option
<Lrc
<SourceMap
>>,
544 terminal_width
: Option
<usize>,
545 external_macro_backtrace
: bool
,
548 dst
: Raw(dst
, colored
),
554 external_macro_backtrace
,
558 pub fn ui_testing(mut self, ui_testing
: bool
) -> Self {
559 self.ui_testing
= ui_testing
;
563 fn maybe_anonymized(&self, line_num
: usize) -> String
{
564 if self.ui_testing { ANONYMIZED_LINE_NUM.to_string() }
else { line_num.to_string() }
569 buffer
: &mut StyledBuffer
,
577 let line_len
= source_string
.len();
578 // Create the source line we will highlight.
579 let left
= margin
.left(line_len
);
580 let right
= margin
.right(line_len
);
581 // On long lines, we strip the source line, accounting for unicode.
583 let code
: String
= source_string
587 // Make sure that the trimming on the right will fall within the
588 // terminal width. FIXME: `unicode_width` sometimes disagrees
589 // with terminals on how wide a `char` is. For now, just accept
590 // that sometimes the code line will be longer than desired.
591 let next
= unicode_width
::UnicodeWidthChar
::width(*ch
).unwrap_or(1);
592 if taken
+ next
> right
- left
{
599 buffer
.puts(line_offset
, code_offset
, &code
, Style
::Quotation
);
600 if margin
.was_cut_left() {
601 // We have stripped some code/whitespace from the beginning, make it clear.
602 buffer
.puts(line_offset
, code_offset
, "...", Style
::LineNumber
);
604 if margin
.was_cut_right(line_len
) {
605 // We have stripped some code after the right-most span end, make it clear we did so.
606 buffer
.puts(line_offset
, code_offset
+ taken
- 3, "...", Style
::LineNumber
);
608 buffer
.puts(line_offset
, 0, &self.maybe_anonymized(line_index
), Style
::LineNumber
);
610 draw_col_separator(buffer
, line_offset
, width_offset
- 2);
613 fn render_source_line(
615 buffer
: &mut StyledBuffer
,
616 file
: Lrc
<SourceFile
>,
621 ) -> Vec
<(usize, Style
)> {
627 // | secondary span label
629 // ^^ ^ ^^^ ^^^^ ^^^ we don't care about code too far to the right of a span, we trim it
631 // | | | actual code found in your source code and the spans we use to mark it
632 // | | when there's too much wasted space to the left, trim it
633 // | vertical divider between the column number and the code
636 if line
.line_index
== 0 {
640 let source_string
= match file
.get_line(line
.line_index
- 1) {
642 None
=> return Vec
::new(),
645 let line_offset
= buffer
.num_lines();
647 let left
= margin
.left(source_string
.len()); // Left trim
648 // Account for unicode characters of width !=0 that were removed.
649 let left
= source_string
652 .map(|ch
| unicode_width
::UnicodeWidthChar
::width(ch
).unwrap_or(1))
665 // Special case when there's only one annotation involved, it is the start of a multiline
666 // span and there's no text at the beginning of the code line. Instead of doing the whole
675 // we simplify the output to:
681 if let [ann
] = &line
.annotations
[..] {
682 if let AnnotationType
::MultilineStart(depth
) = ann
.annotation_type
{
683 if source_string
.chars().take(ann
.start_col
).all(|c
| c
.is_whitespace()) {
684 let style
= if ann
.is_primary
{
685 Style
::UnderlinePrimary
687 Style
::UnderlineSecondary
689 buffer
.putc(line_offset
, width_offset
+ depth
- 1, '
/'
, style
);
690 return vec
![(depth
, style
)];
695 // We want to display like this:
697 // vec.push(vec.pop().unwrap());
698 // --- ^^^ - previous borrow ends here
700 // | error occurs here
701 // previous borrow of `vec` occurs here
703 // But there are some weird edge cases to be aware of:
705 // vec.push(vec.pop().unwrap());
706 // -------- - previous borrow ends here
708 // |this makes no sense
709 // previous borrow of `vec` occurs here
711 // For this reason, we group the lines into "highlight lines"
712 // and "annotations lines", where the highlight lines have the `^`.
714 // Sort the annotations by (start, end col)
715 // The labels are reversed, sort and then reversed again.
716 // Consider a list of annotations (A1, A2, C1, C2, B1, B2) where
717 // the letter signifies the span. Here we are only sorting by the
718 // span and hence, the order of the elements with the same span will
719 // not change. On reversing the ordering (|a, b| but b.cmp(a)), you get
720 // (C1, C2, B1, B2, A1, A2). All the elements with the same span are
721 // still ordered first to last, but all the elements with different
722 // spans are ordered by their spans in last to first order. Last to
723 // first order is important, because the jiggly lines and | are on
724 // the left, so the rightmost span needs to be rendered first,
725 // otherwise the lines would end up needing to go over a message.
727 let mut annotations
= line
.annotations
.clone();
728 annotations
.sort_by_key(|a
| Reverse(a
.start_col
));
730 // First, figure out where each label will be positioned.
732 // In the case where you have the following annotations:
734 // vec.push(vec.pop().unwrap());
735 // -------- - previous borrow ends here [C]
737 // |this makes no sense [B]
738 // previous borrow of `vec` occurs here [A]
740 // `annotations_position` will hold [(2, A), (1, B), (0, C)].
742 // We try, when possible, to stick the rightmost annotation at the end
743 // of the highlight line:
745 // vec.push(vec.pop().unwrap());
746 // --- --- - previous borrow ends here
748 // But sometimes that's not possible because one of the other
749 // annotations overlaps it. For example, from the test
750 // `span_overlap_label`, we have the following annotations
751 // (written on distinct lines for clarity):
757 // In this case, we can't stick the rightmost-most label on
758 // the highlight line, or we would get:
765 // which is totally weird. Instead we want:
773 // which is...less weird, at least. In fact, in general, if
774 // the rightmost span overlaps with any other span, we should
775 // use the "hang below" version, so we can at least make it
776 // clear where the span *starts*. There's an exception for this
777 // logic, when the labels do not have a message:
792 let mut annotations_position
= vec
![];
793 let mut line_len
= 0;
795 for (i
, annotation
) in annotations
.iter().enumerate() {
796 for (j
, next
) in annotations
.iter().enumerate() {
797 if overlaps(next
, annotation
, 0) // This label overlaps with another one and both
798 && annotation
.has_label() // take space (they have text and are not
799 && j
> i
// multiline lines).
801 // We're currently on the first line, move the label one line down
803 // If we're overlapping with an un-labelled annotation with the same span
804 // we can just merge them in the output
805 if next
.start_col
== annotation
.start_col
806 && next
.end_col
== annotation
.end_col
812 // This annotation needs a new line in the output.
817 annotations_position
.push((p
, annotation
));
818 for (j
, next
) in annotations
.iter().enumerate() {
820 let l
= next
.label
.as_ref().map_or(0, |label
| label
.len() + 2);
821 if (overlaps(next
, annotation
, l
) // Do not allow two labels to be in the same
822 // line if they overlap including padding, to
823 // avoid situations like:
830 && annotation
.has_label() // Both labels must have some text, otherwise
831 && next
.has_label()) // they are not overlapping.
832 // Do not add a new line if this annotation
833 // or the next are vertical line placeholders.
834 || (annotation
.takes_space() // If either this or the next annotation is
835 && next
.has_label()) // multiline start/end, move it to a new line
836 || (annotation
.has_label() // so as not to overlap the orizontal lines.
837 && next
.takes_space())
838 || (annotation
.takes_space() && next
.takes_space())
839 || (overlaps(next
, annotation
, l
)
840 && next
.end_col
<= annotation
.end_col
845 // This annotation needs a new line in the output.
851 line_len
= max(line_len
, p
);
858 // If there are no annotations or the only annotations on this line are
859 // MultilineLine, then there's only code being shown, stop processing.
860 if line
.annotations
.iter().all(|a
| a
.is_line()) {
864 // Write the column separator.
866 // After this we will have:
875 for pos
in 0..=line_len
{
876 draw_col_separator(buffer
, line_offset
+ pos
+ 1, width_offset
- 2);
877 buffer
.putc(line_offset
+ pos
+ 1, width_offset
- 2, '
|'
, Style
::LineNumber
);
880 // Write the horizontal lines for multiline annotations
881 // (only the first and last lines need this).
883 // After this we will have:
892 for &(pos
, annotation
) in &annotations_position
{
893 let style
= if annotation
.is_primary
{
894 Style
::UnderlinePrimary
896 Style
::UnderlineSecondary
899 match annotation
.annotation_type
{
900 AnnotationType
::MultilineStart(depth
) | AnnotationType
::MultilineEnd(depth
) => {
905 width_offset
+ depth
,
906 code_offset
+ annotation
.start_col
- left
,
911 buffer
.set_style_range(
913 code_offset
+ annotation
.start_col
- left
,
914 code_offset
+ annotation
.end_col
- left
,
916 annotation
.is_primary
,
923 // Write the vertical lines for labels that are on a different line as the underline.
925 // After this we will have:
934 for &(pos
, annotation
) in &annotations_position
{
935 let style
= if annotation
.is_primary
{
936 Style
::UnderlinePrimary
938 Style
::UnderlineSecondary
942 if pos
> 1 && (annotation
.has_label() || annotation
.takes_space()) {
943 for p
in line_offset
+ 1..=line_offset
+ pos
{
946 (code_offset
+ annotation
.start_col
).saturating_sub(left
),
952 match annotation
.annotation_type
{
953 AnnotationType
::MultilineStart(depth
) => {
954 for p
in line_offset
+ pos
+ 1..line_offset
+ line_len
+ 2 {
955 buffer
.putc(p
, width_offset
+ depth
- 1, '
|'
, style
);
958 AnnotationType
::MultilineEnd(depth
) => {
959 for p
in line_offset
..=line_offset
+ pos
{
960 buffer
.putc(p
, width_offset
+ depth
- 1, '
|'
, style
);
967 // Write the labels on the annotations that actually have a label.
969 // After this we will have:
974 // | something about `foo`
978 for &(pos
, annotation
) in &annotations_position
{
980 if annotation
.is_primary { Style::LabelPrimary }
else { Style::LabelSecondary }
;
981 let (pos
, col
) = if pos
== 0 {
982 (pos
+ 1, (annotation
.end_col
+ 1).saturating_sub(left
))
984 (pos
+ 2, annotation
.start_col
.saturating_sub(left
))
986 if let Some(ref label
) = annotation
.label
{
987 buffer
.puts(line_offset
+ pos
, code_offset
+ col
, &label
, style
);
991 // Sort from biggest span to smallest span so that smaller spans are
992 // represented in the output:
997 // | | something about `foo`
998 // | something about `fn foo()`
999 annotations_position
.sort_by_key(|(_
, ann
)| {
1000 // Decreasing order. When annotations share the same length, prefer `Primary`.
1001 (Reverse(ann
.len()), ann
.is_primary
)
1004 // Write the underlines.
1006 // After this we will have:
1011 // | something about `foo`
1015 for &(_
, annotation
) in &annotations_position
{
1016 let (underline
, style
) = if annotation
.is_primary
{
1017 ('
^', Style
::UnderlinePrimary
)
1019 ('
-'
, Style
::UnderlineSecondary
)
1021 for p
in annotation
.start_col
..annotation
.end_col
{
1024 (code_offset
+ p
).saturating_sub(left
),
1030 annotations_position
1032 .filter_map(|&(_
, annotation
)| match annotation
.annotation_type
{
1033 AnnotationType
::MultilineStart(p
) | AnnotationType
::MultilineEnd(p
) => {
1034 let style
= if annotation
.is_primary
{
1037 Style
::LabelSecondary
1043 .collect
::<Vec
<_
>>()
1046 fn get_multispan_max_line_num(&mut self, msp
: &MultiSpan
) -> usize {
1047 let sm
= match self.sm
{
1053 for primary_span
in msp
.primary_spans() {
1054 if !primary_span
.is_dummy() {
1055 let hi
= sm
.lookup_char_pos(primary_span
.hi());
1056 max
= (hi
.line
).max(max
);
1059 if !self.short_message
{
1060 for span_label
in msp
.span_labels() {
1061 if !span_label
.span
.is_dummy() {
1062 let hi
= sm
.lookup_char_pos(span_label
.span
.hi());
1063 max
= (hi
.line
).max(max
);
1071 fn get_max_line_num(&mut self, span
: &MultiSpan
, children
: &[SubDiagnostic
]) -> usize {
1072 let primary
= self.get_multispan_max_line_num(span
);
1075 .map(|sub
| self.get_multispan_max_line_num(&sub
.span
))
1081 /// Adds a left margin to every line but the first, given a padding length and the label being
1082 /// displayed, keeping the provided highlighting.
1085 buffer
: &mut StyledBuffer
,
1086 msg
: &[(String
, Style
)],
1089 override_style
: Option
<Style
>,
1091 // The extra 5 ` ` is padding that's always needed to align to the `note: `:
1094 // --> file.rs:13:20
1099 // = note: multiline
1104 // | | length of label
1106 // `max_line_num_len`
1107 let padding
= " ".repeat(padding
+ label
.len() + 5);
1109 /// Returns `override` if it is present and `style` is `NoStyle` or `style` otherwise
1110 fn style_or_override(style
: Style
, override_
: Option
<Style
>) -> Style
{
1111 match (style
, override_
) {
1112 (Style
::NoStyle
, Some(override_
)) => override_
,
1117 let mut line_number
= 0;
1119 // Provided the following diagnostic message:
1123 // ("highlighted multiline\nstring to\nsee how it ", Style::NoStyle),
1124 // ("looks", Style::Highlight),
1125 // ("with\nvery ", Style::NoStyle),
1126 // ("weird", Style::Highlight),
1127 // (" formats\n", Style::NoStyle),
1128 // ("see?", Style::Highlight),
1131 // the expected output on a note is (* surround the highlighted text)
1133 // = note: highlighted multiline
1135 // see how it *looks* with
1136 // very *weird* formats
1138 for &(ref text
, ref style
) in msg
.iter() {
1139 let lines
= text
.split('
\n'
).collect
::<Vec
<_
>>();
1140 if lines
.len() > 1 {
1141 for (i
, line
) in lines
.iter().enumerate() {
1144 buffer
.append(line_number
, &padding
, Style
::NoStyle
);
1146 buffer
.append(line_number
, line
, style_or_override(*style
, override_style
));
1149 buffer
.append(line_number
, text
, style_or_override(*style
, override_style
));
1154 fn emit_message_default(
1157 msg
: &[(String
, Style
)],
1158 code
: &Option
<DiagnosticId
>,
1160 max_line_num_len
: usize,
1162 ) -> io
::Result
<()> {
1163 let mut buffer
= StyledBuffer
::new();
1164 let header_style
= if is_secondary { Style::HeaderMsg }
else { Style::MainHeaderMsg }
;
1166 if !msp
.has_primary_spans() && !msp
.has_span_labels() && is_secondary
&& !self.short_message
1168 // This is a secondary message with no span info
1169 for _
in 0..max_line_num_len
{
1170 buffer
.prepend(0, " ", Style
::NoStyle
);
1172 draw_note_separator(&mut buffer
, 0, max_line_num_len
+ 1);
1173 if *level
!= Level
::FailureNote
{
1174 let level_str
= level
.to_string();
1175 if !level_str
.is_empty() {
1176 buffer
.append(0, &level_str
, Style
::MainHeaderMsg
);
1177 buffer
.append(0, ": ", Style
::NoStyle
);
1180 self.msg_to_buffer(&mut buffer
, msg
, max_line_num_len
, "note", None
);
1182 let level_str
= level
.to_string();
1183 // The failure note level itself does not provide any useful diagnostic information
1184 if *level
!= Level
::FailureNote
&& !level_str
.is_empty() {
1185 buffer
.append(0, &level_str
, Style
::Level(level
.clone()));
1187 // only render error codes, not lint codes
1188 if let Some(DiagnosticId
::Error(ref code
)) = *code
{
1189 buffer
.append(0, "[", Style
::Level(level
.clone()));
1190 buffer
.append(0, &code
, Style
::Level(level
.clone()));
1191 buffer
.append(0, "]", Style
::Level(level
.clone()));
1193 if *level
!= Level
::FailureNote
&& !level_str
.is_empty() {
1194 buffer
.append(0, ": ", header_style
);
1196 for &(ref text
, _
) in msg
.iter() {
1197 buffer
.append(0, text
, header_style
);
1201 let mut annotated_files
= FileWithAnnotatedLines
::collect_annotations(msp
, &self.sm
);
1203 // Make sure our primary file comes first
1204 let (primary_lo
, sm
) = if let (Some(sm
), Some(ref primary_span
)) =
1205 (self.sm
.as_ref(), msp
.primary_span().as_ref())
1207 if !primary_span
.is_dummy() {
1208 (sm
.lookup_char_pos(primary_span
.lo()), sm
)
1210 emit_to_destination(&buffer
.render(), level
, &mut self.dst
, self.short_message
)?
;
1214 // If we don't have span information, emit and exit
1215 emit_to_destination(&buffer
.render(), level
, &mut self.dst
, self.short_message
)?
;
1219 annotated_files
.binary_search_by(|x
| x
.file
.name
.cmp(&primary_lo
.file
.name
))
1221 annotated_files
.swap(0, pos
);
1224 // Print out the annotate source lines that correspond with the error
1225 for annotated_file
in annotated_files
{
1226 // we can't annotate anything if the source is unavailable.
1227 if !sm
.ensure_source_file_source_present(annotated_file
.file
.clone()) {
1231 // print out the span location and spacer before we print the annotated source
1232 // to do this, we need to know if this span will be primary
1233 let is_primary
= primary_lo
.file
.name
== annotated_file
.file
.name
;
1235 let loc
= primary_lo
.clone();
1236 if !self.short_message
{
1237 // remember where we are in the output buffer for easy reference
1238 let buffer_msg_line_offset
= buffer
.num_lines();
1240 buffer
.prepend(buffer_msg_line_offset
, "--> ", Style
::LineNumber
);
1242 buffer_msg_line_offset
,
1246 sm
.doctest_offset_line(&loc
.file
.name
, loc
.line
),
1249 Style
::LineAndColumn
,
1251 for _
in 0..max_line_num_len
{
1252 buffer
.prepend(buffer_msg_line_offset
, " ", Style
::NoStyle
);
1260 sm
.doctest_offset_line(&loc
.file
.name
, loc
.line
),
1263 Style
::LineAndColumn
,
1266 } else if !self.short_message
{
1267 // remember where we are in the output buffer for easy reference
1268 let buffer_msg_line_offset
= buffer
.num_lines();
1271 draw_col_separator(&mut buffer
, buffer_msg_line_offset
, max_line_num_len
+ 1);
1273 // Then, the secondary file indicator
1274 buffer
.prepend(buffer_msg_line_offset
+ 1, "::: ", Style
::LineNumber
);
1275 let loc
= if let Some(first_line
) = annotated_file
.lines
.first() {
1276 let col
= if let Some(first_annotation
) = first_line
.annotations
.first() {
1277 format
!(":{}", first_annotation
.start_col
+ 1)
1283 annotated_file
.file
.name
,
1284 sm
.doctest_offset_line(&annotated_file
.file
.name
, first_line
.line_index
),
1288 annotated_file
.file
.name
.to_string()
1290 buffer
.append(buffer_msg_line_offset
+ 1, &loc
, Style
::LineAndColumn
);
1291 for _
in 0..max_line_num_len
{
1292 buffer
.prepend(buffer_msg_line_offset
+ 1, " ", Style
::NoStyle
);
1296 if !self.short_message
{
1297 // Put in the spacer between the location and annotated source
1298 let buffer_msg_line_offset
= buffer
.num_lines();
1299 draw_col_separator_no_space(
1301 buffer_msg_line_offset
,
1302 max_line_num_len
+ 1,
1305 // Contains the vertical lines' positions for active multiline annotations
1306 let mut multilines
= FxHashMap
::default();
1308 // Get the left-side margin to remove it
1309 let mut whitespace_margin
= std
::usize::MAX
;
1310 for line_idx
in 0..annotated_file
.lines
.len() {
1311 let file
= annotated_file
.file
.clone();
1312 let line
= &annotated_file
.lines
[line_idx
];
1313 if let Some(source_string
) = file
.get_line(line
.line_index
- 1) {
1314 let leading_whitespace
=
1315 source_string
.chars().take_while(|c
| c
.is_whitespace()).count();
1316 if source_string
.chars().any(|c
| !c
.is_whitespace()) {
1317 whitespace_margin
= min(whitespace_margin
, leading_whitespace
);
1321 if whitespace_margin
== std
::usize::MAX
{
1322 whitespace_margin
= 0;
1325 // Left-most column any visible span points at.
1326 let mut span_left_margin
= std
::usize::MAX
;
1327 for line
in &annotated_file
.lines
{
1328 for ann
in &line
.annotations
{
1329 span_left_margin
= min(span_left_margin
, ann
.start_col
);
1330 span_left_margin
= min(span_left_margin
, ann
.end_col
);
1333 if span_left_margin
== std
::usize::MAX
{
1334 span_left_margin
= 0;
1337 // Right-most column any visible span points at.
1338 let mut span_right_margin
= 0;
1339 let mut label_right_margin
= 0;
1340 let mut max_line_len
= 0;
1341 for line
in &annotated_file
.lines
{
1344 annotated_file
.file
.get_line(line
.line_index
- 1).map_or(0, |s
| s
.len()),
1346 for ann
in &line
.annotations
{
1347 span_right_margin
= max(span_right_margin
, ann
.start_col
);
1348 span_right_margin
= max(span_right_margin
, ann
.end_col
);
1349 // FIXME: account for labels not in the same line
1350 let label_right
= ann
.label
.as_ref().map_or(0, |l
| l
.len() + 1);
1351 label_right_margin
= max(label_right_margin
, ann
.end_col
+ label_right
);
1355 let width_offset
= 3 + max_line_num_len
;
1356 let code_offset
= if annotated_file
.multiline_depth
== 0 {
1359 width_offset
+ annotated_file
.multiline_depth
+ 1
1362 let column_width
= if let Some(width
) = self.terminal_width
{
1363 width
.saturating_sub(code_offset
)
1364 } else if self.ui_testing
{
1367 term_size
::dimensions()
1368 .map(|(w
, _
)| w
.saturating_sub(code_offset
))
1369 .unwrap_or(std
::usize::MAX
)
1372 let margin
= Margin
::new(
1381 // Next, output the annotate source for this file
1382 for line_idx
in 0..annotated_file
.lines
.len() {
1383 let previous_buffer_line
= buffer
.num_lines();
1385 let depths
= self.render_source_line(
1387 annotated_file
.file
.clone(),
1388 &annotated_file
.lines
[line_idx
],
1394 let mut to_add
= FxHashMap
::default();
1396 for (depth
, style
) in depths
{
1397 if multilines
.get(&depth
).is_some() {
1398 multilines
.remove(&depth
);
1400 to_add
.insert(depth
, style
);
1404 // Set the multiline annotation vertical lines to the left of
1405 // the code in this line.
1406 for (depth
, style
) in &multilines
{
1407 for line
in previous_buffer_line
..buffer
.num_lines() {
1408 draw_multiline_line(&mut buffer
, line
, width_offset
, *depth
, *style
);
1411 // check to see if we need to print out or elide lines that come between
1412 // this annotated line and the next one.
1413 if line_idx
< (annotated_file
.lines
.len() - 1) {
1414 let line_idx_delta
= annotated_file
.lines
[line_idx
+ 1].line_index
1415 - annotated_file
.lines
[line_idx
].line_index
;
1416 if line_idx_delta
> 2 {
1417 let last_buffer_line_num
= buffer
.num_lines();
1418 buffer
.puts(last_buffer_line_num
, 0, "...", Style
::LineNumber
);
1420 // Set the multiline annotation vertical lines on `...` bridging line.
1421 for (depth
, style
) in &multilines
{
1422 draw_multiline_line(
1424 last_buffer_line_num
,
1430 } else if line_idx_delta
== 2 {
1431 let unannotated_line
= annotated_file
1433 .get_line(annotated_file
.lines
[line_idx
].line_index
)
1434 .unwrap_or_else(|| Cow
::from(""));
1436 let last_buffer_line_num
= buffer
.num_lines();
1441 annotated_file
.lines
[line_idx
+ 1].line_index
- 1,
1442 last_buffer_line_num
,
1448 for (depth
, style
) in &multilines
{
1449 draw_multiline_line(
1451 last_buffer_line_num
,
1460 multilines
.extend(&to_add
);
1465 // final step: take our styled buffer, render it, then output it
1466 emit_to_destination(&buffer
.render(), level
, &mut self.dst
, self.short_message
)?
;
1471 fn emit_suggestion_default(
1473 suggestion
: &CodeSuggestion
,
1475 max_line_num_len
: usize,
1476 ) -> io
::Result
<()> {
1477 let sm
= match self.sm
{
1479 None
=> return Ok(()),
1482 // Render the replacements for each suggestion
1483 let suggestions
= suggestion
.splice_lines(&**sm
);
1485 if suggestions
.is_empty() {
1486 // Suggestions coming from macros can have malformed spans. This is a heavy handed
1487 // approach to avoid ICEs by ignoring the suggestion outright.
1491 let mut buffer
= StyledBuffer
::new();
1493 // Render the suggestion message
1494 let level_str
= level
.to_string();
1495 if !level_str
.is_empty() {
1496 buffer
.append(0, &level_str
, Style
::Level(level
.clone()));
1497 buffer
.append(0, ": ", Style
::HeaderMsg
);
1501 &[(suggestion
.msg
.to_owned(), Style
::NoStyle
)],
1504 Some(Style
::HeaderMsg
),
1507 let mut row_num
= 2;
1508 let mut notice_capitalization
= false;
1509 for (complete
, parts
, only_capitalization
) in suggestions
.iter().take(MAX_SUGGESTIONS
) {
1510 notice_capitalization
|= only_capitalization
;
1511 // Only show underline if the suggestion spans a single line and doesn't cover the
1512 // entirety of the code output. If you have multiple replacements in the same line
1513 // of code, show the underline.
1514 let show_underline
= !(parts
.len() == 1 && parts
[0].snippet
.trim() == complete
.trim())
1515 && complete
.lines().count() == 1;
1518 .span_to_lines(parts
[0].span
)
1519 .expect("span_to_lines failed when emitting suggestion");
1521 assert
!(!lines
.lines
.is_empty());
1523 let line_start
= sm
.lookup_char_pos(parts
[0].span
.lo()).line
;
1524 draw_col_separator_no_space(&mut buffer
, 1, max_line_num_len
+ 1);
1525 let mut line_pos
= 0;
1526 let mut lines
= complete
.lines();
1527 for line
in lines
.by_ref().take(MAX_HIGHLIGHT_LINES
) {
1528 // Print the span column to avoid confusion
1532 &self.maybe_anonymized(line_start
+ line_pos
),
1535 // print the suggestion
1536 draw_col_separator(&mut buffer
, row_num
, max_line_num_len
+ 1);
1537 buffer
.append(row_num
, line
, Style
::NoStyle
);
1542 // This offset and the ones below need to be signed to account for replacement code
1543 // that is shorter than the original code.
1544 let mut offset
: isize = 0;
1545 // Only show an underline in the suggestions if the suggestion is not the
1546 // entirety of the code being shown and the displayed code is not multiline.
1548 draw_col_separator(&mut buffer
, row_num
, max_line_num_len
+ 1);
1550 let span_start_pos
= sm
.lookup_char_pos(part
.span
.lo()).col_display
;
1551 let span_end_pos
= sm
.lookup_char_pos(part
.span
.hi()).col_display
;
1553 // Do not underline the leading...
1554 let start
= part
.snippet
.len().saturating_sub(part
.snippet
.trim_start().len());
1555 // ...or trailing spaces. Account for substitutions containing unicode
1557 let sub_len
: usize = part
1561 .map(|ch
| unicode_width
::UnicodeWidthChar
::width(ch
).unwrap_or(1))
1564 let underline_start
= (span_start_pos
+ start
) as isize + offset
;
1565 let underline_end
= (span_start_pos
+ start
+ sub_len
) as isize + offset
;
1566 for p
in underline_start
..underline_end
{
1569 max_line_num_len
+ 3 + p
as usize,
1571 Style
::UnderlinePrimary
,
1574 // underline removals too
1575 if underline_start
== underline_end
{
1576 for p
in underline_start
- 1..underline_start
+ 1 {
1579 max_line_num_len
+ 3 + p
as usize,
1581 Style
::UnderlineSecondary
,
1586 // length of the code after substitution
1587 let full_sub_len
= part
1590 .map(|ch
| unicode_width
::UnicodeWidthChar
::width(ch
).unwrap_or(1))
1591 .sum
::<usize>() as isize;
1593 // length of the code to be substituted
1594 let snippet_len
= span_end_pos
as isize - span_start_pos
as isize;
1595 // For multiple substitutions, use the position *after* the previous
1596 // substitutions have happened.
1597 offset
+= full_sub_len
- snippet_len
;
1602 // if we elided some lines, add an ellipsis
1603 if lines
.next().is_some() {
1604 buffer
.puts(row_num
, max_line_num_len
- 1, "...", Style
::LineNumber
);
1605 } else if !show_underline
{
1606 draw_col_separator_no_space(&mut buffer
, row_num
, max_line_num_len
+ 1);
1610 if suggestions
.len() > MAX_SUGGESTIONS
{
1611 let others
= suggestions
.len() - MAX_SUGGESTIONS
;
1612 let msg
= format
!("and {} other candidate{}", others
, pluralize
!(others
));
1613 buffer
.puts(row_num
, max_line_num_len
+ 3, &msg
, Style
::NoStyle
);
1614 } else if notice_capitalization
{
1615 let msg
= "notice the capitalization difference";
1616 buffer
.puts(row_num
, max_line_num_len
+ 3, &msg
, Style
::NoStyle
);
1618 emit_to_destination(&buffer
.render(), level
, &mut self.dst
, self.short_message
)?
;
1622 fn emit_messages_default(
1625 message
: &[(String
, Style
)],
1626 code
: &Option
<DiagnosticId
>,
1628 children
: &[SubDiagnostic
],
1629 suggestions
: &[CodeSuggestion
],
1631 let max_line_num_len
= if self.ui_testing
{
1632 ANONYMIZED_LINE_NUM
.len()
1634 self.get_max_line_num(span
, children
).to_string().len()
1637 match self.emit_message_default(span
, message
, code
, level
, max_line_num_len
, false) {
1639 if !children
.is_empty()
1640 || suggestions
.iter().any(|s
| s
.style
!= SuggestionStyle
::CompletelyHidden
)
1642 let mut buffer
= StyledBuffer
::new();
1643 if !self.short_message
{
1644 draw_col_separator_no_space(&mut buffer
, 0, max_line_num_len
+ 1);
1646 match emit_to_destination(
1653 Err(e
) => panic
!("failed to emit error: {}", e
),
1656 if !self.short_message
{
1657 for child
in children
{
1658 let span
= child
.render_span
.as_ref().unwrap_or(&child
.span
);
1659 match self.emit_message_default(
1661 &child
.styled_message(),
1667 Err(e
) => panic
!("failed to emit error: {}", e
),
1671 for sugg
in suggestions
{
1672 if sugg
.style
== SuggestionStyle
::CompletelyHidden
{
1673 // do not display this suggestion, it is meant only for tools
1674 } else if sugg
.style
== SuggestionStyle
::HideCodeAlways
{
1675 match self.emit_message_default(
1677 &[(sugg
.msg
.to_owned(), Style
::HeaderMsg
)],
1683 Err(e
) => panic
!("failed to emit error: {}", e
),
1687 match self.emit_suggestion_default(sugg
, &Level
::Help
, max_line_num_len
)
1689 Err(e
) => panic
!("failed to emit error: {}", e
),
1696 Err(e
) => panic
!("failed to emit error: {}", e
),
1699 let mut dst
= self.dst
.writable();
1700 match writeln
!(dst
) {
1701 Err(e
) => panic
!("failed to emit error: {}", e
),
1702 _
=> match dst
.flush() {
1703 Err(e
) => panic
!("failed to emit error: {}", e
),
1710 impl FileWithAnnotatedLines
{
1711 /// Preprocess all the annotations so that they are grouped by file and by line number
1712 /// This helps us quickly iterate over the whole message (including secondary file spans)
1713 pub fn collect_annotations(
1715 source_map
: &Option
<Lrc
<SourceMap
>>,
1716 ) -> Vec
<FileWithAnnotatedLines
> {
1717 fn add_annotation_to_file(
1718 file_vec
: &mut Vec
<FileWithAnnotatedLines
>,
1719 file
: Lrc
<SourceFile
>,
1723 for slot
in file_vec
.iter_mut() {
1724 // Look through each of our files for the one we're adding to
1725 if slot
.file
.name
== file
.name
{
1726 // See if we already have a line for it
1727 for line_slot
in &mut slot
.lines
{
1728 if line_slot
.line_index
== line_index
{
1729 line_slot
.annotations
.push(ann
);
1733 // We don't have a line yet, create one
1734 slot
.lines
.push(Line { line_index, annotations: vec![ann] }
);
1739 // This is the first time we're seeing the file
1740 file_vec
.push(FileWithAnnotatedLines
{
1742 lines
: vec
![Line { line_index, annotations: vec![ann] }
],
1747 let mut output
= vec
![];
1748 let mut multiline_annotations
= vec
![];
1750 if let Some(ref sm
) = source_map
{
1751 for span_label
in msp
.span_labels() {
1752 if span_label
.span
.is_dummy() {
1756 let lo
= sm
.lookup_char_pos(span_label
.span
.lo());
1757 let mut hi
= sm
.lookup_char_pos(span_label
.span
.hi());
1759 // Watch out for "empty spans". If we get a span like 6..6, we
1760 // want to just display a `^` at 6, so convert that to
1761 // 6..7. This is degenerate input, but it's best to degrade
1762 // gracefully -- and the parser likes to supply a span like
1763 // that for EOF, in particular.
1765 if lo
.col_display
== hi
.col_display
&& lo
.line
== hi
.line
{
1766 hi
.col_display
+= 1;
1769 if lo
.line
!= hi
.line
{
1770 let ml
= MultilineAnnotation
{
1772 line_start
: lo
.line
,
1774 start_col
: lo
.col_display
,
1775 end_col
: hi
.col_display
,
1776 is_primary
: span_label
.is_primary
,
1777 label
: span_label
.label
,
1778 overlaps_exactly
: false,
1780 multiline_annotations
.push((lo
.file
, ml
));
1782 let ann
= Annotation
{
1783 start_col
: lo
.col_display
,
1784 end_col
: hi
.col_display
,
1785 is_primary
: span_label
.is_primary
,
1786 label
: span_label
.label
,
1787 annotation_type
: AnnotationType
::Singleline
,
1789 add_annotation_to_file(&mut output
, lo
.file
, lo
.line
, ann
);
1794 // Find overlapping multiline annotations, put them at different depths
1795 multiline_annotations
.sort_by_key(|&(_
, ref ml
)| (ml
.line_start
, ml
.line_end
));
1796 for (_
, ann
) in multiline_annotations
.clone() {
1797 for (_
, a
) in multiline_annotations
.iter_mut() {
1798 // Move all other multiline annotations overlapping with this one
1799 // one level to the right.
1800 if !(ann
.same_span(a
))
1801 && num_overlap(ann
.line_start
, ann
.line_end
, a
.line_start
, a
.line_end
, true)
1804 } else if ann
.same_span(a
) && &ann
!= a
{
1805 a
.overlaps_exactly
= true;
1812 let mut max_depth
= 0; // max overlapping multiline spans
1813 for (file
, ann
) in multiline_annotations
{
1814 max_depth
= max(max_depth
, ann
.depth
);
1815 let mut end_ann
= ann
.as_end();
1816 if !ann
.overlaps_exactly
{
1817 // avoid output like
1839 add_annotation_to_file(&mut output
, file
.clone(), ann
.line_start
, ann
.as_start());
1840 // 4 is the minimum vertical length of a multiline span when presented: two lines
1841 // of code and two lines of underline. This is not true for the special case where
1842 // the beginning doesn't have an underline, but the current logic seems to be
1843 // working correctly.
1844 let middle
= min(ann
.line_start
+ 4, ann
.line_end
);
1845 for line
in ann
.line_start
+ 1..middle
{
1846 // Every `|` that joins the beginning of the span (`___^`) to the end (`|__^`).
1847 add_annotation_to_file(&mut output
, file
.clone(), line
, ann
.as_line());
1849 let line_end
= ann
.line_end
- 1;
1850 if middle
< line_end
{
1851 add_annotation_to_file(&mut output
, file
.clone(), line_end
, ann
.as_line());
1854 end_ann
.annotation_type
= AnnotationType
::Singleline
;
1856 add_annotation_to_file(&mut output
, file
, ann
.line_end
, end_ann
);
1858 for file_vec
in output
.iter_mut() {
1859 file_vec
.multiline_depth
= max_depth
;
1865 fn draw_col_separator(buffer
: &mut StyledBuffer
, line
: usize, col
: usize) {
1866 buffer
.puts(line
, col
, "| ", Style
::LineNumber
);
1869 fn draw_col_separator_no_space(buffer
: &mut StyledBuffer
, line
: usize, col
: usize) {
1870 draw_col_separator_no_space_with_style(buffer
, line
, col
, Style
::LineNumber
);
1873 fn draw_col_separator_no_space_with_style(
1874 buffer
: &mut StyledBuffer
,
1879 buffer
.putc(line
, col
, '
|'
, style
);
1883 buffer
: &mut StyledBuffer
,
1890 for col
in col_from
..col_to
{
1891 buffer
.putc(line
, col
, symbol
, style
);
1895 fn draw_note_separator(buffer
: &mut StyledBuffer
, line
: usize, col
: usize) {
1896 buffer
.puts(line
, col
, "= ", Style
::LineNumber
);
1899 fn draw_multiline_line(
1900 buffer
: &mut StyledBuffer
,
1906 buffer
.putc(line
, offset
+ depth
- 1, '
|'
, style
);
1916 let extra
= if inclusive { 1 }
else { 0 }
;
1917 (b_start
..b_end
+ extra
).contains(&a_start
) || (a_start
..a_end
+ extra
).contains(&b_start
)
1919 fn overlaps(a1
: &Annotation
, a2
: &Annotation
, padding
: usize) -> bool
{
1920 num_overlap(a1
.start_col
, a1
.end_col
+ padding
, a2
.start_col
, a2
.end_col
, false)
1923 fn emit_to_destination(
1924 rendered_buffer
: &[Vec
<StyledString
>],
1926 dst
: &mut Destination
,
1927 short_message
: bool
,
1928 ) -> io
::Result
<()> {
1931 let mut dst
= dst
.writable();
1933 // In order to prevent error message interleaving, where multiple error lines get intermixed
1934 // when multiple compiler processes error simultaneously, we emit errors with additional
1937 // On Unix systems, we write into a buffered terminal rather than directly to a terminal. When
1938 // the .flush() is called we take the buffer created from the buffered writes and write it at
1939 // one shot. Because the Unix systems use ANSI for the colors, which is a text-based styling
1940 // scheme, this buffered approach works and maintains the styling.
1942 // On Windows, styling happens through calls to a terminal API. This prevents us from using the
1943 // same buffering approach. Instead, we use a global Windows mutex, which we acquire long
1944 // enough to output the full error message, then we release.
1945 let _buffer_lock
= lock
::acquire_global_lock("rustc_errors");
1946 for (pos
, line
) in rendered_buffer
.iter().enumerate() {
1948 dst
.apply_style(lvl
.clone(), part
.style
)?
;
1949 write
!(dst
, "{}", part
.text
)?
;
1952 if !short_message
&& (!lvl
.is_failure_note() || pos
!= rendered_buffer
.len() - 1) {
1960 pub enum Destination
{
1961 Terminal(StandardStream
),
1962 Buffered(BufferWriter
),
1963 // The bool denotes whether we should be emitting ansi color codes or not
1964 Raw(Box
<(dyn Write
+ Send
)>, bool
),
1967 pub enum WritableDst
<'a
> {
1968 Terminal(&'a
mut StandardStream
),
1969 Buffered(&'a
mut BufferWriter
, Buffer
),
1970 Raw(&'a
mut (dyn Write
+ Send
)),
1971 ColoredRaw(Ansi
<&'a
mut (dyn Write
+ Send
)>),
1975 fn from_stderr(color
: ColorConfig
) -> Destination
{
1976 let choice
= color
.to_color_choice();
1977 // On Windows we'll be performing global synchronization on the entire
1978 // system for emitting rustc errors, so there's no need to buffer
1981 // On non-Windows we rely on the atomicity of `write` to ensure errors
1982 // don't get all jumbled up.
1984 Terminal(StandardStream
::stderr(choice
))
1986 Buffered(BufferWriter
::stderr(choice
))
1990 fn writable(&mut self) -> WritableDst
<'_
> {
1992 Destination
::Terminal(ref mut t
) => WritableDst
::Terminal(t
),
1993 Destination
::Buffered(ref mut t
) => {
1994 let buf
= t
.buffer();
1995 WritableDst
::Buffered(t
, buf
)
1997 Destination
::Raw(ref mut t
, false) => WritableDst
::Raw(t
),
1998 Destination
::Raw(ref mut t
, true) => WritableDst
::ColoredRaw(Ansi
::new(t
)),
2003 impl<'a
> WritableDst
<'a
> {
2004 fn apply_style(&mut self, lvl
: Level
, style
: Style
) -> io
::Result
<()> {
2005 let mut spec
= ColorSpec
::new();
2007 Style
::LineAndColumn
=> {}
2008 Style
::LineNumber
=> {
2009 spec
.set_bold(true);
2010 spec
.set_intense(true);
2012 spec
.set_fg(Some(Color
::Cyan
));
2014 spec
.set_fg(Some(Color
::Blue
));
2017 Style
::Quotation
=> {}
2018 Style
::MainHeaderMsg
=> {
2019 spec
.set_bold(true);
2021 spec
.set_intense(true).set_fg(Some(Color
::White
));
2024 Style
::UnderlinePrimary
| Style
::LabelPrimary
=> {
2026 spec
.set_bold(true);
2028 Style
::UnderlineSecondary
| Style
::LabelSecondary
=> {
2029 spec
.set_bold(true).set_intense(true);
2031 spec
.set_fg(Some(Color
::Cyan
));
2033 spec
.set_fg(Some(Color
::Blue
));
2036 Style
::HeaderMsg
| Style
::NoStyle
=> {}
2037 Style
::Level(lvl
) => {
2039 spec
.set_bold(true);
2041 Style
::Highlight
=> {
2042 spec
.set_bold(true);
2045 self.set_color(&spec
)
2048 fn set_color(&mut self, color
: &ColorSpec
) -> io
::Result
<()> {
2050 WritableDst
::Terminal(ref mut t
) => t
.set_color(color
),
2051 WritableDst
::Buffered(_
, ref mut t
) => t
.set_color(color
),
2052 WritableDst
::ColoredRaw(ref mut t
) => t
.set_color(color
),
2053 WritableDst
::Raw(_
) => Ok(()),
2057 fn reset(&mut self) -> io
::Result
<()> {
2059 WritableDst
::Terminal(ref mut t
) => t
.reset(),
2060 WritableDst
::Buffered(_
, ref mut t
) => t
.reset(),
2061 WritableDst
::ColoredRaw(ref mut t
) => t
.reset(),
2062 WritableDst
::Raw(_
) => Ok(()),
2067 impl<'a
> Write
for WritableDst
<'a
> {
2068 fn write(&mut self, bytes
: &[u8]) -> io
::Result
<usize> {
2070 WritableDst
::Terminal(ref mut t
) => t
.write(bytes
),
2071 WritableDst
::Buffered(_
, ref mut buf
) => buf
.write(bytes
),
2072 WritableDst
::Raw(ref mut w
) => w
.write(bytes
),
2073 WritableDst
::ColoredRaw(ref mut t
) => t
.write(bytes
),
2077 fn flush(&mut self) -> io
::Result
<()> {
2079 WritableDst
::Terminal(ref mut t
) => t
.flush(),
2080 WritableDst
::Buffered(_
, ref mut buf
) => buf
.flush(),
2081 WritableDst
::Raw(ref mut w
) => w
.flush(),
2082 WritableDst
::ColoredRaw(ref mut w
) => w
.flush(),
2087 impl<'a
> Drop
for WritableDst
<'a
> {
2088 fn drop(&mut self) {
2090 WritableDst
::Buffered(ref mut dst
, ref mut buf
) => {
2091 drop(dst
.print(buf
));
2098 /// Whether the original and suggested code are visually similar enough to warrant extra wording.
2099 pub fn is_case_difference(sm
: &SourceMap
, suggested
: &str, sp
: Span
) -> bool
{
2100 // FIXME: this should probably be extended to also account for `FO0` → `FOO` and unicode.
2101 let found
= match sm
.span_to_snippet(sp
) {
2102 Ok(snippet
) => snippet
,
2104 warn
!("Invalid span {:?}. Err={:?}", sp
, e
);
2108 let ascii_confusables
= &['c'
, 'f'
, 'i'
, 'k'
, 'o'
, 's'
, 'u'
, 'v'
, 'w'
, 'x'
, 'y'
, 'z'
];
2109 // All the chars that differ in capitalization are confusable (above):
2110 let confusable
= found
2112 .zip(suggested
.chars())
2113 .filter(|(f
, s
)| f
!= s
)
2114 .all(|(f
, s
)| (ascii_confusables
.contains(&f
) || ascii_confusables
.contains(&s
)));
2115 confusable
&& found
.to_lowercase() == suggested
.to_lowercase()
2116 // FIXME: We sometimes suggest the same thing we already have, which is a
2117 // bug, but be defensive against that here.
2118 && found
!= suggested