]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_errors/src/emitter.rs
d64a589bd9b2a37b1db629e6e10d7112c206ea36
[rust.git] / compiler / rustc_errors / src / emitter.rs
1 //! The current rustc diagnostics emitter.
2 //!
3 //! An `Emitter` takes care of generating the output from a `DiagnosticBuilder` struct.
4 //!
5 //! There are various `Emitter` implementations that generate different output formats such as
6 //! JSON and human readable output.
7 //!
8 //! The output types are defined in `rustc_session::config::ErrorOutputType`.
9
10 use Destination::*;
11
12 use rustc_span::source_map::SourceMap;
13 use rustc_span::{MultiSpan, SourceFile, Span};
14
15 use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString};
16 use crate::styled_buffer::StyledBuffer;
17 use crate::{
18     CodeSuggestion, Diagnostic, DiagnosticId, Handler, Level, SubDiagnostic, SubstitutionHighlight,
19     SuggestionStyle,
20 };
21
22 use rustc_lint_defs::pluralize;
23
24 use rustc_data_structures::fx::FxHashMap;
25 use rustc_data_structures::sync::Lrc;
26 use rustc_span::hygiene::{ExpnKind, MacroKind};
27 use std::borrow::Cow;
28 use std::cmp::{max, min, Reverse};
29 use std::io;
30 use std::io::prelude::*;
31 use std::iter;
32 use std::path::Path;
33 use termcolor::{Ansi, BufferWriter, ColorChoice, ColorSpec, StandardStream};
34 use termcolor::{Buffer, Color, WriteColor};
35 use tracing::*;
36
37 /// Default column width, used in tests and when terminal dimensions cannot be determined.
38 const DEFAULT_COLUMN_WIDTH: usize = 140;
39
40 /// Describes the way the content of the `rendered` field of the json output is generated
41 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
42 pub enum HumanReadableErrorType {
43     Default(ColorConfig),
44     AnnotateSnippet(ColorConfig),
45     Short(ColorConfig),
46 }
47
48 impl HumanReadableErrorType {
49     /// Returns a (`short`, `color`) tuple
50     pub fn unzip(self) -> (bool, ColorConfig) {
51         match self {
52             HumanReadableErrorType::Default(cc) => (false, cc),
53             HumanReadableErrorType::Short(cc) => (true, cc),
54             HumanReadableErrorType::AnnotateSnippet(cc) => (false, cc),
55         }
56     }
57     pub fn new_emitter(
58         self,
59         dst: Box<dyn Write + Send>,
60         source_map: Option<Lrc<SourceMap>>,
61         teach: bool,
62         terminal_width: Option<usize>,
63         macro_backtrace: bool,
64     ) -> EmitterWriter {
65         let (short, color_config) = self.unzip();
66         let color = color_config.suggests_using_colors();
67         EmitterWriter::new(dst, source_map, short, teach, color, terminal_width, macro_backtrace)
68     }
69 }
70
71 #[derive(Clone, Copy, Debug)]
72 struct Margin {
73     /// The available whitespace in the left that can be consumed when centering.
74     pub whitespace_left: usize,
75     /// The column of the beginning of left-most span.
76     pub span_left: usize,
77     /// The column of the end of right-most span.
78     pub span_right: usize,
79     /// The beginning of the line to be displayed.
80     pub computed_left: usize,
81     /// The end of the line to be displayed.
82     pub computed_right: usize,
83     /// The current width of the terminal. Uses value of `DEFAULT_COLUMN_WIDTH` constant by default
84     /// 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,
89 }
90
91 impl Margin {
92     fn new(
93         whitespace_left: usize,
94         span_left: usize,
95         span_right: usize,
96         label_right: usize,
97         column_width: usize,
98         max_line_len: usize,
99     ) -> Self {
100         // The 6 is padding to give a bit of room for `...` when displaying:
101         // ```
102         // error: message
103         //   --> file.rs:16:58
104         //    |
105         // 16 | ... fn foo(self) -> Self::Bar {
106         //    |                     ^^^^^^^^^
107         // ```
108
109         let mut m = Margin {
110             whitespace_left: whitespace_left.saturating_sub(6),
111             span_left: span_left.saturating_sub(6),
112             span_right: span_right + 6,
113             computed_left: 0,
114             computed_right: 0,
115             column_width,
116             label_right: label_right + 6,
117         };
118         m.compute(max_line_len);
119         m
120     }
121
122     fn was_cut_left(&self) -> bool {
123         self.computed_left > 0
124     }
125
126     fn was_cut_right(&self, line_len: usize) -> bool {
127         let right =
128             if self.computed_right == self.span_right || self.computed_right == self.label_right {
129                 // Account for the "..." padding given above. Otherwise we end up with code lines that
130                 // do fit but end in "..." as if they were trimmed.
131                 self.computed_right - 6
132             } else {
133                 self.computed_right
134             };
135         right < line_len && self.computed_left + self.column_width < line_len
136     }
137
138     fn compute(&mut self, max_line_len: usize) {
139         // When there's a lot of whitespace (>20), we want to trim it as it is useless.
140         self.computed_left = if self.whitespace_left > 20 {
141             self.whitespace_left - 16 // We want some padding.
142         } else {
143             0
144         };
145         // We want to show as much as possible, max_line_len is the right-most boundary for the
146         // relevant code.
147         self.computed_right = max(max_line_len, self.computed_left);
148
149         if self.computed_right - self.computed_left > self.column_width {
150             // Trimming only whitespace isn't enough, let's get craftier.
151             if self.label_right - self.whitespace_left <= self.column_width {
152                 // Attempt to fit the code window only trimming whitespace.
153                 self.computed_left = self.whitespace_left;
154                 self.computed_right = self.computed_left + self.column_width;
155             } else if self.label_right - self.span_left <= self.column_width {
156                 // Attempt to fit the code window considering only the spans and labels.
157                 let padding_left = (self.column_width - (self.label_right - self.span_left)) / 2;
158                 self.computed_left = self.span_left.saturating_sub(padding_left);
159                 self.computed_right = self.computed_left + self.column_width;
160             } else if self.span_right - self.span_left <= self.column_width {
161                 // Attempt to fit the code window considering the spans and labels plus padding.
162                 let padding_left = (self.column_width - (self.span_right - self.span_left)) / 5 * 2;
163                 self.computed_left = self.span_left.saturating_sub(padding_left);
164                 self.computed_right = self.computed_left + self.column_width;
165             } else {
166                 // Mostly give up but still don't show the full line.
167                 self.computed_left = self.span_left;
168                 self.computed_right = self.span_right;
169             }
170         }
171     }
172
173     fn left(&self, line_len: usize) -> usize {
174         min(self.computed_left, line_len)
175     }
176
177     fn right(&self, line_len: usize) -> usize {
178         if line_len.saturating_sub(self.computed_left) <= self.column_width {
179             line_len
180         } else {
181             min(line_len, self.computed_right)
182         }
183     }
184 }
185
186 const ANONYMIZED_LINE_NUM: &str = "LL";
187
188 /// Emitter trait for emitting errors.
189 pub trait Emitter {
190     /// Emit a structured diagnostic.
191     fn emit_diagnostic(&mut self, diag: &Diagnostic);
192
193     /// Emit a notification that an artifact has been output.
194     /// This is currently only supported for the JSON format,
195     /// other formats can, and will, simply ignore it.
196     fn emit_artifact_notification(&mut self, _path: &Path, _artifact_type: &str) {}
197
198     fn emit_future_breakage_report(&mut self, _diags: Vec<Diagnostic>) {}
199
200     /// Emit list of unused externs
201     fn emit_unused_externs(&mut self, _lint_level: &str, _unused_externs: &[&str]) {}
202
203     /// Checks if should show explanations about "rustc --explain"
204     fn should_show_explain(&self) -> bool {
205         true
206     }
207
208     /// Checks if we can use colors in the current output stream.
209     fn supports_color(&self) -> bool {
210         false
211     }
212
213     fn source_map(&self) -> Option<&Lrc<SourceMap>>;
214
215     /// Formats the substitutions of the primary_span
216     ///
217     /// The are a lot of conditions to this method, but in short:
218     ///
219     /// * If the current `Diagnostic` has only one visible `CodeSuggestion`,
220     ///   we format the `help` suggestion depending on the content of the
221     ///   substitutions. In that case, we return the modified span only.
222     ///
223     /// * If the current `Diagnostic` has multiple suggestions,
224     ///   we return the original `primary_span` and the original suggestions.
225     fn primary_span_formatted<'a>(
226         &mut self,
227         diag: &'a Diagnostic,
228     ) -> (MultiSpan, &'a [CodeSuggestion]) {
229         let mut primary_span = diag.span.clone();
230         if let Some((sugg, rest)) = diag.suggestions.split_first() {
231             if rest.is_empty() &&
232                // ^ if there is only one suggestion
233                // don't display multi-suggestions as labels
234                sugg.substitutions.len() == 1 &&
235                // don't display multipart suggestions as labels
236                sugg.substitutions[0].parts.len() == 1 &&
237                // don't display long messages as labels
238                sugg.msg.split_whitespace().count() < 10 &&
239                // don't display multiline suggestions as labels
240                !sugg.substitutions[0].parts[0].snippet.contains('\n') &&
241                ![
242                     // when this style is set we want the suggestion to be a message, not inline
243                     SuggestionStyle::HideCodeAlways,
244                     // trivial suggestion for tooling's sake, never shown
245                     SuggestionStyle::CompletelyHidden,
246                     // subtle suggestion, never shown inline
247                     SuggestionStyle::ShowAlways,
248                ].contains(&sugg.style)
249             {
250                 let substitution = &sugg.substitutions[0].parts[0].snippet.trim();
251                 let msg = if substitution.is_empty() || sugg.style.hide_inline() {
252                     // This substitution is only removal OR we explicitly don't want to show the
253                     // code inline (`hide_inline`). Therefore, we don't show the substitution.
254                     format!("help: {}", sugg.msg)
255                 } else {
256                     // Show the default suggestion text with the substitution
257                     format!(
258                         "help: {}{}: `{}`",
259                         sugg.msg,
260                         if self
261                             .source_map()
262                             .map(|sm| is_case_difference(
263                                 &**sm,
264                                 substitution,
265                                 sugg.substitutions[0].parts[0].span,
266                             ))
267                             .unwrap_or(false)
268                         {
269                             " (notice the capitalization)"
270                         } else {
271                             ""
272                         },
273                         substitution,
274                     )
275                 };
276                 primary_span.push_span_label(sugg.substitutions[0].parts[0].span, msg);
277
278                 // We return only the modified primary_span
279                 (primary_span, &[])
280             } else {
281                 // if there are multiple suggestions, print them all in full
282                 // to be consistent. We could try to figure out if we can
283                 // make one (or the first one) inline, but that would give
284                 // undue importance to a semi-random suggestion
285                 (primary_span, &diag.suggestions)
286             }
287         } else {
288             (primary_span, &diag.suggestions)
289         }
290     }
291
292     fn fix_multispans_in_extern_macros_and_render_macro_backtrace(
293         &self,
294         source_map: &Option<Lrc<SourceMap>>,
295         span: &mut MultiSpan,
296         children: &mut Vec<SubDiagnostic>,
297         level: &Level,
298         backtrace: bool,
299     ) {
300         // Check for spans in macros, before `fix_multispans_in_extern_macros`
301         // has a chance to replace them.
302         let has_macro_spans = iter::once(&*span)
303             .chain(children.iter().map(|child| &child.span))
304             .flat_map(|span| span.primary_spans())
305             .flat_map(|sp| sp.macro_backtrace())
306             .find_map(|expn_data| {
307                 match expn_data.kind {
308                     ExpnKind::Root => None,
309
310                     // Skip past non-macro entries, just in case there
311                     // are some which do actually involve macros.
312                     ExpnKind::Inlined | ExpnKind::Desugaring(..) | ExpnKind::AstPass(..) => None,
313
314                     ExpnKind::Macro(macro_kind, name) => Some((macro_kind, name)),
315                 }
316             });
317
318         if !backtrace {
319             self.fix_multispans_in_extern_macros(source_map, span, children);
320         }
321
322         self.render_multispans_macro_backtrace(span, children, backtrace);
323
324         if !backtrace {
325             if let Some((macro_kind, name)) = has_macro_spans {
326                 let descr = macro_kind.descr();
327
328                 let msg = format!(
329                     "this {level} originates in the {descr} `{name}` \
330                     (in Nightly builds, run with -Z macro-backtrace for more info)",
331                 );
332
333                 children.push(SubDiagnostic {
334                     level: Level::Note,
335                     message: vec![(msg, Style::NoStyle)],
336                     span: MultiSpan::new(),
337                     render_span: None,
338                 });
339             }
340         }
341     }
342
343     fn render_multispans_macro_backtrace(
344         &self,
345         span: &mut MultiSpan,
346         children: &mut Vec<SubDiagnostic>,
347         backtrace: bool,
348     ) {
349         for span in iter::once(span).chain(children.iter_mut().map(|child| &mut child.span)) {
350             self.render_multispan_macro_backtrace(span, backtrace);
351         }
352     }
353
354     fn render_multispan_macro_backtrace(&self, span: &mut MultiSpan, always_backtrace: bool) {
355         let mut new_labels: Vec<(Span, String)> = vec![];
356
357         for &sp in span.primary_spans() {
358             if sp.is_dummy() {
359                 continue;
360             }
361
362             // FIXME(eddyb) use `retain` on `macro_backtrace` to remove all the
363             // entries we don't want to print, to make sure the indices being
364             // printed are contiguous (or omitted if there's only one entry).
365             let macro_backtrace: Vec<_> = sp.macro_backtrace().collect();
366             for (i, trace) in macro_backtrace.iter().rev().enumerate() {
367                 if trace.def_site.is_dummy() {
368                     continue;
369                 }
370
371                 if always_backtrace && !matches!(trace.kind, ExpnKind::Inlined) {
372                     new_labels.push((
373                         trace.def_site,
374                         format!(
375                             "in this expansion of `{}`{}",
376                             trace.kind.descr(),
377                             if macro_backtrace.len() > 1 {
378                                 // if macro_backtrace.len() == 1 it'll be
379                                 // pointed at by "in this macro invocation"
380                                 format!(" (#{})", i + 1)
381                             } else {
382                                 String::new()
383                             },
384                         ),
385                     ));
386                 }
387
388                 // Don't add a label on the call site if the diagnostic itself
389                 // already points to (a part of) that call site, as the label
390                 // is meant for showing the relevant invocation when the actual
391                 // diagnostic is pointing to some part of macro definition.
392                 //
393                 // This also handles the case where an external span got replaced
394                 // with the call site span by `fix_multispans_in_extern_macros`.
395                 //
396                 // NB: `-Zmacro-backtrace` overrides this, for uniformity, as the
397                 // "in this expansion of" label above is always added in that mode,
398                 // and it needs an "in this macro invocation" label to match that.
399                 let redundant_span = trace.call_site.contains(sp);
400
401                 if !redundant_span || always_backtrace {
402                     let msg: Cow<'static, _> = match trace.kind {
403                         ExpnKind::Macro(MacroKind::Attr, _) => {
404                             "this procedural macro expansion".into()
405                         }
406                         ExpnKind::Macro(MacroKind::Derive, _) => {
407                             "this derive macro expansion".into()
408                         }
409                         ExpnKind::Macro(MacroKind::Bang, _) => "this macro invocation".into(),
410                         ExpnKind::Inlined => "the inlined copy of this code".into(),
411                         ExpnKind::Root => "in the crate root".into(),
412                         ExpnKind::AstPass(kind) => kind.descr().into(),
413                         ExpnKind::Desugaring(kind) => {
414                             format!("this {} desugaring", kind.descr()).into()
415                         }
416                     };
417                     new_labels.push((
418                         trace.call_site,
419                         format!(
420                             "in {}{}",
421                             msg,
422                             if macro_backtrace.len() > 1 && always_backtrace {
423                                 // only specify order when the macro
424                                 // backtrace is multiple levels deep
425                                 format!(" (#{})", i + 1)
426                             } else {
427                                 String::new()
428                             },
429                         ),
430                     ));
431                 }
432                 if !always_backtrace {
433                     break;
434                 }
435             }
436         }
437
438         for (label_span, label_text) in new_labels {
439             span.push_span_label(label_span, label_text);
440         }
441     }
442
443     // This does a small "fix" for multispans by looking to see if it can find any that
444     // point directly at external macros. Since these are often difficult to read,
445     // this will change the span to point at the use site.
446     fn fix_multispans_in_extern_macros(
447         &self,
448         source_map: &Option<Lrc<SourceMap>>,
449         span: &mut MultiSpan,
450         children: &mut Vec<SubDiagnostic>,
451     ) {
452         let Some(source_map) = source_map else { return };
453         debug!("fix_multispans_in_extern_macros: before: span={:?} children={:?}", span, children);
454         self.fix_multispan_in_extern_macros(source_map, span);
455         for child in children.iter_mut() {
456             self.fix_multispan_in_extern_macros(source_map, &mut child.span);
457         }
458         debug!("fix_multispans_in_extern_macros: after: span={:?} children={:?}", span, children);
459     }
460
461     // This "fixes" MultiSpans that contain `Span`s pointing to locations inside of external macros.
462     // Since these locations are often difficult to read,
463     // we move these spans from the external macros to their corresponding use site.
464     fn fix_multispan_in_extern_macros(&self, source_map: &Lrc<SourceMap>, span: &mut MultiSpan) {
465         // First, find all the spans in external macros and point instead at their use site.
466         let replacements: Vec<(Span, Span)> = span
467             .primary_spans()
468             .iter()
469             .copied()
470             .chain(span.span_labels().iter().map(|sp_label| sp_label.span))
471             .filter_map(|sp| {
472                 if !sp.is_dummy() && source_map.is_imported(sp) {
473                     let maybe_callsite = sp.source_callsite();
474                     if sp != maybe_callsite {
475                         return Some((sp, maybe_callsite));
476                     }
477                 }
478                 None
479             })
480             .collect();
481
482         // After we have them, make sure we replace these 'bad' def sites with their use sites.
483         for (from, to) in replacements {
484             span.replace(from, to);
485         }
486     }
487 }
488
489 impl Emitter for EmitterWriter {
490     fn source_map(&self) -> Option<&Lrc<SourceMap>> {
491         self.sm.as_ref()
492     }
493
494     fn emit_diagnostic(&mut self, diag: &Diagnostic) {
495         let mut children = diag.children.clone();
496         let (mut primary_span, suggestions) = self.primary_span_formatted(&diag);
497         debug!("emit_diagnostic: suggestions={:?}", suggestions);
498
499         self.fix_multispans_in_extern_macros_and_render_macro_backtrace(
500             &self.sm,
501             &mut primary_span,
502             &mut children,
503             &diag.level,
504             self.macro_backtrace,
505         );
506
507         self.emit_messages_default(
508             &diag.level,
509             &diag.styled_message(),
510             &diag.code,
511             &primary_span,
512             &children,
513             &suggestions,
514         );
515     }
516
517     fn should_show_explain(&self) -> bool {
518         !self.short_message
519     }
520
521     fn supports_color(&self) -> bool {
522         self.dst.supports_color()
523     }
524 }
525
526 /// An emitter that does nothing when emitting a non-fatal diagnostic.
527 /// Fatal diagnostics are forwarded to `fatal_handler` to avoid silent
528 /// failures of rustc, as witnessed e.g. in issue #89358.
529 pub struct SilentEmitter {
530     pub fatal_handler: Handler,
531     pub fatal_note: Option<String>,
532 }
533
534 impl Emitter for SilentEmitter {
535     fn source_map(&self) -> Option<&Lrc<SourceMap>> {
536         None
537     }
538     fn emit_diagnostic(&mut self, d: &Diagnostic) {
539         if d.level == Level::Fatal {
540             let mut d = d.clone();
541             if let Some(ref note) = self.fatal_note {
542                 d.note(note);
543             }
544             self.fatal_handler.emit_diagnostic(&d);
545         }
546     }
547 }
548
549 /// Maximum number of lines we will print for a multiline suggestion; arbitrary.
550 ///
551 /// This should be replaced with a more involved mechanism to output multiline suggestions that
552 /// more closely mimics the regular diagnostic output, where irrelevant code lines are elided.
553 pub const MAX_SUGGESTION_HIGHLIGHT_LINES: usize = 6;
554 /// Maximum number of suggestions to be shown
555 ///
556 /// Arbitrary, but taken from trait import suggestion limit
557 pub const MAX_SUGGESTIONS: usize = 4;
558
559 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
560 pub enum ColorConfig {
561     Auto,
562     Always,
563     Never,
564 }
565
566 impl ColorConfig {
567     fn to_color_choice(self) -> ColorChoice {
568         match self {
569             ColorConfig::Always => {
570                 if atty::is(atty::Stream::Stderr) {
571                     ColorChoice::Always
572                 } else {
573                     ColorChoice::AlwaysAnsi
574                 }
575             }
576             ColorConfig::Never => ColorChoice::Never,
577             ColorConfig::Auto if atty::is(atty::Stream::Stderr) => ColorChoice::Auto,
578             ColorConfig::Auto => ColorChoice::Never,
579         }
580     }
581     fn suggests_using_colors(self) -> bool {
582         match self {
583             ColorConfig::Always | ColorConfig::Auto => true,
584             ColorConfig::Never => false,
585         }
586     }
587 }
588
589 /// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short`
590 pub struct EmitterWriter {
591     dst: Destination,
592     sm: Option<Lrc<SourceMap>>,
593     short_message: bool,
594     teach: bool,
595     ui_testing: bool,
596     terminal_width: Option<usize>,
597
598     macro_backtrace: bool,
599 }
600
601 #[derive(Debug)]
602 pub struct FileWithAnnotatedLines {
603     pub file: Lrc<SourceFile>,
604     pub lines: Vec<Line>,
605     multiline_depth: usize,
606 }
607
608 impl EmitterWriter {
609     pub fn stderr(
610         color_config: ColorConfig,
611         source_map: Option<Lrc<SourceMap>>,
612         short_message: bool,
613         teach: bool,
614         terminal_width: Option<usize>,
615         macro_backtrace: bool,
616     ) -> EmitterWriter {
617         let dst = Destination::from_stderr(color_config);
618         EmitterWriter {
619             dst,
620             sm: source_map,
621             short_message,
622             teach,
623             ui_testing: false,
624             terminal_width,
625             macro_backtrace,
626         }
627     }
628
629     pub fn new(
630         dst: Box<dyn Write + Send>,
631         source_map: Option<Lrc<SourceMap>>,
632         short_message: bool,
633         teach: bool,
634         colored: bool,
635         terminal_width: Option<usize>,
636         macro_backtrace: bool,
637     ) -> EmitterWriter {
638         EmitterWriter {
639             dst: Raw(dst, colored),
640             sm: source_map,
641             short_message,
642             teach,
643             ui_testing: false,
644             terminal_width,
645             macro_backtrace,
646         }
647     }
648
649     pub fn ui_testing(mut self, ui_testing: bool) -> Self {
650         self.ui_testing = ui_testing;
651         self
652     }
653
654     fn maybe_anonymized(&self, line_num: usize) -> String {
655         if self.ui_testing { ANONYMIZED_LINE_NUM.to_string() } else { line_num.to_string() }
656     }
657
658     fn draw_line(
659         &self,
660         buffer: &mut StyledBuffer,
661         source_string: &str,
662         line_index: usize,
663         line_offset: usize,
664         width_offset: usize,
665         code_offset: usize,
666         margin: Margin,
667     ) {
668         // Tabs are assumed to have been replaced by spaces in calling code.
669         debug_assert!(!source_string.contains('\t'));
670         let line_len = source_string.len();
671         // Create the source line we will highlight.
672         let left = margin.left(line_len);
673         let right = margin.right(line_len);
674         // On long lines, we strip the source line, accounting for unicode.
675         let mut taken = 0;
676         let code: String = source_string
677             .chars()
678             .skip(left)
679             .take_while(|ch| {
680                 // Make sure that the trimming on the right will fall within the terminal width.
681                 // FIXME: `unicode_width` sometimes disagrees with terminals on how wide a `char` is.
682                 // For now, just accept that sometimes the code line will be longer than desired.
683                 let next = unicode_width::UnicodeWidthChar::width(*ch).unwrap_or(1);
684                 if taken + next > right - left {
685                     return false;
686                 }
687                 taken += next;
688                 true
689             })
690             .collect();
691         buffer.puts(line_offset, code_offset, &code, Style::Quotation);
692         if margin.was_cut_left() {
693             // We have stripped some code/whitespace from the beginning, make it clear.
694             buffer.puts(line_offset, code_offset, "...", Style::LineNumber);
695         }
696         if margin.was_cut_right(line_len) {
697             // We have stripped some code after the right-most span end, make it clear we did so.
698             buffer.puts(line_offset, code_offset + taken - 3, "...", Style::LineNumber);
699         }
700         buffer.puts(line_offset, 0, &self.maybe_anonymized(line_index), Style::LineNumber);
701
702         draw_col_separator(buffer, line_offset, width_offset - 2);
703     }
704
705     fn render_source_line(
706         &self,
707         buffer: &mut StyledBuffer,
708         file: Lrc<SourceFile>,
709         line: &Line,
710         width_offset: usize,
711         code_offset: usize,
712         margin: Margin,
713     ) -> Vec<(usize, Style)> {
714         // Draw:
715         //
716         //   LL | ... code ...
717         //      |     ^^-^ span label
718         //      |       |
719         //      |       secondary span label
720         //
721         //   ^^ ^ ^^^ ^^^^ ^^^ we don't care about code too far to the right of a span, we trim it
722         //   |  | |   |
723         //   |  | |   actual code found in your source code and the spans we use to mark it
724         //   |  | when there's too much wasted space to the left, trim it
725         //   |  vertical divider between the column number and the code
726         //   column number
727
728         if line.line_index == 0 {
729             return Vec::new();
730         }
731
732         let source_string = match file.get_line(line.line_index - 1) {
733             Some(s) => normalize_whitespace(&*s),
734             None => return Vec::new(),
735         };
736
737         let line_offset = buffer.num_lines();
738
739         let left = margin.left(source_string.len()); // Left trim
740         // Account for unicode characters of width !=0 that were removed.
741         let left = source_string
742             .chars()
743             .take(left)
744             .map(|ch| unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1))
745             .sum();
746
747         self.draw_line(
748             buffer,
749             &source_string,
750             line.line_index,
751             line_offset,
752             width_offset,
753             code_offset,
754             margin,
755         );
756
757         // Special case when there's only one annotation involved, it is the start of a multiline
758         // span and there's no text at the beginning of the code line. Instead of doing the whole
759         // graph:
760         //
761         // 2 |   fn foo() {
762         //   |  _^
763         // 3 | |
764         // 4 | | }
765         //   | |_^ test
766         //
767         // we simplify the output to:
768         //
769         // 2 | / fn foo() {
770         // 3 | |
771         // 4 | | }
772         //   | |_^ test
773         if let [ann] = &line.annotations[..] {
774             if let AnnotationType::MultilineStart(depth) = ann.annotation_type {
775                 if source_string.chars().take(ann.start_col).all(|c| c.is_whitespace()) {
776                     let style = if ann.is_primary {
777                         Style::UnderlinePrimary
778                     } else {
779                         Style::UnderlineSecondary
780                     };
781                     buffer.putc(line_offset, width_offset + depth - 1, '/', style);
782                     return vec![(depth, style)];
783                 }
784             }
785         }
786
787         // We want to display like this:
788         //
789         //      vec.push(vec.pop().unwrap());
790         //      ---      ^^^               - previous borrow ends here
791         //      |        |
792         //      |        error occurs here
793         //      previous borrow of `vec` occurs here
794         //
795         // But there are some weird edge cases to be aware of:
796         //
797         //      vec.push(vec.pop().unwrap());
798         //      --------                    - previous borrow ends here
799         //      ||
800         //      |this makes no sense
801         //      previous borrow of `vec` occurs here
802         //
803         // For this reason, we group the lines into "highlight lines"
804         // and "annotations lines", where the highlight lines have the `^`.
805
806         // Sort the annotations by (start, end col)
807         // The labels are reversed, sort and then reversed again.
808         // Consider a list of annotations (A1, A2, C1, C2, B1, B2) where
809         // the letter signifies the span. Here we are only sorting by the
810         // span and hence, the order of the elements with the same span will
811         // not change. On reversing the ordering (|a, b| but b.cmp(a)), you get
812         // (C1, C2, B1, B2, A1, A2). All the elements with the same span are
813         // still ordered first to last, but all the elements with different
814         // spans are ordered by their spans in last to first order. Last to
815         // first order is important, because the jiggly lines and | are on
816         // the left, so the rightmost span needs to be rendered first,
817         // otherwise the lines would end up needing to go over a message.
818
819         let mut annotations = line.annotations.clone();
820         annotations.sort_by_key(|a| Reverse(a.start_col));
821
822         // First, figure out where each label will be positioned.
823         //
824         // In the case where you have the following annotations:
825         //
826         //      vec.push(vec.pop().unwrap());
827         //      --------                    - previous borrow ends here [C]
828         //      ||
829         //      |this makes no sense [B]
830         //      previous borrow of `vec` occurs here [A]
831         //
832         // `annotations_position` will hold [(2, A), (1, B), (0, C)].
833         //
834         // We try, when possible, to stick the rightmost annotation at the end
835         // of the highlight line:
836         //
837         //      vec.push(vec.pop().unwrap());
838         //      ---      ---               - previous borrow ends here
839         //
840         // But sometimes that's not possible because one of the other
841         // annotations overlaps it. For example, from the test
842         // `span_overlap_label`, we have the following annotations
843         // (written on distinct lines for clarity):
844         //
845         //      fn foo(x: u32) {
846         //      --------------
847         //             -
848         //
849         // In this case, we can't stick the rightmost-most label on
850         // the highlight line, or we would get:
851         //
852         //      fn foo(x: u32) {
853         //      -------- x_span
854         //      |
855         //      fn_span
856         //
857         // which is totally weird. Instead we want:
858         //
859         //      fn foo(x: u32) {
860         //      --------------
861         //      |      |
862         //      |      x_span
863         //      fn_span
864         //
865         // which is...less weird, at least. In fact, in general, if
866         // the rightmost span overlaps with any other span, we should
867         // use the "hang below" version, so we can at least make it
868         // clear where the span *starts*. There's an exception for this
869         // logic, when the labels do not have a message:
870         //
871         //      fn foo(x: u32) {
872         //      --------------
873         //             |
874         //             x_span
875         //
876         // instead of:
877         //
878         //      fn foo(x: u32) {
879         //      --------------
880         //      |      |
881         //      |      x_span
882         //      <EMPTY LINE>
883         //
884         let mut annotations_position = vec![];
885         let mut line_len = 0;
886         let mut p = 0;
887         for (i, annotation) in annotations.iter().enumerate() {
888             for (j, next) in annotations.iter().enumerate() {
889                 if overlaps(next, annotation, 0)  // This label overlaps with another one and both
890                     && annotation.has_label()     // take space (they have text and are not
891                     && j > i                      // multiline lines).
892                     && p == 0
893                 // We're currently on the first line, move the label one line down
894                 {
895                     // If we're overlapping with an un-labelled annotation with the same span
896                     // we can just merge them in the output
897                     if next.start_col == annotation.start_col
898                         && next.end_col == annotation.end_col
899                         && !next.has_label()
900                     {
901                         continue;
902                     }
903
904                     // This annotation needs a new line in the output.
905                     p += 1;
906                     break;
907                 }
908             }
909             annotations_position.push((p, annotation));
910             for (j, next) in annotations.iter().enumerate() {
911                 if j > i {
912                     let l = next.label.as_ref().map_or(0, |label| label.len() + 2);
913                     if (overlaps(next, annotation, l) // Do not allow two labels to be in the same
914                                                      // line if they overlap including padding, to
915                                                      // avoid situations like:
916                                                      //
917                                                      //      fn foo(x: u32) {
918                                                      //      -------^------
919                                                      //      |      |
920                                                      //      fn_spanx_span
921                                                      //
922                         && annotation.has_label()    // Both labels must have some text, otherwise
923                         && next.has_label())         // they are not overlapping.
924                                                      // Do not add a new line if this annotation
925                                                      // or the next are vertical line placeholders.
926                         || (annotation.takes_space() // If either this or the next annotation is
927                             && next.has_label())     // multiline start/end, move it to a new line
928                         || (annotation.has_label()   // so as not to overlap the horizontal lines.
929                             && next.takes_space())
930                         || (annotation.takes_space() && next.takes_space())
931                         || (overlaps(next, annotation, l)
932                             && next.end_col <= annotation.end_col
933                             && next.has_label()
934                             && p == 0)
935                     // Avoid #42595.
936                     {
937                         // This annotation needs a new line in the output.
938                         p += 1;
939                         break;
940                     }
941                 }
942             }
943             line_len = max(line_len, p);
944         }
945
946         if line_len != 0 {
947             line_len += 1;
948         }
949
950         // If there are no annotations or the only annotations on this line are
951         // MultilineLine, then there's only code being shown, stop processing.
952         if line.annotations.iter().all(|a| a.is_line()) {
953             return vec![];
954         }
955
956         // Write the column separator.
957         //
958         // After this we will have:
959         //
960         // 2 |   fn foo() {
961         //   |
962         //   |
963         //   |
964         // 3 |
965         // 4 |   }
966         //   |
967         for pos in 0..=line_len {
968             draw_col_separator(buffer, line_offset + pos + 1, width_offset - 2);
969         }
970
971         // Write the horizontal lines for multiline annotations
972         // (only the first and last lines need this).
973         //
974         // After this we will have:
975         //
976         // 2 |   fn foo() {
977         //   |  __________
978         //   |
979         //   |
980         // 3 |
981         // 4 |   }
982         //   |  _
983         for &(pos, annotation) in &annotations_position {
984             let style = if annotation.is_primary {
985                 Style::UnderlinePrimary
986             } else {
987                 Style::UnderlineSecondary
988             };
989             let pos = pos + 1;
990             match annotation.annotation_type {
991                 AnnotationType::MultilineStart(depth) | AnnotationType::MultilineEnd(depth) => {
992                     draw_range(
993                         buffer,
994                         '_',
995                         line_offset + pos,
996                         width_offset + depth,
997                         (code_offset + annotation.start_col).saturating_sub(left),
998                         style,
999                     );
1000                 }
1001                 _ if self.teach => {
1002                     buffer.set_style_range(
1003                         line_offset,
1004                         (code_offset + annotation.start_col).saturating_sub(left),
1005                         (code_offset + annotation.end_col).saturating_sub(left),
1006                         style,
1007                         annotation.is_primary,
1008                     );
1009                 }
1010                 _ => {}
1011             }
1012         }
1013
1014         // Write the vertical lines for labels that are on a different line as the underline.
1015         //
1016         // After this we will have:
1017         //
1018         // 2 |   fn foo() {
1019         //   |  __________
1020         //   | |    |
1021         //   | |
1022         // 3 | |
1023         // 4 | | }
1024         //   | |_
1025         for &(pos, annotation) in &annotations_position {
1026             let style = if annotation.is_primary {
1027                 Style::UnderlinePrimary
1028             } else {
1029                 Style::UnderlineSecondary
1030             };
1031             let pos = pos + 1;
1032
1033             if pos > 1 && (annotation.has_label() || annotation.takes_space()) {
1034                 for p in line_offset + 1..=line_offset + pos {
1035                     buffer.putc(
1036                         p,
1037                         (code_offset + annotation.start_col).saturating_sub(left),
1038                         '|',
1039                         style,
1040                     );
1041                 }
1042             }
1043             match annotation.annotation_type {
1044                 AnnotationType::MultilineStart(depth) => {
1045                     for p in line_offset + pos + 1..line_offset + line_len + 2 {
1046                         buffer.putc(p, width_offset + depth - 1, '|', style);
1047                     }
1048                 }
1049                 AnnotationType::MultilineEnd(depth) => {
1050                     for p in line_offset..=line_offset + pos {
1051                         buffer.putc(p, width_offset + depth - 1, '|', style);
1052                     }
1053                 }
1054                 _ => (),
1055             }
1056         }
1057
1058         // Write the labels on the annotations that actually have a label.
1059         //
1060         // After this we will have:
1061         //
1062         // 2 |   fn foo() {
1063         //   |  __________
1064         //   |      |
1065         //   |      something about `foo`
1066         // 3 |
1067         // 4 |   }
1068         //   |  _  test
1069         for &(pos, annotation) in &annotations_position {
1070             let style =
1071                 if annotation.is_primary { Style::LabelPrimary } else { Style::LabelSecondary };
1072             let (pos, col) = if pos == 0 {
1073                 (pos + 1, (annotation.end_col + 1).saturating_sub(left))
1074             } else {
1075                 (pos + 2, annotation.start_col.saturating_sub(left))
1076             };
1077             if let Some(ref label) = annotation.label {
1078                 buffer.puts(line_offset + pos, code_offset + col, &label, style);
1079             }
1080         }
1081
1082         // Sort from biggest span to smallest span so that smaller spans are
1083         // represented in the output:
1084         //
1085         // x | fn foo()
1086         //   | ^^^---^^
1087         //   | |  |
1088         //   | |  something about `foo`
1089         //   | something about `fn foo()`
1090         annotations_position.sort_by_key(|(_, ann)| {
1091             // Decreasing order. When annotations share the same length, prefer `Primary`.
1092             (Reverse(ann.len()), ann.is_primary)
1093         });
1094
1095         // Write the underlines.
1096         //
1097         // After this we will have:
1098         //
1099         // 2 |   fn foo() {
1100         //   |  ____-_____^
1101         //   |      |
1102         //   |      something about `foo`
1103         // 3 |
1104         // 4 |   }
1105         //   |  _^  test
1106         for &(_, annotation) in &annotations_position {
1107             let (underline, style) = if annotation.is_primary {
1108                 ('^', Style::UnderlinePrimary)
1109             } else {
1110                 ('-', Style::UnderlineSecondary)
1111             };
1112             for p in annotation.start_col..annotation.end_col {
1113                 buffer.putc(
1114                     line_offset + 1,
1115                     (code_offset + p).saturating_sub(left),
1116                     underline,
1117                     style,
1118                 );
1119             }
1120         }
1121         annotations_position
1122             .iter()
1123             .filter_map(|&(_, annotation)| match annotation.annotation_type {
1124                 AnnotationType::MultilineStart(p) | AnnotationType::MultilineEnd(p) => {
1125                     let style = if annotation.is_primary {
1126                         Style::LabelPrimary
1127                     } else {
1128                         Style::LabelSecondary
1129                     };
1130                     Some((p, style))
1131                 }
1132                 _ => None,
1133             })
1134             .collect::<Vec<_>>()
1135     }
1136
1137     fn get_multispan_max_line_num(&mut self, msp: &MultiSpan) -> usize {
1138         let sm = match self.sm {
1139             Some(ref sm) => sm,
1140             None => return 0,
1141         };
1142
1143         let mut max = 0;
1144         for primary_span in msp.primary_spans() {
1145             if !primary_span.is_dummy() {
1146                 let hi = sm.lookup_char_pos(primary_span.hi());
1147                 max = (hi.line).max(max);
1148             }
1149         }
1150         if !self.short_message {
1151             for span_label in msp.span_labels() {
1152                 if !span_label.span.is_dummy() {
1153                     let hi = sm.lookup_char_pos(span_label.span.hi());
1154                     max = (hi.line).max(max);
1155                 }
1156             }
1157         }
1158
1159         max
1160     }
1161
1162     fn get_max_line_num(&mut self, span: &MultiSpan, children: &[SubDiagnostic]) -> usize {
1163         let primary = self.get_multispan_max_line_num(span);
1164         children
1165             .iter()
1166             .map(|sub| self.get_multispan_max_line_num(&sub.span))
1167             .max()
1168             .unwrap_or(0)
1169             .max(primary)
1170     }
1171
1172     /// Adds a left margin to every line but the first, given a padding length and the label being
1173     /// displayed, keeping the provided highlighting.
1174     fn msg_to_buffer(
1175         &self,
1176         buffer: &mut StyledBuffer,
1177         msg: &[(String, Style)],
1178         padding: usize,
1179         label: &str,
1180         override_style: Option<Style>,
1181     ) {
1182         // The extra 5 ` ` is padding that's always needed to align to the `note: `:
1183         //
1184         //   error: message
1185         //     --> file.rs:13:20
1186         //      |
1187         //   13 |     <CODE>
1188         //      |      ^^^^
1189         //      |
1190         //      = note: multiline
1191         //              message
1192         //   ++^^^----xx
1193         //    |  |   | |
1194         //    |  |   | magic `2`
1195         //    |  |   length of label
1196         //    |  magic `3`
1197         //    `max_line_num_len`
1198         let padding = " ".repeat(padding + label.len() + 5);
1199
1200         /// Returns `override` if it is present and `style` is `NoStyle` or `style` otherwise
1201         fn style_or_override(style: Style, override_: Option<Style>) -> Style {
1202             match (style, override_) {
1203                 (Style::NoStyle, Some(override_)) => override_,
1204                 _ => style,
1205             }
1206         }
1207
1208         let mut line_number = 0;
1209
1210         // Provided the following diagnostic message:
1211         //
1212         //     let msg = vec![
1213         //       ("
1214         //       ("highlighted multiline\nstring to\nsee how it ", Style::NoStyle),
1215         //       ("looks", Style::Highlight),
1216         //       ("with\nvery ", Style::NoStyle),
1217         //       ("weird", Style::Highlight),
1218         //       (" formats\n", Style::NoStyle),
1219         //       ("see?", Style::Highlight),
1220         //     ];
1221         //
1222         // the expected output on a note is (* surround the highlighted text)
1223         //
1224         //        = note: highlighted multiline
1225         //                string to
1226         //                see how it *looks* with
1227         //                very *weird* formats
1228         //                see?
1229         for &(ref text, ref style) in msg.iter() {
1230             let lines = text.split('\n').collect::<Vec<_>>();
1231             if lines.len() > 1 {
1232                 for (i, line) in lines.iter().enumerate() {
1233                     if i != 0 {
1234                         line_number += 1;
1235                         buffer.append(line_number, &padding, Style::NoStyle);
1236                     }
1237                     buffer.append(line_number, line, style_or_override(*style, override_style));
1238                 }
1239             } else {
1240                 buffer.append(line_number, text, style_or_override(*style, override_style));
1241             }
1242         }
1243     }
1244
1245     fn emit_message_default(
1246         &mut self,
1247         msp: &MultiSpan,
1248         msg: &[(String, Style)],
1249         code: &Option<DiagnosticId>,
1250         level: &Level,
1251         max_line_num_len: usize,
1252         is_secondary: bool,
1253     ) -> io::Result<()> {
1254         let mut buffer = StyledBuffer::new();
1255
1256         if !msp.has_primary_spans() && !msp.has_span_labels() && is_secondary && !self.short_message
1257         {
1258             // This is a secondary message with no span info
1259             for _ in 0..max_line_num_len {
1260                 buffer.prepend(0, " ", Style::NoStyle);
1261             }
1262             draw_note_separator(&mut buffer, 0, max_line_num_len + 1);
1263             if *level != Level::FailureNote {
1264                 buffer.append(0, level.to_str(), Style::MainHeaderMsg);
1265                 buffer.append(0, ": ", Style::NoStyle);
1266             }
1267             self.msg_to_buffer(&mut buffer, msg, max_line_num_len, "note", None);
1268         } else {
1269             let mut label_width = 0;
1270             // The failure note level itself does not provide any useful diagnostic information
1271             if *level != Level::FailureNote {
1272                 buffer.append(0, level.to_str(), Style::Level(*level));
1273                 label_width += level.to_str().len();
1274             }
1275             // only render error codes, not lint codes
1276             if let Some(DiagnosticId::Error(ref code)) = *code {
1277                 buffer.append(0, "[", Style::Level(*level));
1278                 buffer.append(0, &code, Style::Level(*level));
1279                 buffer.append(0, "]", Style::Level(*level));
1280                 label_width += 2 + code.len();
1281             }
1282             let header_style = if is_secondary { Style::HeaderMsg } else { Style::MainHeaderMsg };
1283             if *level != Level::FailureNote {
1284                 buffer.append(0, ": ", header_style);
1285                 label_width += 2;
1286             }
1287             for &(ref text, _) in msg.iter() {
1288                 // Account for newlines to align output to its label.
1289                 for (line, text) in normalize_whitespace(text).lines().enumerate() {
1290                     buffer.append(
1291                         0 + line,
1292                         &format!(
1293                             "{}{}",
1294                             if line == 0 { String::new() } else { " ".repeat(label_width) },
1295                             text
1296                         ),
1297                         header_style,
1298                     );
1299                 }
1300             }
1301         }
1302
1303         let mut annotated_files = FileWithAnnotatedLines::collect_annotations(msp, &self.sm);
1304
1305         // Make sure our primary file comes first
1306         let (primary_lo, sm) = if let (Some(sm), Some(ref primary_span)) =
1307             (self.sm.as_ref(), msp.primary_span().as_ref())
1308         {
1309             if !primary_span.is_dummy() {
1310                 (sm.lookup_char_pos(primary_span.lo()), sm)
1311             } else {
1312                 emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;
1313                 return Ok(());
1314             }
1315         } else {
1316             // If we don't have span information, emit and exit
1317             emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;
1318             return Ok(());
1319         };
1320         if let Ok(pos) =
1321             annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name))
1322         {
1323             annotated_files.swap(0, pos);
1324         }
1325
1326         // Print out the annotate source lines that correspond with the error
1327         for annotated_file in annotated_files {
1328             // we can't annotate anything if the source is unavailable.
1329             if !sm.ensure_source_file_source_present(annotated_file.file.clone()) {
1330                 continue;
1331             }
1332
1333             // print out the span location and spacer before we print the annotated source
1334             // to do this, we need to know if this span will be primary
1335             let is_primary = primary_lo.file.name == annotated_file.file.name;
1336             if is_primary {
1337                 let loc = primary_lo.clone();
1338                 if !self.short_message {
1339                     // remember where we are in the output buffer for easy reference
1340                     let buffer_msg_line_offset = buffer.num_lines();
1341
1342                     buffer.prepend(buffer_msg_line_offset, "--> ", Style::LineNumber);
1343                     buffer.append(
1344                         buffer_msg_line_offset,
1345                         &format!(
1346                             "{}:{}:{}",
1347                             sm.filename_for_diagnostics(&loc.file.name),
1348                             sm.doctest_offset_line(&loc.file.name, loc.line),
1349                             loc.col.0 + 1,
1350                         ),
1351                         Style::LineAndColumn,
1352                     );
1353                     for _ in 0..max_line_num_len {
1354                         buffer.prepend(buffer_msg_line_offset, " ", Style::NoStyle);
1355                     }
1356                 } else {
1357                     buffer.prepend(
1358                         0,
1359                         &format!(
1360                             "{}:{}:{}: ",
1361                             sm.filename_for_diagnostics(&loc.file.name),
1362                             sm.doctest_offset_line(&loc.file.name, loc.line),
1363                             loc.col.0 + 1,
1364                         ),
1365                         Style::LineAndColumn,
1366                     );
1367                 }
1368             } else if !self.short_message {
1369                 // remember where we are in the output buffer for easy reference
1370                 let buffer_msg_line_offset = buffer.num_lines();
1371
1372                 // Add spacing line
1373                 draw_col_separator_no_space(
1374                     &mut buffer,
1375                     buffer_msg_line_offset,
1376                     max_line_num_len + 1,
1377                 );
1378
1379                 // Then, the secondary file indicator
1380                 buffer.prepend(buffer_msg_line_offset + 1, "::: ", Style::LineNumber);
1381                 let loc = if let Some(first_line) = annotated_file.lines.first() {
1382                     let col = if let Some(first_annotation) = first_line.annotations.first() {
1383                         format!(":{}", first_annotation.start_col + 1)
1384                     } else {
1385                         String::new()
1386                     };
1387                     format!(
1388                         "{}:{}{}",
1389                         sm.filename_for_diagnostics(&annotated_file.file.name),
1390                         sm.doctest_offset_line(&annotated_file.file.name, first_line.line_index),
1391                         col
1392                     )
1393                 } else {
1394                     format!("{}", sm.filename_for_diagnostics(&annotated_file.file.name))
1395                 };
1396                 buffer.append(buffer_msg_line_offset + 1, &loc, Style::LineAndColumn);
1397                 for _ in 0..max_line_num_len {
1398                     buffer.prepend(buffer_msg_line_offset + 1, " ", Style::NoStyle);
1399                 }
1400             }
1401
1402             if !self.short_message {
1403                 // Put in the spacer between the location and annotated source
1404                 let buffer_msg_line_offset = buffer.num_lines();
1405                 draw_col_separator_no_space(
1406                     &mut buffer,
1407                     buffer_msg_line_offset,
1408                     max_line_num_len + 1,
1409                 );
1410
1411                 // Contains the vertical lines' positions for active multiline annotations
1412                 let mut multilines = FxHashMap::default();
1413
1414                 // Get the left-side margin to remove it
1415                 let mut whitespace_margin = usize::MAX;
1416                 for line_idx in 0..annotated_file.lines.len() {
1417                     let file = annotated_file.file.clone();
1418                     let line = &annotated_file.lines[line_idx];
1419                     if let Some(source_string) = file.get_line(line.line_index - 1) {
1420                         let leading_whitespace = source_string
1421                             .chars()
1422                             .take_while(|c| c.is_whitespace())
1423                             .map(|c| {
1424                                 match c {
1425                                     // Tabs are displayed as 4 spaces
1426                                     '\t' => 4,
1427                                     _ => 1,
1428                                 }
1429                             })
1430                             .sum();
1431                         if source_string.chars().any(|c| !c.is_whitespace()) {
1432                             whitespace_margin = min(whitespace_margin, leading_whitespace);
1433                         }
1434                     }
1435                 }
1436                 if whitespace_margin == usize::MAX {
1437                     whitespace_margin = 0;
1438                 }
1439
1440                 // Left-most column any visible span points at.
1441                 let mut span_left_margin = usize::MAX;
1442                 for line in &annotated_file.lines {
1443                     for ann in &line.annotations {
1444                         span_left_margin = min(span_left_margin, ann.start_col);
1445                         span_left_margin = min(span_left_margin, ann.end_col);
1446                     }
1447                 }
1448                 if span_left_margin == usize::MAX {
1449                     span_left_margin = 0;
1450                 }
1451
1452                 // Right-most column any visible span points at.
1453                 let mut span_right_margin = 0;
1454                 let mut label_right_margin = 0;
1455                 let mut max_line_len = 0;
1456                 for line in &annotated_file.lines {
1457                     max_line_len = max(
1458                         max_line_len,
1459                         annotated_file.file.get_line(line.line_index - 1).map_or(0, |s| s.len()),
1460                     );
1461                     for ann in &line.annotations {
1462                         span_right_margin = max(span_right_margin, ann.start_col);
1463                         span_right_margin = max(span_right_margin, ann.end_col);
1464                         // FIXME: account for labels not in the same line
1465                         let label_right = ann.label.as_ref().map_or(0, |l| l.len() + 1);
1466                         label_right_margin = max(label_right_margin, ann.end_col + label_right);
1467                     }
1468                 }
1469
1470                 let width_offset = 3 + max_line_num_len;
1471                 let code_offset = if annotated_file.multiline_depth == 0 {
1472                     width_offset
1473                 } else {
1474                     width_offset + annotated_file.multiline_depth + 1
1475                 };
1476
1477                 let column_width = if let Some(width) = self.terminal_width {
1478                     width.saturating_sub(code_offset)
1479                 } else if self.ui_testing {
1480                     DEFAULT_COLUMN_WIDTH
1481                 } else {
1482                     termize::dimensions()
1483                         .map(|(w, _)| w.saturating_sub(code_offset))
1484                         .unwrap_or(DEFAULT_COLUMN_WIDTH)
1485                 };
1486
1487                 let margin = Margin::new(
1488                     whitespace_margin,
1489                     span_left_margin,
1490                     span_right_margin,
1491                     label_right_margin,
1492                     column_width,
1493                     max_line_len,
1494                 );
1495
1496                 // Next, output the annotate source for this file
1497                 for line_idx in 0..annotated_file.lines.len() {
1498                     let previous_buffer_line = buffer.num_lines();
1499
1500                     let depths = self.render_source_line(
1501                         &mut buffer,
1502                         annotated_file.file.clone(),
1503                         &annotated_file.lines[line_idx],
1504                         width_offset,
1505                         code_offset,
1506                         margin,
1507                     );
1508
1509                     let mut to_add = FxHashMap::default();
1510
1511                     for (depth, style) in depths {
1512                         if multilines.remove(&depth).is_none() {
1513                             to_add.insert(depth, style);
1514                         }
1515                     }
1516
1517                     // Set the multiline annotation vertical lines to the left of
1518                     // the code in this line.
1519                     for (depth, style) in &multilines {
1520                         for line in previous_buffer_line..buffer.num_lines() {
1521                             draw_multiline_line(&mut buffer, line, width_offset, *depth, *style);
1522                         }
1523                     }
1524                     // check to see if we need to print out or elide lines that come between
1525                     // this annotated line and the next one.
1526                     if line_idx < (annotated_file.lines.len() - 1) {
1527                         let line_idx_delta = annotated_file.lines[line_idx + 1].line_index
1528                             - annotated_file.lines[line_idx].line_index;
1529                         if line_idx_delta > 2 {
1530                             let last_buffer_line_num = buffer.num_lines();
1531                             buffer.puts(last_buffer_line_num, 0, "...", Style::LineNumber);
1532
1533                             // Set the multiline annotation vertical lines on `...` bridging line.
1534                             for (depth, style) in &multilines {
1535                                 draw_multiline_line(
1536                                     &mut buffer,
1537                                     last_buffer_line_num,
1538                                     width_offset,
1539                                     *depth,
1540                                     *style,
1541                                 );
1542                             }
1543                         } else if line_idx_delta == 2 {
1544                             let unannotated_line = annotated_file
1545                                 .file
1546                                 .get_line(annotated_file.lines[line_idx].line_index)
1547                                 .unwrap_or_else(|| Cow::from(""));
1548
1549                             let last_buffer_line_num = buffer.num_lines();
1550
1551                             self.draw_line(
1552                                 &mut buffer,
1553                                 &normalize_whitespace(&unannotated_line),
1554                                 annotated_file.lines[line_idx + 1].line_index - 1,
1555                                 last_buffer_line_num,
1556                                 width_offset,
1557                                 code_offset,
1558                                 margin,
1559                             );
1560
1561                             for (depth, style) in &multilines {
1562                                 draw_multiline_line(
1563                                     &mut buffer,
1564                                     last_buffer_line_num,
1565                                     width_offset,
1566                                     *depth,
1567                                     *style,
1568                                 );
1569                             }
1570                         }
1571                     }
1572
1573                     multilines.extend(&to_add);
1574                 }
1575             }
1576         }
1577
1578         // final step: take our styled buffer, render it, then output it
1579         emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;
1580
1581         Ok(())
1582     }
1583
1584     fn emit_suggestion_default(
1585         &mut self,
1586         suggestion: &CodeSuggestion,
1587         level: &Level,
1588         max_line_num_len: usize,
1589     ) -> io::Result<()> {
1590         let sm = match self.sm {
1591             Some(ref sm) => sm,
1592             None => return Ok(()),
1593         };
1594
1595         // Render the replacements for each suggestion
1596         let suggestions = suggestion.splice_lines(&**sm);
1597         debug!("emit_suggestion_default: suggestions={:?}", suggestions);
1598
1599         if suggestions.is_empty() {
1600             // Suggestions coming from macros can have malformed spans. This is a heavy handed
1601             // approach to avoid ICEs by ignoring the suggestion outright.
1602             return Ok(());
1603         }
1604
1605         let mut buffer = StyledBuffer::new();
1606
1607         // Render the suggestion message
1608         buffer.append(0, level.to_str(), Style::Level(*level));
1609         buffer.append(0, ": ", Style::HeaderMsg);
1610
1611         self.msg_to_buffer(
1612             &mut buffer,
1613             &[(suggestion.msg.to_owned(), Style::NoStyle)],
1614             max_line_num_len,
1615             "suggestion",
1616             Some(Style::HeaderMsg),
1617         );
1618
1619         let mut row_num = 2;
1620         draw_col_separator_no_space(&mut buffer, 1, max_line_num_len + 1);
1621         let mut notice_capitalization = false;
1622         for (complete, parts, highlights, only_capitalization) in
1623             suggestions.iter().take(MAX_SUGGESTIONS)
1624         {
1625             notice_capitalization |= only_capitalization;
1626             // Only show underline if the suggestion spans a single line and doesn't cover the
1627             // entirety of the code output. If you have multiple replacements in the same line
1628             // of code, show the underline.
1629             let show_underline = !(parts.len() == 1 && parts[0].snippet.trim() == complete.trim())
1630                 && complete.lines().count() == 1;
1631
1632             let has_deletion = parts.iter().any(|p| p.is_deletion());
1633             let is_multiline = complete.lines().count() > 1;
1634
1635             let show_diff = has_deletion && !is_multiline;
1636
1637             if show_diff {
1638                 row_num += 1;
1639             }
1640
1641             let file_lines = sm
1642                 .span_to_lines(parts[0].span)
1643                 .expect("span_to_lines failed when emitting suggestion");
1644
1645             assert!(!file_lines.lines.is_empty() || parts[0].span.is_dummy());
1646
1647             let line_start = sm.lookup_char_pos(parts[0].span.lo()).line;
1648             draw_col_separator_no_space(&mut buffer, 1, max_line_num_len + 1);
1649             let mut lines = complete.lines();
1650             for (line_pos, (line, highlight_parts)) in
1651                 lines.by_ref().zip(highlights).take(MAX_SUGGESTION_HIGHLIGHT_LINES).enumerate()
1652             {
1653                 // Print the span column to avoid confusion
1654                 buffer.puts(
1655                     row_num,
1656                     0,
1657                     &self.maybe_anonymized(line_start + line_pos),
1658                     Style::LineNumber,
1659                 );
1660                 if show_diff {
1661                     // Add the line number for both addition and removal to drive the point home.
1662                     //
1663                     // N - fn foo<A: T>(bar: A) {
1664                     // N + fn foo(bar: impl T) {
1665                     buffer.puts(
1666                         row_num - 1,
1667                         0,
1668                         &self.maybe_anonymized(line_start + line_pos),
1669                         Style::LineNumber,
1670                     );
1671                     buffer.puts(row_num - 1, max_line_num_len + 1, "- ", Style::Removal);
1672                     buffer.puts(
1673                         row_num - 1,
1674                         max_line_num_len + 3,
1675                         &normalize_whitespace(
1676                             &*file_lines
1677                                 .file
1678                                 .get_line(file_lines.lines[line_pos].line_index)
1679                                 .unwrap(),
1680                         ),
1681                         Style::NoStyle,
1682                     );
1683                     buffer.puts(row_num, max_line_num_len + 1, "+ ", Style::Addition);
1684                 } else if is_multiline {
1685                     match &highlight_parts[..] {
1686                         [SubstitutionHighlight { start: 0, end }] if *end == line.len() => {
1687                             buffer.puts(row_num, max_line_num_len + 1, "+ ", Style::Addition);
1688                         }
1689                         [] => {
1690                             draw_col_separator(&mut buffer, row_num, max_line_num_len + 1);
1691                         }
1692                         _ => {
1693                             buffer.puts(row_num, max_line_num_len + 1, "~ ", Style::Addition);
1694                         }
1695                     }
1696                 } else {
1697                     draw_col_separator(&mut buffer, row_num, max_line_num_len + 1);
1698                 }
1699
1700                 // print the suggestion
1701                 buffer.append(row_num, &normalize_whitespace(line), Style::NoStyle);
1702
1703                 // Colorize addition/replacements with green.
1704                 for &SubstitutionHighlight { start, end } in highlight_parts {
1705                     // Account for tabs when highlighting (#87972).
1706                     let tabs: usize = line
1707                         .chars()
1708                         .take(start)
1709                         .map(|ch| match ch {
1710                             '\t' => 3,
1711                             _ => 0,
1712                         })
1713                         .sum();
1714                     buffer.set_style_range(
1715                         row_num,
1716                         max_line_num_len + 3 + start + tabs,
1717                         max_line_num_len + 3 + end + tabs,
1718                         Style::Addition,
1719                         true,
1720                     );
1721                 }
1722                 row_num += 1;
1723             }
1724
1725             // This offset and the ones below need to be signed to account for replacement code
1726             // that is shorter than the original code.
1727             let mut offsets: Vec<(usize, isize)> = Vec::new();
1728             // Only show an underline in the suggestions if the suggestion is not the
1729             // entirety of the code being shown and the displayed code is not multiline.
1730             if show_underline {
1731                 draw_col_separator(&mut buffer, row_num, max_line_num_len + 1);
1732                 for part in parts {
1733                     let span_start_pos = sm.lookup_char_pos(part.span.lo()).col_display;
1734                     let span_end_pos = sm.lookup_char_pos(part.span.hi()).col_display;
1735
1736                     // Do not underline the leading...
1737                     let start = part.snippet.len().saturating_sub(part.snippet.trim_start().len());
1738                     // ...or trailing spaces. Account for substitutions containing unicode
1739                     // characters.
1740                     let sub_len: usize = part
1741                         .snippet
1742                         .trim()
1743                         .chars()
1744                         .map(|ch| unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1))
1745                         .sum();
1746
1747                     let offset: isize = offsets
1748                         .iter()
1749                         .filter_map(
1750                             |(start, v)| if span_start_pos <= *start { None } else { Some(v) },
1751                         )
1752                         .sum();
1753                     let underline_start = (span_start_pos + start) as isize + offset;
1754                     let underline_end = (span_start_pos + start + sub_len) as isize + offset;
1755                     assert!(underline_start >= 0 && underline_end >= 0);
1756                     let padding: usize = max_line_num_len + 3;
1757                     for p in underline_start..underline_end {
1758                         if !show_diff {
1759                             // If this is a replacement, underline with `^`, if this is an addition
1760                             // underline with `+`.
1761                             buffer.putc(
1762                                 row_num,
1763                                 (padding as isize + p) as usize,
1764                                 if part.is_addition(&sm) { '+' } else { '~' },
1765                                 Style::Addition,
1766                             );
1767                         }
1768                     }
1769                     if show_diff {
1770                         // Colorize removal with red in diff format.
1771                         buffer.set_style_range(
1772                             row_num - 2,
1773                             (padding as isize + span_start_pos as isize) as usize,
1774                             (padding as isize + span_end_pos as isize) as usize,
1775                             Style::Removal,
1776                             true,
1777                         );
1778                     }
1779
1780                     // length of the code after substitution
1781                     let full_sub_len = part
1782                         .snippet
1783                         .chars()
1784                         .map(|ch| unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1))
1785                         .sum::<usize>() as isize;
1786
1787                     // length of the code to be substituted
1788                     let snippet_len = span_end_pos as isize - span_start_pos as isize;
1789                     // For multiple substitutions, use the position *after* the previous
1790                     // substitutions have happened, only when further substitutions are
1791                     // located strictly after.
1792                     offsets.push((span_end_pos, full_sub_len - snippet_len));
1793                 }
1794                 row_num += 1;
1795             }
1796
1797             // if we elided some lines, add an ellipsis
1798             if lines.next().is_some() {
1799                 buffer.puts(row_num, max_line_num_len - 1, "...", Style::LineNumber);
1800             } else if !show_underline {
1801                 draw_col_separator_no_space(&mut buffer, row_num, max_line_num_len + 1);
1802                 row_num += 1;
1803             }
1804         }
1805         if suggestions.len() > MAX_SUGGESTIONS {
1806             let others = suggestions.len() - MAX_SUGGESTIONS;
1807             let msg = format!("and {} other candidate{}", others, pluralize!(others));
1808             buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle);
1809         } else if notice_capitalization {
1810             let msg = "notice the capitalization difference";
1811             buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle);
1812         }
1813         emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;
1814         Ok(())
1815     }
1816
1817     fn emit_messages_default(
1818         &mut self,
1819         level: &Level,
1820         message: &[(String, Style)],
1821         code: &Option<DiagnosticId>,
1822         span: &MultiSpan,
1823         children: &[SubDiagnostic],
1824         suggestions: &[CodeSuggestion],
1825     ) {
1826         let max_line_num_len = if self.ui_testing {
1827             ANONYMIZED_LINE_NUM.len()
1828         } else {
1829             let n = self.get_max_line_num(span, children);
1830             num_decimal_digits(n)
1831         };
1832
1833         match self.emit_message_default(span, message, code, level, max_line_num_len, false) {
1834             Ok(()) => {
1835                 if !children.is_empty()
1836                     || suggestions.iter().any(|s| s.style != SuggestionStyle::CompletelyHidden)
1837                 {
1838                     let mut buffer = StyledBuffer::new();
1839                     if !self.short_message {
1840                         draw_col_separator_no_space(&mut buffer, 0, max_line_num_len + 1);
1841                     }
1842                     if let Err(e) = emit_to_destination(
1843                         &buffer.render(),
1844                         level,
1845                         &mut self.dst,
1846                         self.short_message,
1847                     ) {
1848                         panic!("failed to emit error: {}", e)
1849                     }
1850                 }
1851                 if !self.short_message {
1852                     for child in children {
1853                         let span = child.render_span.as_ref().unwrap_or(&child.span);
1854                         if let Err(err) = self.emit_message_default(
1855                             &span,
1856                             &child.styled_message(),
1857                             &None,
1858                             &child.level,
1859                             max_line_num_len,
1860                             true,
1861                         ) {
1862                             panic!("failed to emit error: {}", err);
1863                         }
1864                     }
1865                     for sugg in suggestions {
1866                         if sugg.style == SuggestionStyle::CompletelyHidden {
1867                             // do not display this suggestion, it is meant only for tools
1868                         } else if sugg.style == SuggestionStyle::HideCodeAlways {
1869                             if let Err(e) = self.emit_message_default(
1870                                 &MultiSpan::new(),
1871                                 &[(sugg.msg.to_owned(), Style::HeaderMsg)],
1872                                 &None,
1873                                 &Level::Help,
1874                                 max_line_num_len,
1875                                 true,
1876                             ) {
1877                                 panic!("failed to emit error: {}", e);
1878                             }
1879                         } else if let Err(e) =
1880                             self.emit_suggestion_default(sugg, &Level::Help, max_line_num_len)
1881                         {
1882                             panic!("failed to emit error: {}", e);
1883                         };
1884                     }
1885                 }
1886             }
1887             Err(e) => panic!("failed to emit error: {}", e),
1888         }
1889
1890         let mut dst = self.dst.writable();
1891         match writeln!(dst) {
1892             Err(e) => panic!("failed to emit error: {}", e),
1893             _ => {
1894                 if let Err(e) = dst.flush() {
1895                     panic!("failed to emit error: {}", e)
1896                 }
1897             }
1898         }
1899     }
1900 }
1901
1902 impl FileWithAnnotatedLines {
1903     /// Preprocess all the annotations so that they are grouped by file and by line number
1904     /// This helps us quickly iterate over the whole message (including secondary file spans)
1905     pub fn collect_annotations(
1906         msp: &MultiSpan,
1907         source_map: &Option<Lrc<SourceMap>>,
1908     ) -> Vec<FileWithAnnotatedLines> {
1909         fn add_annotation_to_file(
1910             file_vec: &mut Vec<FileWithAnnotatedLines>,
1911             file: Lrc<SourceFile>,
1912             line_index: usize,
1913             ann: Annotation,
1914         ) {
1915             for slot in file_vec.iter_mut() {
1916                 // Look through each of our files for the one we're adding to
1917                 if slot.file.name == file.name {
1918                     // See if we already have a line for it
1919                     for line_slot in &mut slot.lines {
1920                         if line_slot.line_index == line_index {
1921                             line_slot.annotations.push(ann);
1922                             return;
1923                         }
1924                     }
1925                     // We don't have a line yet, create one
1926                     slot.lines.push(Line { line_index, annotations: vec![ann] });
1927                     slot.lines.sort();
1928                     return;
1929                 }
1930             }
1931             // This is the first time we're seeing the file
1932             file_vec.push(FileWithAnnotatedLines {
1933                 file,
1934                 lines: vec![Line { line_index, annotations: vec![ann] }],
1935                 multiline_depth: 0,
1936             });
1937         }
1938
1939         let mut output = vec![];
1940         let mut multiline_annotations = vec![];
1941
1942         if let Some(ref sm) = source_map {
1943             for span_label in msp.span_labels() {
1944                 if span_label.span.is_dummy() {
1945                     continue;
1946                 }
1947
1948                 let lo = sm.lookup_char_pos(span_label.span.lo());
1949                 let mut hi = sm.lookup_char_pos(span_label.span.hi());
1950
1951                 // Watch out for "empty spans". If we get a span like 6..6, we
1952                 // want to just display a `^` at 6, so convert that to
1953                 // 6..7. This is degenerate input, but it's best to degrade
1954                 // gracefully -- and the parser likes to supply a span like
1955                 // that for EOF, in particular.
1956
1957                 if lo.col_display == hi.col_display && lo.line == hi.line {
1958                     hi.col_display += 1;
1959                 }
1960
1961                 if lo.line != hi.line {
1962                     let ml = MultilineAnnotation {
1963                         depth: 1,
1964                         line_start: lo.line,
1965                         line_end: hi.line,
1966                         start_col: lo.col_display,
1967                         end_col: hi.col_display,
1968                         is_primary: span_label.is_primary,
1969                         label: span_label.label,
1970                         overlaps_exactly: false,
1971                     };
1972                     multiline_annotations.push((lo.file, ml));
1973                 } else {
1974                     let ann = Annotation {
1975                         start_col: lo.col_display,
1976                         end_col: hi.col_display,
1977                         is_primary: span_label.is_primary,
1978                         label: span_label.label,
1979                         annotation_type: AnnotationType::Singleline,
1980                     };
1981                     add_annotation_to_file(&mut output, lo.file, lo.line, ann);
1982                 };
1983             }
1984         }
1985
1986         // Find overlapping multiline annotations, put them at different depths
1987         multiline_annotations.sort_by_key(|&(_, ref ml)| (ml.line_start, ml.line_end));
1988         for (_, ann) in multiline_annotations.clone() {
1989             for (_, a) in multiline_annotations.iter_mut() {
1990                 // Move all other multiline annotations overlapping with this one
1991                 // one level to the right.
1992                 if !(ann.same_span(a))
1993                     && num_overlap(ann.line_start, ann.line_end, a.line_start, a.line_end, true)
1994                 {
1995                     a.increase_depth();
1996                 } else if ann.same_span(a) && &ann != a {
1997                     a.overlaps_exactly = true;
1998                 } else {
1999                     break;
2000                 }
2001             }
2002         }
2003
2004         let mut max_depth = 0; // max overlapping multiline spans
2005         for (file, ann) in multiline_annotations {
2006             max_depth = max(max_depth, ann.depth);
2007             let mut end_ann = ann.as_end();
2008             if !ann.overlaps_exactly {
2009                 // avoid output like
2010                 //
2011                 //  |        foo(
2012                 //  |   _____^
2013                 //  |  |_____|
2014                 //  | ||         bar,
2015                 //  | ||     );
2016                 //  | ||      ^
2017                 //  | ||______|
2018                 //  |  |______foo
2019                 //  |         baz
2020                 //
2021                 // and instead get
2022                 //
2023                 //  |       foo(
2024                 //  |  _____^
2025                 //  | |         bar,
2026                 //  | |     );
2027                 //  | |      ^
2028                 //  | |      |
2029                 //  | |______foo
2030                 //  |        baz
2031                 add_annotation_to_file(&mut output, file.clone(), ann.line_start, ann.as_start());
2032                 // 4 is the minimum vertical length of a multiline span when presented: two lines
2033                 // of code and two lines of underline. This is not true for the special case where
2034                 // the beginning doesn't have an underline, but the current logic seems to be
2035                 // working correctly.
2036                 let middle = min(ann.line_start + 4, ann.line_end);
2037                 for line in ann.line_start + 1..middle {
2038                     // Every `|` that joins the beginning of the span (`___^`) to the end (`|__^`).
2039                     add_annotation_to_file(&mut output, file.clone(), line, ann.as_line());
2040                 }
2041                 let line_end = ann.line_end - 1;
2042                 if middle < line_end {
2043                     add_annotation_to_file(&mut output, file.clone(), line_end, ann.as_line());
2044                 }
2045             } else {
2046                 end_ann.annotation_type = AnnotationType::Singleline;
2047             }
2048             add_annotation_to_file(&mut output, file, ann.line_end, end_ann);
2049         }
2050         for file_vec in output.iter_mut() {
2051             file_vec.multiline_depth = max_depth;
2052         }
2053         output
2054     }
2055 }
2056
2057 // instead of taking the String length or dividing by 10 while > 0, we multiply a limit by 10 until
2058 // we're higher. If the loop isn't exited by the `return`, the last multiplication will wrap, which
2059 // is OK, because while we cannot fit a higher power of 10 in a usize, the loop will end anyway.
2060 // This is also why we need the max number of decimal digits within a `usize`.
2061 fn num_decimal_digits(num: usize) -> usize {
2062     #[cfg(target_pointer_width = "64")]
2063     const MAX_DIGITS: usize = 20;
2064
2065     #[cfg(target_pointer_width = "32")]
2066     const MAX_DIGITS: usize = 10;
2067
2068     #[cfg(target_pointer_width = "16")]
2069     const MAX_DIGITS: usize = 5;
2070
2071     let mut lim = 10;
2072     for num_digits in 1..MAX_DIGITS {
2073         if num < lim {
2074             return num_digits;
2075         }
2076         lim = lim.wrapping_mul(10);
2077     }
2078     MAX_DIGITS
2079 }
2080
2081 // We replace some characters so the CLI output is always consistent and underlines aligned.
2082 const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[
2083     ('\t', "    "),   // We do our own tab replacement
2084     ('\u{200D}', ""), // Replace ZWJ with nothing for consistent terminal output of grapheme clusters.
2085     ('\u{202A}', ""), // The following unicode text flow control characters are inconsistently
2086     ('\u{202B}', ""), // supported accross CLIs and can cause confusion due to the bytes on disk
2087     ('\u{202D}', ""), // not corresponding to the visible source code, so we replace them always.
2088     ('\u{202E}', ""),
2089     ('\u{2066}', ""),
2090     ('\u{2067}', ""),
2091     ('\u{2068}', ""),
2092     ('\u{202C}', ""),
2093     ('\u{2069}', ""),
2094 ];
2095
2096 fn normalize_whitespace(str: &str) -> String {
2097     let mut s = str.to_string();
2098     for (c, replacement) in OUTPUT_REPLACEMENTS {
2099         s = s.replace(*c, replacement);
2100     }
2101     s
2102 }
2103
2104 fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
2105     buffer.puts(line, col, "| ", Style::LineNumber);
2106 }
2107
2108 fn draw_col_separator_no_space(buffer: &mut StyledBuffer, line: usize, col: usize) {
2109     draw_col_separator_no_space_with_style(buffer, line, col, Style::LineNumber);
2110 }
2111
2112 fn draw_col_separator_no_space_with_style(
2113     buffer: &mut StyledBuffer,
2114     line: usize,
2115     col: usize,
2116     style: Style,
2117 ) {
2118     buffer.putc(line, col, '|', style);
2119 }
2120
2121 fn draw_range(
2122     buffer: &mut StyledBuffer,
2123     symbol: char,
2124     line: usize,
2125     col_from: usize,
2126     col_to: usize,
2127     style: Style,
2128 ) {
2129     for col in col_from..col_to {
2130         buffer.putc(line, col, symbol, style);
2131     }
2132 }
2133
2134 fn draw_note_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
2135     buffer.puts(line, col, "= ", Style::LineNumber);
2136 }
2137
2138 fn draw_multiline_line(
2139     buffer: &mut StyledBuffer,
2140     line: usize,
2141     offset: usize,
2142     depth: usize,
2143     style: Style,
2144 ) {
2145     buffer.putc(line, offset + depth - 1, '|', style);
2146 }
2147
2148 fn num_overlap(
2149     a_start: usize,
2150     a_end: usize,
2151     b_start: usize,
2152     b_end: usize,
2153     inclusive: bool,
2154 ) -> bool {
2155     let extra = if inclusive { 1 } else { 0 };
2156     (b_start..b_end + extra).contains(&a_start) || (a_start..a_end + extra).contains(&b_start)
2157 }
2158 fn overlaps(a1: &Annotation, a2: &Annotation, padding: usize) -> bool {
2159     num_overlap(a1.start_col, a1.end_col + padding, a2.start_col, a2.end_col, false)
2160 }
2161
2162 fn emit_to_destination(
2163     rendered_buffer: &[Vec<StyledString>],
2164     lvl: &Level,
2165     dst: &mut Destination,
2166     short_message: bool,
2167 ) -> io::Result<()> {
2168     use crate::lock;
2169
2170     let mut dst = dst.writable();
2171
2172     // In order to prevent error message interleaving, where multiple error lines get intermixed
2173     // when multiple compiler processes error simultaneously, we emit errors with additional
2174     // steps.
2175     //
2176     // On Unix systems, we write into a buffered terminal rather than directly to a terminal. When
2177     // the .flush() is called we take the buffer created from the buffered writes and write it at
2178     // one shot.  Because the Unix systems use ANSI for the colors, which is a text-based styling
2179     // scheme, this buffered approach works and maintains the styling.
2180     //
2181     // On Windows, styling happens through calls to a terminal API. This prevents us from using the
2182     // same buffering approach.  Instead, we use a global Windows mutex, which we acquire long
2183     // enough to output the full error message, then we release.
2184     let _buffer_lock = lock::acquire_global_lock("rustc_errors");
2185     for (pos, line) in rendered_buffer.iter().enumerate() {
2186         for part in line {
2187             dst.apply_style(*lvl, part.style)?;
2188             write!(dst, "{}", part.text)?;
2189             dst.reset()?;
2190         }
2191         if !short_message && (!lvl.is_failure_note() || pos != rendered_buffer.len() - 1) {
2192             writeln!(dst)?;
2193         }
2194     }
2195     dst.flush()?;
2196     Ok(())
2197 }
2198
2199 pub enum Destination {
2200     Terminal(StandardStream),
2201     Buffered(BufferWriter),
2202     // The bool denotes whether we should be emitting ansi color codes or not
2203     Raw(Box<(dyn Write + Send)>, bool),
2204 }
2205
2206 pub enum WritableDst<'a> {
2207     Terminal(&'a mut StandardStream),
2208     Buffered(&'a mut BufferWriter, Buffer),
2209     Raw(&'a mut (dyn Write + Send)),
2210     ColoredRaw(Ansi<&'a mut (dyn Write + Send)>),
2211 }
2212
2213 impl Destination {
2214     fn from_stderr(color: ColorConfig) -> Destination {
2215         let choice = color.to_color_choice();
2216         // On Windows we'll be performing global synchronization on the entire
2217         // system for emitting rustc errors, so there's no need to buffer
2218         // anything.
2219         //
2220         // On non-Windows we rely on the atomicity of `write` to ensure errors
2221         // don't get all jumbled up.
2222         if cfg!(windows) {
2223             Terminal(StandardStream::stderr(choice))
2224         } else {
2225             Buffered(BufferWriter::stderr(choice))
2226         }
2227     }
2228
2229     fn writable(&mut self) -> WritableDst<'_> {
2230         match *self {
2231             Destination::Terminal(ref mut t) => WritableDst::Terminal(t),
2232             Destination::Buffered(ref mut t) => {
2233                 let buf = t.buffer();
2234                 WritableDst::Buffered(t, buf)
2235             }
2236             Destination::Raw(ref mut t, false) => WritableDst::Raw(t),
2237             Destination::Raw(ref mut t, true) => WritableDst::ColoredRaw(Ansi::new(t)),
2238         }
2239     }
2240
2241     fn supports_color(&self) -> bool {
2242         match *self {
2243             Self::Terminal(ref stream) => stream.supports_color(),
2244             Self::Buffered(ref buffer) => buffer.buffer().supports_color(),
2245             Self::Raw(_, supports_color) => supports_color,
2246         }
2247     }
2248 }
2249
2250 impl<'a> WritableDst<'a> {
2251     fn apply_style(&mut self, lvl: Level, style: Style) -> io::Result<()> {
2252         let mut spec = ColorSpec::new();
2253         match style {
2254             Style::Addition => {
2255                 spec.set_fg(Some(Color::Green)).set_intense(true);
2256             }
2257             Style::Removal => {
2258                 spec.set_fg(Some(Color::Red)).set_intense(true);
2259             }
2260             Style::LineAndColumn => {}
2261             Style::LineNumber => {
2262                 spec.set_bold(true);
2263                 spec.set_intense(true);
2264                 if cfg!(windows) {
2265                     spec.set_fg(Some(Color::Cyan));
2266                 } else {
2267                     spec.set_fg(Some(Color::Blue));
2268                 }
2269             }
2270             Style::Quotation => {}
2271             Style::MainHeaderMsg => {
2272                 spec.set_bold(true);
2273                 if cfg!(windows) {
2274                     spec.set_intense(true).set_fg(Some(Color::White));
2275                 }
2276             }
2277             Style::UnderlinePrimary | Style::LabelPrimary => {
2278                 spec = lvl.color();
2279                 spec.set_bold(true);
2280             }
2281             Style::UnderlineSecondary | Style::LabelSecondary => {
2282                 spec.set_bold(true).set_intense(true);
2283                 if cfg!(windows) {
2284                     spec.set_fg(Some(Color::Cyan));
2285                 } else {
2286                     spec.set_fg(Some(Color::Blue));
2287                 }
2288             }
2289             Style::HeaderMsg | Style::NoStyle => {}
2290             Style::Level(lvl) => {
2291                 spec = lvl.color();
2292                 spec.set_bold(true);
2293             }
2294             Style::Highlight => {
2295                 spec.set_bold(true);
2296             }
2297         }
2298         self.set_color(&spec)
2299     }
2300
2301     fn set_color(&mut self, color: &ColorSpec) -> io::Result<()> {
2302         match *self {
2303             WritableDst::Terminal(ref mut t) => t.set_color(color),
2304             WritableDst::Buffered(_, ref mut t) => t.set_color(color),
2305             WritableDst::ColoredRaw(ref mut t) => t.set_color(color),
2306             WritableDst::Raw(_) => Ok(()),
2307         }
2308     }
2309
2310     fn reset(&mut self) -> io::Result<()> {
2311         match *self {
2312             WritableDst::Terminal(ref mut t) => t.reset(),
2313             WritableDst::Buffered(_, ref mut t) => t.reset(),
2314             WritableDst::ColoredRaw(ref mut t) => t.reset(),
2315             WritableDst::Raw(_) => Ok(()),
2316         }
2317     }
2318 }
2319
2320 impl<'a> Write for WritableDst<'a> {
2321     fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
2322         match *self {
2323             WritableDst::Terminal(ref mut t) => t.write(bytes),
2324             WritableDst::Buffered(_, ref mut buf) => buf.write(bytes),
2325             WritableDst::Raw(ref mut w) => w.write(bytes),
2326             WritableDst::ColoredRaw(ref mut t) => t.write(bytes),
2327         }
2328     }
2329
2330     fn flush(&mut self) -> io::Result<()> {
2331         match *self {
2332             WritableDst::Terminal(ref mut t) => t.flush(),
2333             WritableDst::Buffered(_, ref mut buf) => buf.flush(),
2334             WritableDst::Raw(ref mut w) => w.flush(),
2335             WritableDst::ColoredRaw(ref mut w) => w.flush(),
2336         }
2337     }
2338 }
2339
2340 impl<'a> Drop for WritableDst<'a> {
2341     fn drop(&mut self) {
2342         if let WritableDst::Buffered(ref mut dst, ref mut buf) = self {
2343             drop(dst.print(buf));
2344         }
2345     }
2346 }
2347
2348 /// Whether the original and suggested code are visually similar enough to warrant extra wording.
2349 pub fn is_case_difference(sm: &SourceMap, suggested: &str, sp: Span) -> bool {
2350     // FIXME: this should probably be extended to also account for `FO0` → `FOO` and unicode.
2351     let found = match sm.span_to_snippet(sp) {
2352         Ok(snippet) => snippet,
2353         Err(e) => {
2354             warn!(error = ?e, "Invalid span {:?}", sp);
2355             return false;
2356         }
2357     };
2358     let ascii_confusables = &['c', 'f', 'i', 'k', 'o', 's', 'u', 'v', 'w', 'x', 'y', 'z'];
2359     // All the chars that differ in capitalization are confusable (above):
2360     let confusable = iter::zip(found.chars(), suggested.chars())
2361         .filter(|(f, s)| f != s)
2362         .all(|(f, s)| (ascii_confusables.contains(&f) || ascii_confusables.contains(&s)));
2363     confusable && found.to_lowercase() == suggested.to_lowercase()
2364             // FIXME: We sometimes suggest the same thing we already have, which is a
2365             //        bug, but be defensive against that here.
2366             && found != suggested
2367 }