diff --git a/.cargo/config b/.cargo/config index c1ea8d02bf4a..aabd82dd6c1c 100644 --- a/.cargo/config +++ b/.cargo/config @@ -2,10 +2,8 @@ [build] rustflags = [ "--cfg", "procmacro2_semver_exempt", - "--cfg", "parallel_queries", ] rustdocflags = [ "--cfg", "procmacro2_semver_exempt", - "--cfg", "parallel_queries", ] diff --git a/common/Cargo.toml b/common/Cargo.toml index ff5b260ac2e6..8d28f795b5f2 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,17 +1,30 @@ [package] name = "swc_common" -version = "0.1.4" +version = "0.2.0" authors = ["강동윤 "] license = "Apache-2.0/MIT" repository = "https://github.com/swc-project/swc.git" documentation = "https://swc-project.github.io/rustdoc/swc_common/" description = "Common utilities for the swc project." +[features] +default = [] +# Enable folder and visitor. Requires nightly compiler. +fold = ["ast_node/fold"] + + [dependencies] -ast_node = { version = "0.2", path = "../macros/ast_node" } +ast_node = { version = "0.3", path = "../macros/ast_node" } string_cache = "0.7" either = "1.5" -rustc-ap-rustc_errors = "313" -rustc-ap-syntax = "313" -rustc-ap-rustc_data_structures = "313" -rustc-ap-syntax_pos = "313" \ No newline at end of file +scoped-tls = { version = "0.1.1", features = ["nightly"] } +unicode-width = "0.1.4" +cfg-if = "0.1.2" +log = "0.4" +atty = "0.2" +parking_lot = "0.6" +fxhash = "0.2" +termcolor = "1.0" +rayon = "1" +rayon-core = "1" +owning_ref = "0.4" diff --git a/common/src/errors/diagnostic.rs b/common/src/errors/diagnostic.rs index 9a1247ab7b1c..5abdfecaff4c 100644 --- a/common/src/errors/diagnostic.rs +++ b/common/src/errors/diagnostic.rs @@ -1,62 +1,107 @@ -use super::Handler; -use rustc_errors::{Diagnostic as RustcDiagnostic, Level}; -pub use rustc_errors::{DiagnosticId, DiagnosticStyledString}; +use super::{snippet::Style, Applicability, CodeSuggestion, Level, Substitution, SubstitutionPart}; use std::fmt; -use MultiSpan; -use Span; +use syntax_pos::{MultiSpan, Span}; #[must_use] +#[derive(Clone, Debug, PartialEq, Hash)] pub struct Diagnostic { - pub(crate) inner: Box, + pub level: Level, + pub message: Vec<(String, Style)>, + pub code: Option, + pub span: MultiSpan, + pub children: Vec, + pub suggestions: Vec, } -impl Diagnostic { - #[inline(always)] - pub fn new(level: Level, msg: &str) -> Self { - Self::new_with_code(level, None, msg) +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum DiagnosticId { + Error(String), + Lint(String), +} + +/// For example a note attached to an error. +#[derive(Clone, Debug, PartialEq, Hash)] +pub struct SubDiagnostic { + pub level: Level, + pub message: Vec<(String, Style)>, + pub span: MultiSpan, + pub render_span: Option, +} + +#[derive(PartialEq, Eq)] +pub struct DiagnosticStyledString(pub Vec); + +impl DiagnosticStyledString { + pub fn new() -> DiagnosticStyledString { + DiagnosticStyledString(vec![]) + } + pub fn push_normal>(&mut self, t: S) { + self.0.push(StringPart::Normal(t.into())); + } + pub fn push_highlighted>(&mut self, t: S) { + self.0.push(StringPart::Highlighted(t.into())); + } + pub fn normal>(t: S) -> DiagnosticStyledString { + DiagnosticStyledString(vec![StringPart::Normal(t.into())]) } - #[inline(always)] - pub fn new_with_code(level: Level, code: Option, msg: &str) -> Self { - Diagnostic { - inner: box RustcDiagnostic::new_with_code(level, code, msg), - } + pub fn highlighted>(t: S) -> DiagnosticStyledString { + DiagnosticStyledString(vec![StringPart::Highlighted(t.into())]) } - #[inline(always)] - pub fn new_note(msg: &str) -> Self { - Self::new(Level::Note, msg) + pub fn content(&self) -> String { + self.0.iter().map(|x| x.content()).collect::() } +} - #[inline(always)] - pub fn new_warn(msg: &str) -> Self { - Self::new(Level::Warning, msg) +#[derive(PartialEq, Eq)] +pub enum StringPart { + Normal(String), + Highlighted(String), +} + +impl StringPart { + pub fn content(&self) -> &str { + match self { + &StringPart::Normal(ref s) | &StringPart::Highlighted(ref s) => s, + } } +} - #[inline(always)] - pub fn new_error(msg: &str) -> Self { - Self::new(Level::Error, msg) +impl Diagnostic { + pub fn new(level: Level, message: &str) -> Self { + Diagnostic::new_with_code(level, None, message) } - #[inline(always)] - pub fn new_fatal(msg: &str) -> Self { - Self::new(Level::Fatal, msg) + pub fn new_with_code(level: Level, code: Option, message: &str) -> Self { + Diagnostic { + level, + message: vec![(message.to_owned(), Style::NoStyle)], + code, + span: MultiSpan::new(), + children: vec![], + suggestions: vec![], + } } - #[inline(always)] - pub fn emit_to(self, handler: &Handler) { - handler.emit(self) + pub fn is_error(&self) -> bool { + match self.level { + Level::Bug | Level::Fatal | Level::PhaseFatal | Level::Error | Level::FailureNote => { + true + } + + Level::Warning | Level::Note | Level::Help | Level::Cancelled => false, + } } /// Cancel the diagnostic (a structured diagnostic must either be emitted or /// canceled or it will panic when dropped). - #[inline(always)] - pub fn cancel(mut self) -> Self { - self.inner.cancel(); - self + pub fn cancel(&mut self) { + self.level = Level::Cancelled; } + pub fn cancelled(&self) -> bool { - self.inner.cancelled() + self.level == Level::Cancelled } /// Add a span/label to be included in the resulting snippet. @@ -65,75 +110,98 @@ impl Diagnostic { /// all, and you just supplied a `Span` to create the diagnostic, /// then the snippet will just include that `Span`, which is /// called the primary span. - #[inline(always)] - pub fn span_label>(mut self, span: Span, label: T) -> Self { - self.inner.span_label(span, label.into()); + pub fn span_label>(&mut self, span: Span, label: T) -> &mut Self { + self.span.push_span_label(span, label.into()); + self + } + + pub fn replace_span_with(&mut self, after: Span) -> &mut Self { + let before = self.span.clone(); + self.set_span(after); + for span_label in before.span_labels() { + if let Some(label) = span_label.label { + self.span_label(after, label); + } + } self } - #[inline(always)] pub fn note_expected_found( - mut self, - label: &fmt::Display, + &mut self, + label: &dyn fmt::Display, expected: DiagnosticStyledString, found: DiagnosticStyledString, - ) -> Self { - self.inner.note_expected_found(label, expected, found); - self + ) -> &mut Self { + self.note_expected_found_extra(label, expected, found, &"", &"") } - #[inline(always)] pub fn note_expected_found_extra( - mut self, - label: &fmt::Display, + &mut self, + label: &dyn fmt::Display, expected: DiagnosticStyledString, found: DiagnosticStyledString, - expected_extra: &fmt::Display, - found_extra: &fmt::Display, - ) -> Self { - self.inner - .note_expected_found_extra(label, expected, found, expected_extra, found_extra); + expected_extra: &dyn fmt::Display, + found_extra: &dyn fmt::Display, + ) -> &mut Self { + let mut msg: Vec<_> = vec![(format!("expected {} `", label), Style::NoStyle)]; + msg.extend(expected.0.iter().map(|x| match *x { + StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), + StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), + })); + msg.push((format!("`{}\n", expected_extra), Style::NoStyle)); + msg.push((format!(" found {} `", label), Style::NoStyle)); + msg.extend(found.0.iter().map(|x| match *x { + StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), + StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), + })); + msg.push((format!("`{}", found_extra), Style::NoStyle)); + + // For now, just attach these as notes + self.highlighted_note(msg); self } - #[inline(always)] - pub fn note(mut self, msg: &str) -> Self { - self.inner.note(msg); + pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self { + self.highlighted_note(vec![ + (format!("`{}` from trait: `", name), Style::NoStyle), + (signature, Style::Highlight), + ("`".to_string(), Style::NoStyle), + ]); self } - // pub fn highlighted_note(mut self, msg: Vec<(String, Style)>) -> Self { - // self.inner.highlighted_note(msg); - // self - // } + pub fn note(&mut self, msg: &str) -> &mut Self { + self.sub(Level::Note, msg, MultiSpan::new(), None); + self + } - #[inline(always)] - pub fn span_note>(mut self, sp: S, msg: &str) -> Self { - self.inner.span_note(sp, msg); + pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self { + self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None); self } - #[inline(always)] - pub fn warn(mut self, msg: &str) -> Self { - self.inner.warn(msg); + pub fn span_note>(&mut self, sp: S, msg: &str) -> &mut Self { + self.sub(Level::Note, msg, sp.into(), None); self } - #[inline(always)] - pub fn span_warn>(mut self, sp: S, msg: &str) -> Self { - self.inner.span_warn(sp, msg); + pub fn warn(&mut self, msg: &str) -> &mut Self { + self.sub(Level::Warning, msg, MultiSpan::new(), None); self } - #[inline(always)] - pub fn help(mut self, msg: &str) -> Self { - self.inner.help(msg); + pub fn span_warn>(&mut self, sp: S, msg: &str) -> &mut Self { + self.sub(Level::Warning, msg, sp.into(), None); self } - #[inline(always)] - pub fn span_help>(mut self, sp: S, msg: &str) -> Self { - self.inner.span_help(sp, msg); + pub fn help(&mut self, msg: &str) -> &mut Self { + self.sub(Level::Help, msg, MultiSpan::new(), None); + self + } + + pub fn span_help>(&mut self, sp: S, msg: &str) -> &mut Self { + self.sub(Level::Help, msg, sp.into(), None); self } @@ -142,9 +210,19 @@ impl Diagnostic { /// and not the text. /// /// See `CodeSuggestion` for more information. - #[inline(always)] - pub fn span_suggestion_short(mut self, sp: Span, msg: &str, suggestion: String) -> Self { - self.inner.span_suggestion_short(sp, msg, suggestion); + #[deprecated(note = "Use `span_suggestion_short_with_applicability`")] + pub fn span_suggestion_short(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitutions: vec![Substitution { + parts: vec![SubstitutionPart { + snippet: suggestion, + span: sp, + }], + }], + msg: msg.to_owned(), + show_code_when_inline: false, + applicability: Applicability::Unspecified, + }); self } @@ -162,46 +240,214 @@ impl Diagnostic { /// * should not contain any parts like "the following", "as shown" /// * may look like "to do xyz, use" or "to do xyz, use abc" /// * may contain a name of a function, variable or type, but not whole - /// expressions + /// expressions /// /// See `CodeSuggestion` for more information. - #[inline(always)] - pub fn span_suggestion(mut self, sp: Span, msg: &str, suggestion: String) -> Self { - self.inner.span_suggestion(sp, msg, suggestion); + #[deprecated(note = "Use `span_suggestion_with_applicability`")] + pub fn span_suggestion(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitutions: vec![Substitution { + parts: vec![SubstitutionPart { + snippet: suggestion, + span: sp, + }], + }], + msg: msg.to_owned(), + show_code_when_inline: true, + applicability: Applicability::Unspecified, + }); + self + } + + pub fn multipart_suggestion_with_applicability( + &mut self, + msg: &str, + suggestion: Vec<(Span, String)>, + applicability: Applicability, + ) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitutions: vec![Substitution { + parts: suggestion + .into_iter() + .map(|(span, snippet)| SubstitutionPart { snippet, span }) + .collect(), + }], + msg: msg.to_owned(), + show_code_when_inline: true, + applicability, + }); self } + #[deprecated(note = "Use `multipart_suggestion_with_applicability`")] + pub fn multipart_suggestion( + &mut self, + msg: &str, + suggestion: Vec<(Span, String)>, + ) -> &mut Self { + self.multipart_suggestion_with_applicability(msg, suggestion, Applicability::Unspecified) + } + /// Prints out a message with multiple suggested edits of the code. - #[inline(always)] - pub fn span_suggestions(mut self, sp: Span, msg: &str, suggestions: Vec) -> Self { - self.inner.span_suggestions(sp, msg, suggestions); + #[deprecated(note = "Use `span_suggestions_with_applicability`")] + pub fn span_suggestions(&mut self, sp: Span, msg: &str, suggestions: Vec) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitutions: suggestions + .into_iter() + .map(|snippet| Substitution { + parts: vec![SubstitutionPart { snippet, span: sp }], + }) + .collect(), + msg: msg.to_owned(), + show_code_when_inline: true, + applicability: Applicability::Unspecified, + }); + self + } + + /// This is a suggestion that may contain mistakes or fillers and should + /// be read and understood by a human. + pub fn span_suggestion_with_applicability( + &mut self, + sp: Span, + msg: &str, + suggestion: String, + applicability: Applicability, + ) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitutions: vec![Substitution { + parts: vec![SubstitutionPart { + snippet: suggestion, + span: sp, + }], + }], + msg: msg.to_owned(), + show_code_when_inline: true, + applicability, + }); + self + } + + pub fn span_suggestions_with_applicability( + &mut self, + sp: Span, + msg: &str, + suggestions: impl Iterator, + applicability: Applicability, + ) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitutions: suggestions + .map(|snippet| Substitution { + parts: vec![SubstitutionPart { snippet, span: sp }], + }) + .collect(), + msg: msg.to_owned(), + show_code_when_inline: true, + applicability, + }); + self + } + + pub fn span_suggestion_short_with_applicability( + &mut self, + sp: Span, + msg: &str, + suggestion: String, + applicability: Applicability, + ) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitutions: vec![Substitution { + parts: vec![SubstitutionPart { + snippet: suggestion, + span: sp, + }], + }], + msg: msg.to_owned(), + show_code_when_inline: false, + applicability, + }); self } - #[inline(always)] - pub fn span>(mut self, sp: S) -> Self { - self.inner.set_span(sp); + pub fn set_span>(&mut self, sp: S) -> &mut Self { + self.span = sp.into(); self } - #[inline(always)] - pub fn code(mut self, s: DiagnosticId) -> Self { - self.inner.code(s); + pub fn code(&mut self, s: DiagnosticId) -> &mut Self { + self.code = Some(s); self } - // /// Used by a lint. Copies over all details *but* the "main - // /// message". - // pub fn copy_details_not_message(mut self, from: &Diagnostic) { - // self.span = from.span.clone(); - // self.code = from.code.clone(); - // self.children.extend(from.children.iter().cloned()) - // } + pub fn get_code(&self) -> Option { + self.code.clone() + } + + pub fn message(&self) -> String { + self.message + .iter() + .map(|i| i.0.as_str()) + .collect::() + } + + pub fn styled_message(&self) -> &Vec<(String, Style)> { + &self.message + } + + /// Used by a lint. Copies over all details *but* the "main + /// message". + pub fn copy_details_not_message(&mut self, from: &Diagnostic) { + self.span = from.span.clone(); + self.code = from.code.clone(); + self.children.extend(from.children.iter().cloned()) + } + + /// Convenience function for internal use, clients should use one of the + /// public methods above. + pub fn sub( + &mut self, + level: Level, + message: &str, + span: MultiSpan, + render_span: Option, + ) { + let sub = SubDiagnostic { + level, + message: vec![(message.to_owned(), Style::NoStyle)], + span, + render_span, + }; + self.children.push(sub); + } + + /// Convenience function for internal use, clients should use one of the + /// public methods above. + fn sub_with_highlights( + &mut self, + level: Level, + message: Vec<(String, Style)>, + span: MultiSpan, + render_span: Option, + ) { + let sub = SubDiagnostic { + level, + message, + span, + render_span, + }; + self.children.push(sub); + } } -impl From for Diagnostic { - #[inline(always)] - fn from(inner: RustcDiagnostic) -> Self { - Diagnostic { inner: box inner } +impl SubDiagnostic { + pub fn message(&self) -> String { + self.message + .iter() + .map(|i| i.0.as_str()) + .collect::() + } + + pub fn styled_message(&self) -> &Vec<(String, Style)> { + &self.message } } diff --git a/common/src/errors/diagnostic_builder.rs b/common/src/errors/diagnostic_builder.rs index 01144ee8b96e..43c5863e0366 100644 --- a/common/src/errors/diagnostic_builder.rs +++ b/common/src/errors/diagnostic_builder.rs @@ -1,48 +1,139 @@ -use super::Handler; -use rustc_errors::{ - Diagnostic as RustcDiagnostic, DiagnosticBuilder as Builder, DiagnosticId, - DiagnosticStyledString, Level, +use super::{Applicability, Diagnostic, DiagnosticId, DiagnosticStyledString, Handler, Level}; +use std::{ + fmt::{self, Debug}, + ops::{Deref, DerefMut}, + thread::panicking, }; -use std::fmt; -use MultiSpan; -use Span; +use syntax_pos::{MultiSpan, Span}; +/// Used for emitting structured error messages and other diagnostic +/// information. +/// +/// If there is some state in a downstream crate you would like to +/// access in the methods of `DiagnosticBuilder` here, consider +/// extending `HandlerFlags`, accessed via `self.handler.flags`. #[must_use] +#[derive(Clone)] pub struct DiagnosticBuilder<'a> { - db: Box>, + pub handler: &'a Handler, + diagnostic: Diagnostic, + allow_suggestions: bool, } -impl<'a> DiagnosticBuilder<'a> { - pub fn new(handler: &'a Handler, level: Level, msg: &str) -> Self { - Self::new_with_code(handler, level, None, msg) +/// In general, the `DiagnosticBuilder` uses deref to allow access to +/// the fields and methods of the embedded `diagnostic` in a +/// transparent way. *However,* many of the methods are intended to +/// be used in a chained way, and hence ought to return `self`. In +/// that case, we can't just naively forward to the method on the +/// `diagnostic`, because the return type would be a `&Diagnostic` +/// instead of a `&DiagnosticBuilder<'a>`. This `forward!` macro makes +/// it easy to declare such methods on the builder. +macro_rules! forward { + // Forward pattern for &self -> &Self + (pub fn $n:ident(&self, $($name:ident: $ty:ty),* $(,)*) -> &Self) => { + pub fn $n(&self, $($name: $ty),*) -> &Self { + #[allow(deprecated)] + self.diagnostic.$n($($name),*); + self + } + }; + + // Forward pattern for &mut self -> &mut Self + (pub fn $n:ident(&mut self, $($name:ident: $ty:ty),* $(,)*) -> &mut Self) => { + pub fn $n(&mut self, $($name: $ty),*) -> &mut Self { + #[allow(deprecated)] + self.diagnostic.$n($($name),*); + self + } + }; + + // Forward pattern for &mut self -> &mut Self, with S: Into + // type parameter. No obvious way to make this more generic. + (pub fn $n:ident>( + &mut self, + $($name:ident: $ty:ty),* + $(,)*) -> &mut Self) => { + pub fn $n>(&mut self, $($name: $ty),*) -> &mut Self { + #[allow(deprecated)] + self.diagnostic.$n($($name),*); + self + } + }; +} + +impl<'a> Deref for DiagnosticBuilder<'a> { + type Target = Diagnostic; + + fn deref(&self) -> &Diagnostic { + &self.diagnostic } +} - pub fn new_with_code( - handler: &'a Handler, - level: Level, - code: Option, - msg: &str, - ) -> Self { - DiagnosticBuilder { - db: box Builder::new_diagnostic( - &handler.inner, - RustcDiagnostic::new_with_code(level, code, msg), - ), +impl<'a> DerefMut for DiagnosticBuilder<'a> { + fn deref_mut(&mut self) -> &mut Diagnostic { + &mut self.diagnostic + } +} + +impl<'a> DiagnosticBuilder<'a> { + /// Emit the diagnostic. + pub fn emit(&mut self) { + if self.cancelled() { + return; } + + self.handler.emit_db(&self); + self.cancel(); } - pub fn emit(mut self) { - self.db.emit() + /// Buffers the diagnostic for later emission, unless handler + /// has disabled such buffering. + pub fn buffer(mut self, buffered_diagnostics: &mut Vec) { + if self.handler.flags.dont_buffer_diagnostics || self.handler.flags.treat_err_as_bug { + self.emit(); + return; + } + + // We need to use `ptr::read` because `DiagnosticBuilder` + // implements `Drop`. + let diagnostic; + unsafe { + diagnostic = ::std::ptr::read(&self.diagnostic); + ::std::mem::forget(self); + }; + // Logging here is useful to help track down where in logs an error was + // actually emitted. + debug!("buffer: diagnostic={:?}", diagnostic); + buffered_diagnostics.push(diagnostic); } - /// Cancel the diagnostic (a structured diagnostic must either be emitted or - /// canceled or it will panic when dropped). - pub fn cancel(mut self) -> Self { - self.db.cancel(); + /// Convenience function for internal use, clients should use one of the + /// span_* methods instead. + pub fn sub>( + &mut self, + level: Level, + message: &str, + span: Option, + ) -> &mut Self { + let span = span.map(|s| s.into()).unwrap_or_else(|| MultiSpan::new()); + self.diagnostic.sub(level, message, span, None); self } - pub fn cancelled(&self) -> bool { - self.db.cancelled() + + /// Delay emission of this diagnostic as a bug. + /// + /// This can be useful in contexts where an error indicates a bug but + /// typically this only happens when other compilation errors have already + /// happened. In those cases this can be used to defer emission of this + /// diagnostic as a bug in the compiler only if no other errors have been + /// emitted. + /// + /// In the meantime, though, callsites are required to deal with the "bug" + /// locally in whichever way makes the most sense. + pub fn delay_as_bug(&mut self) { + self.level = Level::Bug; + self.handler.delay_as_bug(self.diagnostic.clone()); + self.cancel(); } /// Add a span/label to be included in the resulting snippet. @@ -51,129 +142,184 @@ impl<'a> DiagnosticBuilder<'a> { /// all, and you just supplied a `Span` to create the diagnostic, /// then the snippet will just include that `Span`, which is /// called the primary span. - pub fn span_label>(mut self, span: Span, label: T) -> Self { - self.db.span_label(span, label.into()); + pub fn span_label>(&mut self, span: Span, label: T) -> &mut Self { + self.diagnostic.span_label(span, label); self } - pub fn note_expected_found( - mut self, - label: &fmt::Display, - expected: DiagnosticStyledString, - found: DiagnosticStyledString, - ) -> Self { - self.db.note_expected_found(label, expected, found); - self - } + forward!(pub fn note_expected_found(&mut self, + label: &dyn fmt::Display, + expected: DiagnosticStyledString, + found: DiagnosticStyledString, + ) -> &mut Self); - pub fn note_expected_found_extra( - mut self, - label: &fmt::Display, - expected: DiagnosticStyledString, - found: DiagnosticStyledString, - expected_extra: &fmt::Display, - found_extra: &fmt::Display, - ) -> Self { - self.db - .note_expected_found_extra(label, expected, found, expected_extra, found_extra); - self - } + forward!(pub fn note_expected_found_extra(&mut self, + label: &dyn fmt::Display, + expected: DiagnosticStyledString, + found: DiagnosticStyledString, + expected_extra: &dyn fmt::Display, + found_extra: &dyn fmt::Display, + ) -> &mut Self); - pub fn note(mut self, msg: &str) -> Self { - self.db.note(msg); - self - } + forward!(pub fn note(&mut self, msg: &str) -> &mut Self); + forward!(pub fn span_note>(&mut self, + sp: S, + msg: &str, + ) -> &mut Self); + forward!(pub fn warn(&mut self, msg: &str) -> &mut Self); + forward!(pub fn span_warn>(&mut self, sp: S, msg: &str) -> &mut Self); + forward!(pub fn help(&mut self , msg: &str) -> &mut Self); + forward!(pub fn span_help>(&mut self, + sp: S, + msg: &str, + ) -> &mut Self); - // pub fn highlighted_note(mut self, msg: Vec<(String, Style)>) -> Self { - // self.db.highlighted_note(msg); - // self - // } + #[deprecated(note = "Use `span_suggestion_short_with_applicability`")] + forward!(pub fn span_suggestion_short( + &mut self, + sp: Span, + msg: &str, + suggestion: String, + ) -> &mut Self); - pub fn span_note>(mut self, sp: S, msg: &str) -> Self { - self.db.span_note(sp, msg); - self - } + #[deprecated(note = "Use `multipart_suggestion_with_applicability`")] + forward!(pub fn multipart_suggestion( + &mut self, + msg: &str, + suggestion: Vec<(Span, String)>, + ) -> &mut Self); - pub fn warn(mut self, msg: &str) -> Self { - self.db.warn(msg); - self - } + #[deprecated(note = "Use `span_suggestion_with_applicability`")] + forward!(pub fn span_suggestion(&mut self, + sp: Span, + msg: &str, + suggestion: String, + ) -> &mut Self); + + #[deprecated(note = "Use `span_suggestions_with_applicability`")] + forward!(pub fn span_suggestions(&mut self, + sp: Span, + msg: &str, + suggestions: Vec, + ) -> &mut Self); - pub fn span_warn>(mut self, sp: S, msg: &str) -> Self { - self.db.span_warn(sp, msg); + pub fn multipart_suggestion_with_applicability( + &mut self, + msg: &str, + suggestion: Vec<(Span, String)>, + applicability: Applicability, + ) -> &mut Self { + if !self.allow_suggestions { + return self; + } + self.diagnostic + .multipart_suggestion_with_applicability(msg, suggestion, applicability); self } - pub fn help(mut self, msg: &str) -> Self { - self.db.help(msg); + pub fn span_suggestion_with_applicability( + &mut self, + sp: Span, + msg: &str, + suggestion: String, + applicability: Applicability, + ) -> &mut Self { + if !self.allow_suggestions { + return self; + } + self.diagnostic + .span_suggestion_with_applicability(sp, msg, suggestion, applicability); self } - pub fn span_help>(mut self, sp: S, msg: &str) -> Self { - self.db.span_help(sp, msg); + pub fn span_suggestions_with_applicability( + &mut self, + sp: Span, + msg: &str, + suggestions: impl Iterator, + applicability: Applicability, + ) -> &mut Self { + if !self.allow_suggestions { + return self; + } + self.diagnostic + .span_suggestions_with_applicability(sp, msg, suggestions, applicability); self } - /// Prints out a message with a suggested edit of the code. If the - /// suggestion is presented inline it will only show the text message - /// and not the text. - /// - /// See `CodeSuggestion` for more information. - pub fn span_suggestion_short(mut self, sp: Span, msg: &str, suggestion: String) -> Self { - self.db.span_suggestion_short(sp, msg, suggestion); + pub fn span_suggestion_short_with_applicability( + &mut self, + sp: Span, + msg: &str, + suggestion: String, + applicability: Applicability, + ) -> &mut Self { + if !self.allow_suggestions { + return self; + } + self.diagnostic.span_suggestion_short_with_applicability( + sp, + msg, + suggestion, + applicability, + ); self } + forward!(pub fn set_span>(&mut self, sp: S) -> &mut Self); + forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self); - /// Prints out a message with a suggested edit of the code. - /// - /// In case of short messages and a simple suggestion, - /// rustc displays it as a label like - /// - /// "try adding parentheses: `(tup.0).1`" - /// - /// The message - /// - /// * should not end in any punctuation (a `:` is added automatically) - /// * should not be a question - /// * should not contain any parts like "the following", "as shown" - /// * may look like "to do xyz, use" or "to do xyz, use abc" - /// * may contain a name of a function, variable or type, but not whole - /// expressions - /// - /// See `CodeSuggestion` for more information. - pub fn span_suggestion(mut self, sp: Span, msg: &str, suggestion: String) -> Self { - self.db.span_suggestion(sp, msg, suggestion); + pub fn allow_suggestions(&mut self, allow: bool) -> &mut Self { + self.allow_suggestions = allow; self } - /// Prints out a message with multiple suggested edits of the code. - pub fn span_suggestions(mut self, sp: Span, msg: &str, suggestions: Vec) -> Self { - self.db.span_suggestions(sp, msg, suggestions); - self + /// Convenience function for internal use, clients should use one of the + /// struct_* methods on Handler. + pub fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> { + DiagnosticBuilder::new_with_code(handler, level, None, message) } - pub fn span>(mut self, sp: S) -> Self { - self.db.set_span(sp); - self + /// Convenience function for internal use, clients should use one of the + /// struct_* methods on Handler. + pub fn new_with_code( + handler: &'a Handler, + level: Level, + code: Option, + message: &str, + ) -> DiagnosticBuilder<'a> { + let diagnostic = Diagnostic::new_with_code(level, code, message); + DiagnosticBuilder::new_diagnostic(handler, diagnostic) } - pub fn code(mut self, s: DiagnosticId) -> Self { - self.db.code(s); - self + /// Creates a new `DiagnosticBuilder` with an already constructed + /// diagnostic. + pub fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> { + DiagnosticBuilder { + handler, + diagnostic, + allow_suggestions: true, + } } +} - // /// Used by a lint. Copies over all details *but* the "main - // /// message". - // pub fn copy_details_not_message(mut self, from: &Diagnostic) { - // self.span = from.span.clone(); - // self.code = from.code.clone(); - // self.children.extend(from.children.iter().cloned()) - // } +impl<'a> Debug for DiagnosticBuilder<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.diagnostic.fmt(f) + } } -impl<'a> From> for DiagnosticBuilder<'a> { - #[inline(always)] - fn from(db: Builder<'a>) -> Self { - DiagnosticBuilder { db: box db } +/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or canceled +/// or we emit a bug. +impl<'a> Drop for DiagnosticBuilder<'a> { + fn drop(&mut self) { + if !panicking() && !self.cancelled() { + let mut db = DiagnosticBuilder::new( + self.handler, + Level::Bug, + "Error constructed but not emitted", + ); + db.emit(); + panic!(); + } } } diff --git a/common/src/errors/emitter.rs b/common/src/errors/emitter.rs new file mode 100644 index 000000000000..46b30398062e --- /dev/null +++ b/common/src/errors/emitter.rs @@ -0,0 +1,1618 @@ +use self::Destination::*; +use super::{ + snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString}, + styled_buffer::StyledBuffer, + CodeSuggestion, DiagnosticBuilder, DiagnosticId, Level, SourceMapperDyn, SubDiagnostic, +}; +use crate::sync::Lrc; +use atty; +use fxhash::FxHashMap; +use std::{ + borrow::Cow, + cmp::{min, Reverse}, + io::{self, prelude::*}, + ops::Range, +}; +use syntax_pos::{MultiSpan, SourceFile, Span}; +use termcolor::{Buffer, BufferWriter, Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; +use unicode_width; + +const ANONYMIZED_LINE_NUM: &str = "LL"; + +/// Emitter trait for emitting errors. +pub trait Emitter { + /// Emit a structured diagnostic. + fn emit(&mut self, db: &DiagnosticBuilder); + + /// Check if should show explanations about "rustc --explain" + fn should_show_explain(&self) -> bool { + true + } +} + +impl Emitter for EmitterWriter { + fn emit(&mut self, db: &DiagnosticBuilder) { + let mut primary_span = db.span.clone(); + let mut children = db.children.clone(); + let mut suggestions: &[_] = &[]; + + if let Some((sugg, rest)) = db.suggestions.split_first() { + if rest.is_empty() && + // don't display multi-suggestions as labels + sugg.substitutions.len() == 1 && + // don't display multipart suggestions as labels + sugg.substitutions[0].parts.len() == 1 && + // don't display long messages as labels + sugg.msg.split_whitespace().count() < 10 && + // don't display multiline suggestions as labels + !sugg.substitutions[0].parts[0].snippet.contains('\n') + { + let substitution = &sugg.substitutions[0].parts[0].snippet.trim(); + let msg = if substitution.len() == 0 || !sugg.show_code_when_inline { + // This substitution is only removal or we explicitly don't want to show the + // code inline, don't show it + format!("help: {}", sugg.msg) + } else { + format!("help: {}: `{}`", sugg.msg, substitution) + }; + primary_span.push_span_label(sugg.substitutions[0].parts[0].span, msg); + } else { + // if there are multiple suggestions, print them all in full + // to be consistent. We could try to figure out if we can + // make one (or the first one) inline, but that would give + // undue importance to a semi-random suggestion + suggestions = &db.suggestions; + } + } + + self.fix_multispans_in_std_macros( + &mut primary_span, + &mut children, + db.handler.flags.external_macro_backtrace, + ); + + self.emit_messages_default( + &db.level, + &db.styled_message(), + &db.code, + &primary_span, + &children, + &suggestions, + ); + } + + fn should_show_explain(&self) -> bool { + !self.short_message + } +} + +/// maximum number of lines we will print for each error; arbitrary. +pub const MAX_HIGHLIGHT_LINES: usize = 6; +/// maximum number of suggestions to be shown +/// +/// Arbitrary, but taken from trait import suggestion limit +pub const MAX_SUGGESTIONS: usize = 4; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ColorConfig { + Auto, + Always, + Never, +} + +impl ColorConfig { + fn to_color_choice(&self) -> ColorChoice { + match *self { + ColorConfig::Always => { + if atty::is(atty::Stream::Stderr) { + ColorChoice::Always + } else { + ColorChoice::AlwaysAnsi + } + } + ColorConfig::Never => ColorChoice::Never, + ColorConfig::Auto if atty::is(atty::Stream::Stderr) => ColorChoice::Auto, + ColorConfig::Auto => ColorChoice::Never, + } + } +} + +pub struct EmitterWriter { + dst: Destination, + sm: Option>, + short_message: bool, + teach: bool, + ui_testing: bool, +} + +struct FileWithAnnotatedLines { + file: Lrc, + lines: Vec, + multiline_depth: usize, +} + +impl EmitterWriter { + pub fn stderr( + color_config: ColorConfig, + source_map: Option>, + short_message: bool, + teach: bool, + ) -> EmitterWriter { + let dst = Destination::from_stderr(color_config); + EmitterWriter { + dst, + sm: source_map, + short_message, + teach, + ui_testing: false, + } + } + + pub fn new( + dst: Box, + source_map: Option>, + short_message: bool, + teach: bool, + ) -> EmitterWriter { + EmitterWriter { + dst: Raw(dst), + sm: source_map, + short_message, + teach, + ui_testing: false, + } + } + + pub fn ui_testing(mut self, ui_testing: bool) -> Self { + self.ui_testing = ui_testing; + self + } + + fn maybe_anonymized(&self, line_num: usize) -> String { + if self.ui_testing { + ANONYMIZED_LINE_NUM.to_string() + } else { + line_num.to_string() + } + } + + fn preprocess_annotations(&mut self, msp: &MultiSpan) -> Vec { + fn add_annotation_to_file( + file_vec: &mut Vec, + file: Lrc, + line_index: usize, + ann: Annotation, + ) { + for slot in file_vec.iter_mut() { + // Look through each of our files for the one we're adding to + if slot.file.name == file.name { + // See if we already have a line for it + for line_slot in &mut slot.lines { + if line_slot.line_index == line_index { + line_slot.annotations.push(ann); + return; + } + } + // We don't have a line yet, create one + slot.lines.push(Line { + line_index, + annotations: vec![ann], + }); + slot.lines.sort(); + return; + } + } + // This is the first time we're seeing the file + file_vec.push(FileWithAnnotatedLines { + file, + lines: vec![Line { + line_index, + annotations: vec![ann], + }], + multiline_depth: 0, + }); + } + + let mut output = vec![]; + let mut multiline_annotations = vec![]; + + if let Some(ref sm) = self.sm { + for span_label in msp.span_labels() { + if span_label.span.is_dummy() { + continue; + } + + let lo = sm.lookup_char_pos(span_label.span.lo()); + let mut hi = sm.lookup_char_pos(span_label.span.hi()); + + // Watch out for "empty spans". If we get a span like 6..6, we + // want to just display a `^` at 6, so convert that to + // 6..7. This is degenerate input, but it's best to degrade + // gracefully -- and the parser likes to supply a span like + // that for EOF, in particular. + if lo.col_display == hi.col_display && lo.line == hi.line { + hi.col_display += 1; + } + + let ann_type = if lo.line != hi.line { + let ml = MultilineAnnotation { + depth: 1, + line_start: lo.line, + line_end: hi.line, + start_col: lo.col_display, + end_col: hi.col_display, + is_primary: span_label.is_primary, + label: span_label.label.clone(), + }; + multiline_annotations.push((lo.file.clone(), ml.clone())); + AnnotationType::Multiline(ml) + } else { + AnnotationType::Singleline + }; + let ann = Annotation { + start_col: lo.col_display, + end_col: hi.col_display, + is_primary: span_label.is_primary, + label: span_label.label.clone(), + annotation_type: ann_type, + }; + + if !ann.is_multiline() { + add_annotation_to_file(&mut output, lo.file, lo.line, ann); + } + } + } + + // Find overlapping multiline annotations, put them at different depths + multiline_annotations.sort_by_key(|&(_, ref ml)| (ml.line_start, ml.line_end)); + for item in multiline_annotations.clone() { + let ann = item.1; + for item in multiline_annotations.iter_mut() { + let ref mut a = item.1; + // Move all other multiline annotations overlapping with this one + // one level to the right. + if &ann != a + && num_overlap(ann.line_start, ann.line_end, a.line_start, a.line_end, true) + { + a.increase_depth(); + } else { + break; + } + } + } + + let mut max_depth = 0; // max overlapping multiline spans + for (file, ann) in multiline_annotations { + if ann.depth > max_depth { + max_depth = ann.depth; + } + add_annotation_to_file(&mut output, file.clone(), ann.line_start, ann.as_start()); + let middle = min(ann.line_start + 4, ann.line_end); + for line in ann.line_start + 1..middle { + add_annotation_to_file(&mut output, file.clone(), line, ann.as_line()); + } + if middle < ann.line_end - 1 { + for line in ann.line_end - 1..ann.line_end { + add_annotation_to_file(&mut output, file.clone(), line, ann.as_line()); + } + } + add_annotation_to_file(&mut output, file, ann.line_end, ann.as_end()); + } + for file_vec in output.iter_mut() { + file_vec.multiline_depth = max_depth; + } + output + } + + fn render_source_line( + &self, + buffer: &mut StyledBuffer, + file: Lrc, + line: &Line, + width_offset: usize, + code_offset: usize, + ) -> Vec<(usize, Style)> { + if line.line_index == 0 { + return Vec::new(); + } + + let source_string = match file.get_line(line.line_index - 1) { + Some(s) => s, + None => return Vec::new(), + }; + + let line_offset = buffer.num_lines(); + + // First create the source line we will highlight. + buffer.puts(line_offset, code_offset, &source_string, Style::Quotation); + buffer.puts( + line_offset, + 0, + &self.maybe_anonymized(line.line_index), + Style::LineNumber, + ); + + draw_col_separator(buffer, line_offset, width_offset - 2); + + // Special case when there's only one annotation involved, it is the start of a + // multiline span and there's no text at the beginning of the code line. + // Instead of doing the whole graph: + // + // 2 | fn foo() { + // | _^ + // 3 | | + // 4 | | } + // | |_^ test + // + // we simplify the output to: + // + // 2 | / fn foo() { + // 3 | | + // 4 | | } + // | |_^ test + if line.annotations.len() == 1 { + if let Some(ref ann) = line.annotations.get(0) { + if let AnnotationType::MultilineStart(depth) = ann.annotation_type { + if source_string + .chars() + .take(ann.start_col) + .all(|c| c.is_whitespace()) + { + let style = if ann.is_primary { + Style::UnderlinePrimary + } else { + Style::UnderlineSecondary + }; + buffer.putc(line_offset, width_offset + depth - 1, '/', style); + return vec![(depth, style)]; + } + } + } + } + + // We want to display like this: + // + // vec.push(vec.pop().unwrap()); + // --- ^^^ - previous borrow ends here + // | | + // | error occurs here + // previous borrow of `vec` occurs here + // + // But there are some weird edge cases to be aware of: + // + // vec.push(vec.pop().unwrap()); + // -------- - previous borrow ends here + // || + // |this makes no sense + // previous borrow of `vec` occurs here + // + // For this reason, we group the lines into "highlight lines" + // and "annotations lines", where the highlight lines have the `^`. + + // Sort the annotations by (start, end col) + // The labels are reversed, sort and then reversed again. + // Consider a list of annotations (A1, A2, C1, C2, B1, B2) where + // the letter signifies the span. Here we are only sorting by the + // span and hence, the order of the elements with the same span will + // not change. On reversing the ordering (|a, b| but b.cmp(a)), you get + // (C1, C2, B1, B2, A1, A2). All the elements with the same span are + // still ordered first to last, but all the elements with different + // spans are ordered by their spans in last to first order. Last to + // first order is important, because the jiggly lines and | are on + // the left, so the rightmost span needs to be rendered first, + // otherwise the lines would end up needing to go over a message. + + let mut annotations = line.annotations.clone(); + annotations.sort_by_key(|a| Reverse(a.start_col)); + + // First, figure out where each label will be positioned. + // + // In the case where you have the following annotations: + // + // vec.push(vec.pop().unwrap()); + // -------- - previous borrow ends here [C] + // || + // |this makes no sense [B] + // previous borrow of `vec` occurs here [A] + // + // `annotations_position` will hold [(2, A), (1, B), (0, C)]. + // + // We try, when possible, to stick the rightmost annotation at the end + // of the highlight line: + // + // vec.push(vec.pop().unwrap()); + // --- --- - previous borrow ends here + // + // But sometimes that's not possible because one of the other + // annotations overlaps it. For example, from the test + // `span_overlap_label`, we have the following annotations + // (written on distinct lines for clarity): + // + // fn foo(x: u32) { + // -------------- + // - + // + // In this case, we can't stick the rightmost-most label on + // the highlight line, or we would get: + // + // fn foo(x: u32) { + // -------- x_span + // | + // fn_span + // + // which is totally weird. Instead we want: + // + // fn foo(x: u32) { + // -------------- + // | | + // | x_span + // fn_span + // + // which is...less weird, at least. In fact, in general, if + // the rightmost span overlaps with any other span, we should + // use the "hang below" version, so we can at least make it + // clear where the span *starts*. There's an exception for this + // logic, when the labels do not have a message: + // + // fn foo(x: u32) { + // -------------- + // | + // x_span + // + // instead of: + // + // fn foo(x: u32) { + // -------------- + // | | + // | x_span + // + // + let mut annotations_position = vec![]; + let mut line_len = 0; + let mut p = 0; + for (i, annotation) in annotations.iter().enumerate() { + for (j, next) in annotations.iter().enumerate() { + if overlaps(next, annotation, 0) // This label overlaps with another one and both + && annotation.has_label() // take space (they have text and are not + && j > i // multiline lines). + && p == 0 + // We're currently on the first line, move the label one line down + { + // This annotation needs a new line in the output. + p += 1; + break; + } + } + annotations_position.push((p, annotation)); + for (j, next) in annotations.iter().enumerate() { + if j > i { + let l = if let Some(ref label) = next.label { + label.len() + 2 + } else { + 0 + }; + if (overlaps(next, annotation, l) // Do not allow two labels to be in the same + // line if they overlap including padding, to + // avoid situations like: + // + // fn foo(x: u32) { + // -------^------ + // | | + // fn_spanx_span + // + && annotation.has_label() // Both labels must have some text, otherwise + && next.has_label()) // they are not overlapping. + // Do not add a new line if this annotation + // or the next are vertical line placeholders. + || (annotation.takes_space() // If either this or the next annotation is + && next.has_label()) // multiline start/end, move it to a new line + || (annotation.has_label() // so as not to overlap the orizontal lines. + && next.takes_space()) + || (annotation.takes_space() && next.takes_space()) + || (overlaps(next, annotation, l) + && next.end_col <= annotation.end_col + && next.has_label() + && p == 0) + // Avoid #42595. + { + // This annotation needs a new line in the output. + p += 1; + break; + } + } + } + if line_len < p { + line_len = p; + } + } + + if line_len != 0 { + line_len += 1; + } + + // If there are no annotations or the only annotations on this line are + // MultilineLine, then there's only code being shown, stop processing. + if line.annotations.iter().all(|a| a.is_line()) { + return vec![]; + } + + // Write the colunmn separator. + // + // After this we will have: + // + // 2 | fn foo() { + // | + // | + // | + // 3 | + // 4 | } + // | + for pos in 0..=line_len { + draw_col_separator(buffer, line_offset + pos + 1, width_offset - 2); + buffer.putc( + line_offset + pos + 1, + width_offset - 2, + '|', + Style::LineNumber, + ); + } + + // Write the horizontal lines for multiline annotations + // (only the first and last lines need this). + // + // After this we will have: + // + // 2 | fn foo() { + // | __________ + // | + // | + // 3 | + // 4 | } + // | _ + for &(pos, annotation) in &annotations_position { + let style = if annotation.is_primary { + Style::UnderlinePrimary + } else { + Style::UnderlineSecondary + }; + let pos = pos + 1; + match annotation.annotation_type { + AnnotationType::MultilineStart(depth) | AnnotationType::MultilineEnd(depth) => { + draw_range( + buffer, + '_', + line_offset + pos, + width_offset + depth, + code_offset + annotation.start_col, + style, + ); + } + _ if self.teach => { + buffer.set_style_range( + line_offset, + code_offset + annotation.start_col, + code_offset + annotation.end_col, + style, + annotation.is_primary, + ); + } + _ => {} + } + } + + // Write the vertical lines for labels that are on a different line as the + // underline. + // + // After this we will have: + // + // 2 | fn foo() { + // | __________ + // | | | + // | | + // 3 | + // 4 | | } + // | |_ + for &(pos, annotation) in &annotations_position { + let style = if annotation.is_primary { + Style::UnderlinePrimary + } else { + Style::UnderlineSecondary + }; + let pos = pos + 1; + + if pos > 1 && (annotation.has_label() || annotation.takes_space()) { + for p in line_offset + 1..=line_offset + pos { + buffer.putc(p, code_offset + annotation.start_col, '|', style); + } + } + match annotation.annotation_type { + AnnotationType::MultilineStart(depth) => { + for p in line_offset + pos + 1..line_offset + line_len + 2 { + buffer.putc(p, width_offset + depth - 1, '|', style); + } + } + AnnotationType::MultilineEnd(depth) => { + for p in line_offset..=line_offset + pos { + buffer.putc(p, width_offset + depth - 1, '|', style); + } + } + _ => (), + } + } + + // Write the labels on the annotations that actually have a label. + // + // After this we will have: + // + // 2 | fn foo() { + // | __________ + // | | + // | something about `foo` + // 3 | + // 4 | } + // | _ test + for &(pos, annotation) in &annotations_position { + let style = if annotation.is_primary { + Style::LabelPrimary + } else { + Style::LabelSecondary + }; + let (pos, col) = if pos == 0 { + (pos + 1, annotation.end_col + 1) + } else { + (pos + 2, annotation.start_col) + }; + if let Some(ref label) = annotation.label { + buffer.puts(line_offset + pos, code_offset + col, &label, style); + } + } + + // Sort from biggest span to smallest span so that smaller spans are + // represented in the output: + // + // x | fn foo() + // | ^^^---^^ + // | | | + // | | something about `foo` + // | something about `fn foo()` + annotations_position.sort_by(|a, b| { + // Decreasing order + a.1.len().cmp(&b.1.len()).reverse() + }); + + // Write the underlines. + // + // After this we will have: + // + // 2 | fn foo() { + // | ____-_____^ + // | | + // | something about `foo` + // 3 | + // 4 | } + // | _^ test + for &(_, annotation) in &annotations_position { + let (underline, style) = if annotation.is_primary { + ('^', Style::UnderlinePrimary) + } else { + ('-', Style::UnderlineSecondary) + }; + for p in annotation.start_col..annotation.end_col { + buffer.putc(line_offset + 1, code_offset + p, underline, style); + } + } + annotations_position + .iter() + .filter_map(|&(_, annotation)| match annotation.annotation_type { + AnnotationType::MultilineStart(p) | AnnotationType::MultilineEnd(p) => { + let style = if annotation.is_primary { + Style::LabelPrimary + } else { + Style::LabelSecondary + }; + Some((p, style)) + } + _ => None, + }) + .collect::>() + } + + fn get_multispan_max_line_num(&mut self, msp: &MultiSpan) -> usize { + let mut max = 0; + if let Some(ref sm) = self.sm { + for primary_span in msp.primary_spans() { + if !primary_span.is_dummy() { + let hi = sm.lookup_char_pos(primary_span.hi()); + if hi.line > max { + max = hi.line; + } + } + } + if !self.short_message { + for span_label in msp.span_labels() { + if !span_label.span.is_dummy() { + let hi = sm.lookup_char_pos(span_label.span.hi()); + if hi.line > max { + max = hi.line; + } + } + } + } + } + max + } + + fn get_max_line_num(&mut self, span: &MultiSpan, children: &[SubDiagnostic]) -> usize { + let mut max = 0; + + let primary = self.get_multispan_max_line_num(span); + max = if primary > max { primary } else { max }; + + for sub in children { + let sub_result = self.get_multispan_max_line_num(&sub.span); + max = if sub_result > max { primary } else { max }; + } + max + } + + // This "fixes" MultiSpans that contain Spans that are pointing to locations + // inside of <*macros>. Since these locations are often difficult to read, + // we move these Spans from <*macros> to their corresponding use site. + fn fix_multispan_in_std_macros( + &mut self, + span: &mut MultiSpan, + always_backtrace: bool, + ) -> bool { + let mut spans_updated = false; + + if let Some(ref sm) = self.sm { + let mut before_after: Vec<(Span, Span)> = vec![]; + let mut new_labels: Vec<(Span, String)> = vec![]; + + // First, find all the spans in <*macros> and point instead at their use site + for sp in span.primary_spans() { + if sp.is_dummy() { + continue; + } + let call_sp = sm.call_span_if_macro(*sp); + if call_sp != *sp && !always_backtrace { + before_after.push((*sp, call_sp)); + } + } + for (label_span, label_text) in new_labels { + span.push_span_label(label_span, label_text); + } + for sp_label in span.span_labels() { + if sp_label.span.is_dummy() { + continue; + } + // if sm.span_to_filename(sp_label.span.clone()).is_macros() && + // !always_backtrace { let v = + // sp_label.span.macro_backtrace(); if let Some(use_site) = + // v.last() { before_after.push((sp_label.span.clone(), + // use_site.call_site.clone())); } + // } + } + // After we have them, make sure we replace these 'bad' def sites with their use + // sites + for (before, after) in before_after { + span.replace(before, after); + spans_updated = true; + } + } + + spans_updated + } + + // This does a small "fix" for multispans by looking to see if it can find any + // that point directly at <*macros>. Since these are often difficult to + // read, this will change the span to point at the use site. + fn fix_multispans_in_std_macros( + &mut self, + span: &mut MultiSpan, + children: &mut Vec, + backtrace: bool, + ) { + let mut spans_updated = self.fix_multispan_in_std_macros(span, backtrace); + for child in children.iter_mut() { + spans_updated |= self.fix_multispan_in_std_macros(&mut child.span, backtrace); + } + if spans_updated { + children.push(SubDiagnostic { + level: Level::Note, + message: vec![( + "this error originates in a macro outside of the current crate (in Nightly \ + builds, run with -Z external-macro-backtrace for more info)" + .to_string(), + Style::NoStyle, + )], + span: MultiSpan::new(), + render_span: None, + }); + } + } + + /// Add a left margin to every line but the first, given a padding length + /// and the label being displayed, keeping the provided highlighting. + fn msg_to_buffer( + &self, + buffer: &mut StyledBuffer, + msg: &[(String, Style)], + padding: usize, + label: &str, + override_style: Option