about summary refs log tree commit diff
path: root/src/librustc_errors
diff options
context:
space:
mode:
authorDjzin <djzin@users.noreply.github.com>2017-05-27 14:31:47 +0100
committerDjzin <djzin@users.noreply.github.com>2017-05-27 14:31:47 +0100
commit74751358e625878306aa193fed788e79aa53d4fa (patch)
tree1ba9b336d1ddb45d9f688d69f5bd4ede028db622 /src/librustc_errors
parentc6307a2fa55c3d62c06b85b349257a8194093442 (diff)
parent3e7908f616745573a11ad7dfad245f12be0069da (diff)
downloadrust-74751358e625878306aa193fed788e79aa53d4fa.tar.gz
rust-74751358e625878306aa193fed788e79aa53d4fa.zip
Merge remote-tracking branch 'upstream/master' into fast-swap
Diffstat (limited to 'src/librustc_errors')
-rw-r--r--src/librustc_errors/diagnostic.rs114
-rw-r--r--src/librustc_errors/diagnostic_builder.rs40
-rw-r--r--src/librustc_errors/emitter.rs401
-rw-r--r--src/librustc_errors/lib.rs140
-rw-r--r--src/librustc_errors/snippet.rs35
5 files changed, 480 insertions, 250 deletions
diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs
index 1b77ead92de..861880aa265 100644
--- a/src/librustc_errors/diagnostic.rs
+++ b/src/librustc_errors/diagnostic.rs
@@ -9,9 +9,9 @@
 // except according to those terms.
 
 use CodeSuggestion;
+use Substitution;
 use Level;
 use RenderSpan;
-use RenderSpan::Suggestion;
 use std::fmt;
 use syntax_pos::{MultiSpan, Span};
 use snippet::Style;
@@ -24,6 +24,7 @@ pub struct Diagnostic {
     pub code: Option<String>,
     pub span: MultiSpan,
     pub children: Vec<SubDiagnostic>,
+    pub suggestions: Vec<CodeSuggestion>,
 }
 
 /// For example a note attached to an error.
@@ -35,6 +36,46 @@ pub struct SubDiagnostic {
     pub render_span: Option<RenderSpan>,
 }
 
+#[derive(PartialEq, Eq)]
+pub struct DiagnosticStyledString(pub Vec<StringPart>);
+
+impl DiagnosticStyledString {
+    pub fn new() -> DiagnosticStyledString {
+        DiagnosticStyledString(vec![])
+    }
+    pub fn push_normal<S: Into<String>>(&mut self, t: S) {
+        self.0.push(StringPart::Normal(t.into()));
+    }
+    pub fn push_highlighted<S: Into<String>>(&mut self, t: S) {
+        self.0.push(StringPart::Highlighted(t.into()));
+    }
+    pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString {
+        DiagnosticStyledString(vec![StringPart::Normal(t.into())])
+    }
+
+    pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString {
+        DiagnosticStyledString(vec![StringPart::Highlighted(t.into())])
+    }
+
+    pub fn content(&self) -> String {
+        self.0.iter().map(|x| x.content()).collect::<String>()
+    }
+}
+
+#[derive(PartialEq, Eq)]
+pub enum StringPart {
+    Normal(String),
+    Highlighted(String),
+}
+
+impl StringPart {
+    pub fn content(&self) -> String {
+        match self {
+            &StringPart::Normal(ref s) | & StringPart::Highlighted(ref s) => s.to_owned()
+        }
+    }
+}
+
 impl Diagnostic {
     pub fn new(level: Level, message: &str) -> Self {
         Diagnostic::new_with_code(level, None, message)
@@ -47,6 +88,7 @@ impl Diagnostic {
             code: code,
             span: MultiSpan::new(),
             children: vec![],
+            suggestions: vec![],
         }
     }
 
@@ -73,16 +115,15 @@ 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.
-    pub fn span_label(&mut self, span: Span, label: &fmt::Display)
-                      -> &mut Self {
-        self.span.push_span_label(span, format!("{}", label));
+    pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self {
+        self.span.push_span_label(span, label.into());
         self
     }
 
     pub fn note_expected_found(&mut self,
                                label: &fmt::Display,
-                               expected: &fmt::Display,
-                               found: &fmt::Display)
+                               expected: DiagnosticStyledString,
+                               found: DiagnosticStyledString)
                                -> &mut Self
     {
         self.note_expected_found_extra(label, expected, found, &"", &"")
@@ -90,21 +131,29 @@ impl Diagnostic {
 
     pub fn note_expected_found_extra(&mut self,
                                      label: &fmt::Display,
-                                     expected: &fmt::Display,
-                                     found: &fmt::Display,
+                                     expected: DiagnosticStyledString,
+                                     found: DiagnosticStyledString,
                                      expected_extra: &fmt::Display,
                                      found_extra: &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(vec![
-            (format!("expected {} `", label), Style::NoStyle),
-            (format!("{}", expected), Style::Highlight),
-            (format!("`{}\n", expected_extra), Style::NoStyle),
-            (format!("   found {} `", label), Style::NoStyle),
-            (format!("{}", found), Style::Highlight),
-            (format!("`{}", found_extra), Style::NoStyle),
-        ]);
+        self.highlighted_note(msg);
         self
     }
 
@@ -154,19 +203,26 @@ impl Diagnostic {
 
     /// Prints out a message with a suggested edit of the code.
     ///
-    /// See `diagnostic::RenderSpan::Suggestion` for more information.
-    pub fn span_suggestion<S: Into<MultiSpan>>(&mut self,
-                                               sp: S,
-                                               msg: &str,
-                                               suggestion: String)
-                                               -> &mut Self {
-        self.sub(Level::Help,
-                 msg,
-                 MultiSpan::new(),
-                 Some(Suggestion(CodeSuggestion {
-                     msp: sp.into(),
-                     substitutes: vec![suggestion],
-                 })));
+    /// See `diagnostic::CodeSuggestion` for more information.
+    pub fn span_suggestion(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self {
+        self.suggestions.push(CodeSuggestion {
+            substitution_parts: vec![Substitution {
+                span: sp,
+                substitutions: vec![suggestion],
+            }],
+            msg: msg.to_owned(),
+        });
+        self
+    }
+
+    pub fn span_suggestions(&mut self, sp: Span, msg: &str, suggestions: Vec<String>) -> &mut Self {
+        self.suggestions.push(CodeSuggestion {
+            substitution_parts: vec![Substitution {
+                span: sp,
+                substitutions: suggestions,
+            }],
+            msg: msg.to_owned(),
+        });
         self
     }
 
diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs
index 7dfea6b8951..0081339a363 100644
--- a/src/librustc_errors/diagnostic_builder.rs
+++ b/src/librustc_errors/diagnostic_builder.rs
@@ -9,6 +9,8 @@
 // except according to those terms.
 
 use Diagnostic;
+use DiagnosticStyledString;
+
 use Level;
 use Handler;
 use std::fmt::{self, Debug};
@@ -97,7 +99,10 @@ impl<'a> DiagnosticBuilder<'a> {
 
         self.handler.emitter.borrow_mut().emit(&self);
         self.cancel();
-        self.handler.panic_if_treat_err_as_bug();
+
+        if self.level == Level::Error {
+            self.handler.panic_if_treat_err_as_bug();
+        }
 
         // if self.is_fatal() {
         //     panic!(FatalError);
@@ -110,19 +115,21 @@ 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.
-    forward!(pub fn span_label(&mut self, span: Span, label: &fmt::Display)
-                               -> &mut Self);
+    pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self {
+        self.diagnostic.span_label(span, label);
+        self
+    }
 
     forward!(pub fn note_expected_found(&mut self,
                                         label: &fmt::Display,
-                                        expected: &fmt::Display,
-                                        found: &fmt::Display)
+                                        expected: DiagnosticStyledString,
+                                        found: DiagnosticStyledString)
                                         -> &mut Self);
 
     forward!(pub fn note_expected_found_extra(&mut self,
                                               label: &fmt::Display,
-                                              expected: &fmt::Display,
-                                              found: &fmt::Display,
+                                              expected: DiagnosticStyledString,
+                                              found: DiagnosticStyledString,
                                               expected_extra: &fmt::Display,
                                               found_extra: &fmt::Display)
                                               -> &mut Self);
@@ -139,11 +146,16 @@ impl<'a> DiagnosticBuilder<'a> {
                                                   sp: S,
                                                   msg: &str)
                                                   -> &mut Self);
-    forward!(pub fn span_suggestion<S: Into<MultiSpan>>(&mut self,
-                                                        sp: S,
-                                                        msg: &str,
-                                                        suggestion: String)
-                                                        -> &mut Self);
+    forward!(pub fn span_suggestion(&mut self,
+                                    sp: Span,
+                                    msg: &str,
+                                    suggestion: String)
+                                    -> &mut Self);
+    forward!(pub fn span_suggestions(&mut self,
+                                     sp: Span,
+                                     msg: &str,
+                                     suggestions: Vec<String>)
+                                     -> &mut Self);
     forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self);
     forward!(pub fn code(&mut self, s: String) -> &mut Self);
 
@@ -180,8 +192,8 @@ impl<'a> Debug for DiagnosticBuilder<'a> {
     }
 }
 
-/// Destructor bomb - a DiagnosticBuilder must be either emitted or cancelled or
-/// we emit a bug.
+/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or cancelled
+/// or we emit a bug.
 impl<'a> Drop for DiagnosticBuilder<'a> {
     fn drop(&mut self) {
         if !panicking() && !self.cancelled() {
diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs
index 431edb3c9bc..a9645f9ab7b 100644
--- a/src/librustc_errors/emitter.rs
+++ b/src/librustc_errors/emitter.rs
@@ -10,7 +10,7 @@
 
 use self::Destination::*;
 
-use syntax_pos::{COMMAND_LINE_SP, DUMMY_SP, FileMap, Span, MultiSpan, CharPos};
+use syntax_pos::{DUMMY_SP, FileMap, Span, MultiSpan, CharPos};
 
 use {Level, CodeSuggestion, DiagnosticBuilder, SubDiagnostic, CodeMapper};
 use RenderSpan::*;
@@ -21,6 +21,8 @@ use std::io::prelude::*;
 use std::io;
 use std::rc::Rc;
 use term;
+use std::collections::HashMap;
+use std::cmp::min;
 
 /// Emitter trait for emitting errors.
 pub trait Emitter {
@@ -32,6 +34,36 @@ impl Emitter for EmitterWriter {
     fn emit(&mut self, db: &DiagnosticBuilder) {
         let mut primary_span = db.span.clone();
         let mut children = db.children.clone();
+
+        if let Some((sugg, rest)) = db.suggestions.split_first() {
+            if rest.is_empty() &&
+               // don't display multipart suggestions as labels
+               sugg.substitution_parts.len() == 1 &&
+               // don't display multi-suggestions as labels
+               sugg.substitutions() == 1 &&
+               // don't display long messages as labels
+               sugg.msg.split_whitespace().count() < 10 &&
+               // don't display multiline suggestions as labels
+               sugg.substitution_parts[0].substitutions[0].find('\n').is_none() {
+                let substitution = &sugg.substitution_parts[0].substitutions[0];
+                let msg = format!("help: {} `{}`", sugg.msg, substitution);
+                primary_span.push_span_label(sugg.substitution_spans().next().unwrap(), 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
+                for sugg in &db.suggestions {
+                    children.push(SubDiagnostic {
+                        level: Level::Help,
+                        message: Vec::new(),
+                        span: MultiSpan::new(),
+                        render_span: Some(Suggestion(sugg.clone())),
+                    });
+                }
+            }
+        }
+
         self.fix_multispans_in_std_macros(&mut primary_span, &mut children);
         self.emit_messages_default(&db.level,
                                    &db.styled_message(),
@@ -43,6 +75,10 @@ impl Emitter for EmitterWriter {
 
 /// 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 {
@@ -72,21 +108,6 @@ struct FileWithAnnotatedLines {
     multiline_depth: usize,
 }
 
-
-/// Do not use this for messages that end in `\n` – use `println_maybe_styled` instead. See
-/// `EmitterWriter::print_maybe_styled` for details.
-macro_rules! print_maybe_styled {
-    ($dst: expr, $style: expr, $($arg: tt)*) => {
-        $dst.print_maybe_styled(format_args!($($arg)*), $style, false)
-    }
-}
-
-macro_rules! println_maybe_styled {
-    ($dst: expr, $style: expr, $($arg: tt)*) => {
-        $dst.print_maybe_styled(format_args!($($arg)*), $style, true)
-    }
-}
-
 impl EmitterWriter {
     pub fn stderr(color_config: ColorConfig, code_map: Option<Rc<CodeMapper>>) -> EmitterWriter {
         if color_config.use_color() {
@@ -151,20 +172,11 @@ impl EmitterWriter {
 
         if let Some(ref cm) = self.cm {
             for span_label in msp.span_labels() {
-                if span_label.span == DUMMY_SP || span_label.span == COMMAND_LINE_SP {
+                if span_label.span == DUMMY_SP {
                     continue;
                 }
                 let lo = cm.lookup_char_pos(span_label.span.lo);
                 let mut hi = cm.lookup_char_pos(span_label.span.hi);
-                let mut is_minimized = false;
-
-                // If the span is long multi-line, simplify down to the span of one character
-                let max_multiline_span_length = 8;
-                if lo.line != hi.line && (hi.line - lo.line) > max_multiline_span_length {
-                    hi.line = lo.line;
-                    hi.col = CharPos(lo.col.0 + 1);
-                    is_minimized = true;
-                }
 
                 // 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
@@ -175,16 +187,7 @@ impl EmitterWriter {
                     hi.col = CharPos(lo.col.0 + 1);
                 }
 
-                let mut ann = Annotation {
-                    start_col: lo.col.0,
-                    end_col: hi.col.0,
-                    is_primary: span_label.is_primary,
-                    label: span_label.label.clone(),
-                    annotation_type: AnnotationType::Singleline,
-                };
-                if is_minimized {
-                    ann.annotation_type = AnnotationType::Minimized;
-                } else if lo.line != hi.line {
+                let ann_type = if lo.line != hi.line {
                     let ml = MultilineAnnotation {
                         depth: 1,
                         line_start: lo.line,
@@ -194,8 +197,17 @@ impl EmitterWriter {
                         is_primary: span_label.is_primary,
                         label: span_label.label.clone(),
                     };
-                    ann.annotation_type = AnnotationType::Multiline(ml.clone());
-                    multiline_annotations.push((lo.file.clone(), ml));
+                    multiline_annotations.push((lo.file.clone(), ml.clone()));
+                    AnnotationType::Multiline(ml)
+                } else {
+                    AnnotationType::Singleline
+                };
+                let ann = Annotation {
+                    start_col: lo.col.0,
+                    end_col: hi.col.0,
+                    is_primary: span_label.is_primary,
+                    label: span_label.label.clone(),
+                    annotation_type: ann_type,
                 };
 
                 if !ann.is_multiline() {
@@ -233,9 +245,15 @@ impl EmitterWriter {
                 max_depth = ann.depth;
             }
             add_annotation_to_file(&mut output, file.clone(), ann.line_start, ann.as_start());
-            for line in ann.line_start + 1..ann.line_end {
+            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() {
@@ -249,16 +267,13 @@ impl EmitterWriter {
                           file: Rc<FileMap>,
                           line: &Line,
                           width_offset: usize,
-                          multiline_depth: usize) {
-        let source_string = file.get_line(line.line_index - 1)
-            .unwrap_or("");
+                          code_offset: usize) -> Vec<(usize, Style)> {
+        let source_string = match file.get_line(line.line_index - 1) {
+            Some(s) => s,
+            None => return Vec::new(),
+        };
 
         let line_offset = buffer.num_lines();
-        let code_offset = if multiline_depth == 0 {
-            width_offset
-        } else {
-            width_offset + multiline_depth + 1
-        };
 
         // First create the source line we will highlight.
         buffer.puts(line_offset, code_offset, &source_string, Style::Quotation);
@@ -269,6 +284,41 @@ impl EmitterWriter {
 
         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[0..ann.start_col].trim() == "" {
+                        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());
@@ -286,7 +336,7 @@ impl EmitterWriter {
         //      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 `~`.
+        // and "annotations lines", where the highlight lines have the `^`.
 
         // Sort the annotations by (start, end col)
         let mut annotations = line.annotations.clone();
@@ -361,10 +411,8 @@ impl EmitterWriter {
         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.is_line()      // take space (they have text and are not
-                    && !next.is_line()            // multiline lines).
-                    && annotation.has_label()
-                    && j > i
+                    && 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.
@@ -380,7 +428,7 @@ impl EmitterWriter {
                     } else {
                         0
                     };
-                    if overlaps(next, annotation, l) // Do not allow two labels to be in the same
+                    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:
                                                      //
@@ -389,11 +437,18 @@ impl EmitterWriter {
                                                      //      |      |
                                                      //      fn_spanx_span
                                                      //
-                        && !annotation.is_line()     // Do not add a new line if this annotation
-                        && !next.is_line()           // or the next are vertical line placeholders.
                         && annotation.has_label()    // Both labels must have some text, otherwise
-                        && next.has_label()          // they are not overlapping.
+                        && 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())
                     {
+                        // This annotation needs a new line in the output.
                         p += 1;
                         break;
                     }
@@ -403,6 +458,7 @@ impl EmitterWriter {
                 line_len = p;
             }
         }
+
         if line_len != 0 {
             line_len += 1;
         }
@@ -410,25 +466,9 @@ impl EmitterWriter {
         // 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.is_empty() || line.annotations.iter()
-            .filter(|a| {
-                // Set the multiline annotation vertical lines to the left of
-                // the code in this line.
-                if let AnnotationType::MultilineLine(depth) = a.annotation_type {
-                    buffer.putc(line_offset,
-                                width_offset + depth - 1,
-                                '|',
-                                if a.is_primary {
-                                    Style::UnderlinePrimary
-                                } else {
-                                    Style::UnderlineSecondary
-                                });
-                    false
-                } else {
-                    true
-                }
-            }).collect::<Vec<_>>().len() == 0
+            .filter(|a| !a.is_line()).collect::<Vec<_>>().len() == 0
         {
-            return;
+            return vec![];
         }
 
         // Write the colunmn separator.
@@ -483,8 +523,7 @@ impl EmitterWriter {
             }
         }
 
-        // Write the vertical lines for multiline spans and for labels that are
-        // on a different line as the underline.
+        // Write the vertical lines for labels that are on a different line as the underline.
         //
         // After this we will have:
         //
@@ -492,7 +531,7 @@ impl EmitterWriter {
         //   |  __________
         //   | |    |
         //   | |
-        // 3 | |
+        // 3 |
         // 4 | | }
         //   | |_
         for &(pos, annotation) in &annotations_position {
@@ -503,7 +542,7 @@ impl EmitterWriter {
             };
             let pos = pos + 1;
 
-            if pos > 1 && annotation.has_label() {
+            if pos > 1 && (annotation.has_label() || annotation.takes_space()) {
                 for p in line_offset + 1..line_offset + pos + 1 {
                     buffer.putc(p,
                                 code_offset + annotation.start_col,
@@ -528,16 +567,6 @@ impl EmitterWriter {
                                     style);
                     }
                 }
-                AnnotationType::MultilineLine(depth) => {
-                    // the first line will have already be filled when we checked
-                    // wether there were any annotations for this line.
-                    for p in line_offset + 1..line_offset + line_len + 2 {
-                        buffer.putc(p,
-                                    width_offset + depth - 1,
-                                    '|',
-                                    style);
-                    }
-                }
                 _ => (),
             }
         }
@@ -547,12 +576,12 @@ impl EmitterWriter {
         // After this we will have:
         //
         // 2 |   fn foo() {
-        //   |  __________ starting here...
-        //   | |    |
-        //   | |    something about `foo`
-        // 3 | |
-        // 4 | | }
-        //   | |_  ...ending here: test
+        //   |  __________
+        //   |      |
+        //   |      something about `foo`
+        // 3 |
+        // 4 |   }
+        //   |  _  test
         for &(pos, annotation) in &annotations_position {
             let style = if annotation.is_primary {
                 Style::LabelPrimary
@@ -590,12 +619,12 @@ impl EmitterWriter {
         // After this we will have:
         //
         // 2 |   fn foo() {
-        //   |  ____-_____^ starting here...
-        //   | |    |
-        //   | |    something about `foo`
-        // 3 | |
-        // 4 | | }
-        //   | |_^  ...ending here: test
+        //   |  ____-_____^
+        //   |      |
+        //   |      something about `foo`
+        // 3 |
+        // 4 |   }
+        //   |  _^  test
         for &(_, annotation) in &annotations_position {
             let (underline, style) = if annotation.is_primary {
                 ('^', Style::UnderlinePrimary)
@@ -609,13 +638,27 @@ impl EmitterWriter {
                             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::<Vec<_>>()
     }
 
     fn get_multispan_max_line_num(&mut self, msp: &MultiSpan) -> usize {
         let mut max = 0;
         if let Some(ref cm) = self.cm {
             for primary_span in msp.primary_spans() {
-                if primary_span != &DUMMY_SP && primary_span != &COMMAND_LINE_SP {
+                if primary_span != &DUMMY_SP {
                     let hi = cm.lookup_char_pos(primary_span.hi);
                     if hi.line > max {
                         max = hi.line;
@@ -623,7 +666,7 @@ impl EmitterWriter {
                 }
             }
             for span_label in msp.span_labels() {
-                if span_label.span != DUMMY_SP && span_label.span != COMMAND_LINE_SP {
+                if span_label.span != DUMMY_SP {
                     let hi = cm.lookup_char_pos(span_label.span.hi);
                     if hi.line > max {
                         max = hi.line;
@@ -659,20 +702,20 @@ impl EmitterWriter {
 
             // First, find all the spans in <*macros> and point instead at their use site
             for sp in span.primary_spans() {
-                if (*sp == COMMAND_LINE_SP) || (*sp == DUMMY_SP) {
+                if *sp == DUMMY_SP {
                     continue;
                 }
                 if cm.span_to_filename(sp.clone()).contains("macros>") {
-                    let v = cm.macro_backtrace(sp.clone());
+                    let v = sp.macro_backtrace();
                     if let Some(use_site) = v.last() {
                         before_after.push((sp.clone(), use_site.call_site.clone()));
                     }
                 }
-                for trace in cm.macro_backtrace(sp.clone()).iter().rev() {
+                for trace in sp.macro_backtrace().iter().rev() {
                     // Only show macro locations that are local
                     // and display them like a span_note
                     if let Some(def_site) = trace.def_site_span {
-                        if (def_site == COMMAND_LINE_SP) || (def_site == DUMMY_SP) {
+                        if def_site == DUMMY_SP {
                             continue;
                         }
                         // Check to make sure we're not in any <*macros>
@@ -689,11 +732,11 @@ impl EmitterWriter {
                 span.push_span_label(label_span, label_text);
             }
             for sp_label in span.span_labels() {
-                if (sp_label.span == COMMAND_LINE_SP) || (sp_label.span == DUMMY_SP) {
+                if sp_label.span == DUMMY_SP {
                     continue;
                 }
                 if cm.span_to_filename(sp_label.span.clone()).contains("macros>") {
-                    let v = cm.macro_backtrace(sp_label.span.clone());
+                    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()));
                     }
@@ -734,7 +777,7 @@ impl EmitterWriter {
     /// displayed, keeping the provided highlighting.
     fn msg_to_buffer(&self,
                      buffer: &mut StyledBuffer,
-                     msg: &Vec<(String, Style)>,
+                     msg: &[(String, Style)],
                      padding: usize,
                      label: &str,
                      override_style: Option<Style>) {
@@ -848,7 +891,7 @@ impl EmitterWriter {
         // Make sure our primary file comes first
         let primary_lo = if let (Some(ref cm), Some(ref primary_span)) =
             (self.cm.as_ref(), msp.primary_span().as_ref()) {
-            if primary_span != &&DUMMY_SP && primary_span != &&COMMAND_LINE_SP {
+            if primary_span != &&DUMMY_SP {
                 cm.lookup_char_pos(primary_span.lo)
             } else {
                 emit_to_destination(&buffer.render(), level, &mut self.dst)?;
@@ -866,6 +909,11 @@ impl EmitterWriter {
 
         // Print out the annotate source lines that correspond with the error
         for annotated_file in annotated_files {
+            // we can't annotate anything if the source is unavailable.
+            if annotated_file.file.src.is_none() {
+                continue;
+            }
+
             // print out the span location and spacer before we print the annotated source
             // to do this, we need to know if this span will be primary
             let is_primary = primary_lo.file.name == annotated_file.file.name;
@@ -902,22 +950,64 @@ impl EmitterWriter {
             let buffer_msg_line_offset = buffer.num_lines();
             draw_col_separator_no_space(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1);
 
+            // Contains the vertical lines' positions for active multiline annotations
+            let mut multilines = HashMap::new();
+
             // Next, output the annotate source for this file
             for line_idx in 0..annotated_file.lines.len() {
-                self.render_source_line(&mut buffer,
-                                        annotated_file.file.clone(),
-                                        &annotated_file.lines[line_idx],
-                                        3 + max_line_num_len,
-                                        annotated_file.multiline_depth);
+                let previous_buffer_line = buffer.num_lines();
+
+                let width_offset = 3 + max_line_num_len;
+                let code_offset = if annotated_file.multiline_depth == 0 {
+                    width_offset
+                } else {
+                    width_offset + annotated_file.multiline_depth + 1
+                };
 
+                let depths = self.render_source_line(&mut buffer,
+                                                     annotated_file.file.clone(),
+                                                     &annotated_file.lines[line_idx],
+                                                     width_offset,
+                                                     code_offset);
+
+                let mut to_add = HashMap::new();
+
+                for (depth, style) in depths {
+                    if multilines.get(&depth).is_some() {
+                        multilines.remove(&depth);
+                    } else {
+                        to_add.insert(depth, style);
+                    }
+                }
+
+                // Set the multiline annotation vertical lines to the left of
+                // the code in this line.
+                for (depth, style) in &multilines {
+                    for line in previous_buffer_line..buffer.num_lines() {
+                        draw_multiline_line(&mut buffer,
+                                            line,
+                                            width_offset,
+                                            *depth,
+                                            *style);
+                    }
+                }
                 // check to see if we need to print out or elide lines that come between
-                // this annotated line and the next one
+                // this annotated line and the next one.
                 if line_idx < (annotated_file.lines.len() - 1) {
                     let line_idx_delta = annotated_file.lines[line_idx + 1].line_index -
                                          annotated_file.lines[line_idx].line_index;
                     if line_idx_delta > 2 {
                         let last_buffer_line_num = buffer.num_lines();
                         buffer.puts(last_buffer_line_num, 0, "...", Style::LineNumber);
+
+                        // Set the multiline annotation vertical lines on `...` bridging line.
+                        for (depth, style) in &multilines {
+                            draw_multiline_line(&mut buffer,
+                                                last_buffer_line_num,
+                                                width_offset,
+                                                *depth,
+                                                *style);
+                        }
                     } else if line_idx_delta == 2 {
                         let unannotated_line = annotated_file.file
                             .get_line(annotated_file.lines[line_idx].line_index)
@@ -932,11 +1022,21 @@ impl EmitterWriter {
                                     Style::LineNumber);
                         draw_col_separator(&mut buffer, last_buffer_line_num, 1 + max_line_num_len);
                         buffer.puts(last_buffer_line_num,
-                                    3 + max_line_num_len,
+                                    code_offset,
                                     &unannotated_line,
                                     Style::Quotation);
+
+                        for (depth, style) in &multilines {
+                            draw_multiline_line(&mut buffer,
+                                                last_buffer_line_num,
+                                                width_offset,
+                                                *depth,
+                                                *style);
+                        }
                     }
                 }
+
+                multilines.extend(&to_add);
             }
         }
 
@@ -948,43 +1048,48 @@ impl EmitterWriter {
     fn emit_suggestion_default(&mut self,
                                suggestion: &CodeSuggestion,
                                level: &Level,
-                               msg: &Vec<(String, Style)>,
                                max_line_num_len: usize)
                                -> io::Result<()> {
         use std::borrow::Borrow;
 
-        let primary_span = suggestion.msp.primary_span().unwrap();
+        let primary_span = suggestion.substitution_spans().next().unwrap();
         if let Some(ref cm) = self.cm {
             let mut buffer = StyledBuffer::new();
 
-            buffer.append(0, &level.to_string(), Style::Level(level.clone()));
-            buffer.append(0, ": ", Style::HeaderMsg);
-            self.msg_to_buffer(&mut buffer,
-                               msg,
-                               max_line_num_len,
-                               "suggestion",
-                               Some(Style::HeaderMsg));
-
             let lines = cm.span_to_lines(primary_span).unwrap();
 
             assert!(!lines.lines.is_empty());
 
-            let complete = suggestion.splice_lines(cm.borrow());
+            buffer.append(0, &level.to_string(), Style::Level(level.clone()));
+            buffer.append(0, ": ", Style::HeaderMsg);
+            self.msg_to_buffer(&mut buffer,
+                            &[(suggestion.msg.to_owned(), Style::NoStyle)],
+                            max_line_num_len,
+                            "suggestion",
+                            Some(Style::HeaderMsg));
 
-            // print the suggestion without any line numbers, but leave
-            // space for them. This helps with lining up with previous
-            // snippets from the actual error being reported.
-            let mut lines = complete.lines();
+            let suggestions = suggestion.splice_lines(cm.borrow());
             let mut row_num = 1;
-            for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) {
-                draw_col_separator(&mut buffer, row_num, max_line_num_len + 1);
-                buffer.append(row_num, line, Style::NoStyle);
-                row_num += 1;
-            }
+            for complete in suggestions.iter().take(MAX_SUGGESTIONS) {
+
+                // print the suggestion without any line numbers, but leave
+                // space for them. This helps with lining up with previous
+                // snippets from the actual error being reported.
+                let mut lines = complete.lines();
+                for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) {
+                    draw_col_separator(&mut buffer, row_num, max_line_num_len + 1);
+                    buffer.append(row_num, line, Style::NoStyle);
+                    row_num += 1;
+                }
 
-            // if we elided some lines, add an ellipsis
-            if let Some(_) = lines.next() {
-                buffer.append(row_num, "...", Style::NoStyle);
+                // if we elided some lines, add an ellipsis
+                if let Some(_) = lines.next() {
+                    buffer.append(row_num, "...", Style::NoStyle);
+                }
+            }
+            if suggestions.len() > MAX_SUGGESTIONS {
+                let msg = format!("and {} other candidates", suggestions.len() - MAX_SUGGESTIONS);
+                buffer.append(row_num, &msg, Style::NoStyle);
             }
             emit_to_destination(&buffer.render(), level, &mut self.dst)?;
         }
@@ -1025,7 +1130,6 @@ impl EmitterWriter {
                         Some(Suggestion(ref cs)) => {
                             match self.emit_suggestion_default(cs,
                                                                &child.level,
-                                                               &child.styled_message(),
                                                                max_line_num_len) {
                                 Err(e) => panic!("failed to emit error: {}", e),
                                 _ => ()
@@ -1085,6 +1189,15 @@ fn draw_note_separator(buffer: &mut StyledBuffer, line: usize, col: usize) {
     buffer.puts(line, col, "= ", Style::LineNumber);
 }
 
+fn draw_multiline_line(buffer: &mut StyledBuffer,
+                       line: usize,
+                       offset: usize,
+                       depth: usize,
+                       style: Style)
+{
+    buffer.putc(line, offset + depth - 1, '|', style);
+}
+
 fn num_overlap(a_start: usize, a_end: usize, b_start: usize, b_end:usize, inclusive: bool) -> bool {
     let extra = if inclusive {
         1
@@ -1183,10 +1296,8 @@ impl Write for BufferedWriter {
     }
     fn flush(&mut self) -> io::Result<()> {
         let mut stderr = io::stderr();
-        let result = (|| {
-            stderr.write_all(&self.buffer)?;
-            stderr.flush()
-        })();
+        let result = stderr.write_all(&self.buffer)
+                           .and_then(|_| stderr.flush());
         self.buffer.clear();
         result
     }
diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs
index 4c889dad8ca..f7191e49216 100644
--- a/src/librustc_errors/lib.rs
+++ b/src/librustc_errors/lib.rs
@@ -9,7 +9,6 @@
 // except according to those terms.
 
 #![crate_name = "rustc_errors"]
-#![unstable(feature = "rustc_private", issue = "27812")]
 #![crate_type = "dylib"]
 #![crate_type = "rlib"]
 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
@@ -19,10 +18,13 @@
 
 #![feature(custom_attribute)]
 #![allow(unused_attributes)]
-#![feature(rustc_private)]
-#![feature(staged_api)]
 #![feature(range_contains)]
 #![feature(libc)]
+#![feature(conservative_impl_trait)]
+
+#![cfg_attr(stage0, unstable(feature = "rustc_private", issue = "27812"))]
+#![cfg_attr(stage0, feature(rustc_private))]
+#![cfg_attr(stage0, feature(staged_api))]
 
 extern crate term;
 extern crate libc;
@@ -48,7 +50,6 @@ pub mod styled_buffer;
 mod lock;
 
 use syntax_pos::{BytePos, Loc, FileLinesResult, FileName, MultiSpan, Span, NO_EXPANSION};
-use syntax_pos::MacroBacktrace;
 
 #[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
 pub enum RenderSpan {
@@ -66,8 +67,33 @@ pub enum RenderSpan {
 
 #[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
 pub struct CodeSuggestion {
-    pub msp: MultiSpan,
-    pub substitutes: Vec<String>,
+    /// Each substitute can have multiple variants due to multiple
+    /// applicable suggestions
+    ///
+    /// `foo.bar` might be replaced with `a.b` or `x.y` by replacing
+    /// `foo` and `bar` on their own:
+    ///
+    /// ```
+    /// vec![
+    ///     (0..3, vec!["a", "x"]),
+    ///     (4..7, vec!["b", "y"]),
+    /// ]
+    /// ```
+    ///
+    /// or by replacing the entire span:
+    ///
+    /// ```
+    /// vec![(0..7, vec!["a.b", "x.y"])]
+    /// ```
+    pub substitution_parts: Vec<Substitution>,
+    pub msg: String,
+}
+
+#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
+/// See the docs on `CodeSuggestion::substitutions`
+pub struct Substitution {
+    pub span: Span,
+    pub substitutions: Vec<String>,
 }
 
 pub trait CodeMapper {
@@ -75,13 +101,22 @@ pub trait CodeMapper {
     fn span_to_lines(&self, sp: Span) -> FileLinesResult;
     fn span_to_string(&self, sp: Span) -> String;
     fn span_to_filename(&self, sp: Span) -> FileName;
-    fn macro_backtrace(&self, span: Span) -> Vec<MacroBacktrace>;
     fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span>;
 }
 
 impl CodeSuggestion {
-    /// Returns the assembled code suggestion.
-    pub fn splice_lines(&self, cm: &CodeMapper) -> String {
+    /// Returns the number of substitutions
+    fn substitutions(&self) -> usize {
+        self.substitution_parts[0].substitutions.len()
+    }
+
+    /// Returns the number of substitutions
+    pub fn substitution_spans<'a>(&'a self) -> impl Iterator<Item = Span> + 'a {
+        self.substitution_parts.iter().map(|sub| sub.span)
+    }
+
+    /// Returns the assembled code suggestions.
+    pub fn splice_lines(&self, cm: &CodeMapper) -> Vec<String> {
         use syntax_pos::{CharPos, Loc, Pos};
 
         fn push_trailing(buf: &mut String,
@@ -103,24 +138,26 @@ impl CodeSuggestion {
             }
         }
 
-        let mut primary_spans = self.msp.primary_spans().to_owned();
-
-        assert_eq!(primary_spans.len(), self.substitutes.len());
-        if primary_spans.is_empty() {
-            return format!("");
+        if self.substitution_parts.is_empty() {
+            return vec![String::new()];
         }
 
+        let mut primary_spans: Vec<_> = self.substitution_parts
+            .iter()
+            .map(|sub| (sub.span, &sub.substitutions))
+            .collect();
+
         // Assumption: all spans are in the same file, and all spans
         // are disjoint. Sort in ascending order.
-        primary_spans.sort_by_key(|sp| sp.lo);
+        primary_spans.sort_by_key(|sp| sp.0.lo);
 
         // Find the bounding span.
-        let lo = primary_spans.iter().map(|sp| sp.lo).min().unwrap();
-        let hi = primary_spans.iter().map(|sp| sp.hi).min().unwrap();
+        let lo = primary_spans.iter().map(|sp| sp.0.lo).min().unwrap();
+        let hi = primary_spans.iter().map(|sp| sp.0.hi).min().unwrap();
         let bounding_span = Span {
             lo: lo,
             hi: hi,
-            expn_id: NO_EXPANSION,
+            ctxt: NO_EXPANSION,
         };
         let lines = cm.span_to_lines(bounding_span).unwrap();
         assert!(!lines.lines.is_empty());
@@ -139,33 +176,40 @@ impl CodeSuggestion {
         prev_hi.col = CharPos::from_usize(0);
 
         let mut prev_line = fm.get_line(lines.lines[0].line_index);
-        let mut buf = String::new();
+        let mut bufs = vec![String::new(); self.substitutions()];
 
-        for (sp, substitute) in primary_spans.iter().zip(self.substitutes.iter()) {
+        for (sp, substitutes) in primary_spans {
             let cur_lo = cm.lookup_char_pos(sp.lo);
-            if prev_hi.line == cur_lo.line {
-                push_trailing(&mut buf, prev_line, &prev_hi, Some(&cur_lo));
-            } else {
-                push_trailing(&mut buf, prev_line, &prev_hi, None);
-                // push lines between the previous and current span (if any)
-                for idx in prev_hi.line..(cur_lo.line - 1) {
-                    if let Some(line) = fm.get_line(idx) {
-                        buf.push_str(line);
-                        buf.push('\n');
+            for (buf, substitute) in bufs.iter_mut().zip(substitutes) {
+                if prev_hi.line == cur_lo.line {
+                    push_trailing(buf, prev_line, &prev_hi, Some(&cur_lo));
+                } else {
+                    push_trailing(buf, prev_line, &prev_hi, None);
+                    // push lines between the previous and current span (if any)
+                    for idx in prev_hi.line..(cur_lo.line - 1) {
+                        if let Some(line) = fm.get_line(idx) {
+                            buf.push_str(line);
+                            buf.push('\n');
+                        }
+                    }
+                    if let Some(cur_line) = fm.get_line(cur_lo.line - 1) {
+                        buf.push_str(&cur_line[..cur_lo.col.to_usize()]);
                     }
                 }
-                if let Some(cur_line) = fm.get_line(cur_lo.line - 1) {
-                    buf.push_str(&cur_line[..cur_lo.col.to_usize()]);
-                }
+                buf.push_str(substitute);
             }
-            buf.push_str(substitute);
             prev_hi = cm.lookup_char_pos(sp.hi);
             prev_line = fm.get_line(prev_hi.line - 1);
         }
-        push_trailing(&mut buf, prev_line, &prev_hi, None);
-        // remove trailing newline
-        buf.pop();
-        buf
+        for buf in &mut bufs {
+            // if the replacement already ends with a newline, don't print the next line
+            if !buf.ends_with('\n') {
+                push_trailing(buf, prev_line, &prev_hi, None);
+            }
+            // remove trailing newline
+            buf.pop();
+        }
+        bufs
     }
 }
 
@@ -205,7 +249,7 @@ impl error::Error for ExplicitBug {
     }
 }
 
-pub use diagnostic::{Diagnostic, SubDiagnostic};
+pub use diagnostic::{Diagnostic, SubDiagnostic, DiagnosticStyledString, StringPart};
 pub use diagnostic_builder::DiagnosticBuilder;
 
 /// A handler deals with errors; certain errors
@@ -339,7 +383,7 @@ impl Handler {
     pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> FatalError {
         self.emit(&sp.into(), msg, Fatal);
         self.panic_if_treat_err_as_bug();
-        return FatalError;
+        FatalError
     }
     pub fn span_fatal_with_code<S: Into<MultiSpan>>(&self,
                                                     sp: S,
@@ -348,7 +392,7 @@ impl Handler {
                                                     -> FatalError {
         self.emit_with_code(&sp.into(), msg, code, Fatal);
         self.panic_if_treat_err_as_bug();
-        return FatalError;
+        FatalError
     }
     pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
         self.emit(&sp.into(), msg, Error);
@@ -377,6 +421,9 @@ impl Handler {
         panic!(ExplicitBug);
     }
     pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
+        if self.treat_err_as_bug {
+            self.span_bug(sp, msg);
+        }
         let mut delayed = self.delayed_span_bug.borrow_mut();
         *delayed = Some((sp.into(), msg.to_string()));
     }
@@ -386,6 +433,14 @@ impl Handler {
     pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
         self.emit(&sp.into(), msg, Note);
     }
+    pub fn span_note_diag<'a>(&'a self,
+                              sp: Span,
+                              msg: &str)
+                              -> DiagnosticBuilder<'a> {
+        let mut db = DiagnosticBuilder::new(self, Note, msg);
+        db.set_span(sp);
+        db
+    }
     pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
         self.span_bug(sp, &format!("unimplemented {}", msg));
     }
@@ -446,10 +501,7 @@ impl Handler {
 
                 return;
             }
-            1 => s = "aborting due to previous error".to_string(),
-            _ => {
-                s = format!("aborting due to {} previous errors", self.err_count.get());
-            }
+            _ => s = "aborting due to previous error(s)".to_string(),
         }
 
         panic!(self.fatal(&s));
diff --git a/src/librustc_errors/snippet.rs b/src/librustc_errors/snippet.rs
index 5debbf4d37c..7401ead2208 100644
--- a/src/librustc_errors/snippet.rs
+++ b/src/librustc_errors/snippet.rs
@@ -63,7 +63,7 @@ impl MultilineAnnotation {
             start_col: self.start_col,
             end_col: self.start_col + 1,
             is_primary: self.is_primary,
-            label: Some("starting here...".to_owned()),
+            label: None,
             annotation_type: AnnotationType::MultilineStart(self.depth)
         }
     }
@@ -73,10 +73,7 @@ impl MultilineAnnotation {
             start_col: self.end_col - 1,
             end_col: self.end_col,
             is_primary: self.is_primary,
-            label: match self.label {
-                Some(ref label) => Some(format!("...ending here: {}", label)),
-                None => Some("...ending here".to_owned()),
-            },
+            label: self.label.clone(),
             annotation_type: AnnotationType::MultilineEnd(self.depth)
         }
     }
@@ -97,9 +94,6 @@ pub enum AnnotationType {
     /// Annotation under a single line of code
     Singleline,
 
-    /// Annotation under the first character of a multiline span
-    Minimized,
-
     /// Annotation enclosing the first and last character of a multiline span
     Multiline(MultilineAnnotation),
 
@@ -109,15 +103,18 @@ pub enum AnnotationType {
     // Each of these corresponds to one part of the following diagram:
     //
     //     x |   foo(1 + bar(x,
-    //       |  _________^ starting here...           < MultilineStart
-    //     x | |             y),                      < MultilineLine
-    //       | |______________^ ...ending here: label < MultilineEnd
+    //       |  _________^              < MultilineStart
+    //     x | |             y),        < MultilineLine
+    //       | |______________^ label   < MultilineEnd
     //     x |       z);
     /// Annotation marking the first character of a fully shown multiline span
     MultilineStart(usize),
     /// Annotation marking the last character of a fully shown multiline span
     MultilineEnd(usize),
     /// Line at the left enclosing the lines of a fully shown multiline span
+    // Just a placeholder for the drawing algorithm, to know that it shouldn't skip the first 4
+    // and last 2 lines of code. The actual line is drawn in `emit_message_default` and not in
+    // `draw_multiline_line`.
     MultilineLine(usize),
 }
 
@@ -144,13 +141,6 @@ pub struct Annotation {
 }
 
 impl Annotation {
-    pub fn is_minimized(&self) -> bool {
-        match self.annotation_type {
-            AnnotationType::Minimized => true,
-            _ => false,
-        }
-    }
-
     /// Wether this annotation is a vertical line placeholder.
     pub fn is_line(&self) -> bool {
         if let AnnotationType::MultilineLine(_) = self.annotation_type {
@@ -196,6 +186,15 @@ impl Annotation {
             false
         }
     }
+
+    pub fn takes_space(&self) -> bool {
+        // Multiline annotations always have to keep vertical space.
+        match self.annotation_type {
+            AnnotationType::MultilineStart(_) |
+            AnnotationType::MultilineEnd(_) => true,
+            _ => false,
+        }
+    }
 }
 
 #[derive(Debug)]