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