about summary refs log tree commit diff
path: root/src/libsyntax/errors
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2016-04-20 14:56:01 -0400
committerNiko Matsakis <niko@alum.mit.edu>2016-05-02 11:47:10 -0400
commit11dc974a38fd533aa692cea213305056cd3a6902 (patch)
tree2c0d355faea79ba7412e56e1017fd8f65acaf72c /src/libsyntax/errors
parent5b150cf0ca2145c7d03a2b5ed92d9f65cc0ebcca (diff)
downloadrust-11dc974a38fd533aa692cea213305056cd3a6902.tar.gz
rust-11dc974a38fd533aa692cea213305056cd3a6902.zip
refactor to use new snippet code and model
Major changes:
- Remove old snippet rendering code and use the new stuff.
- Introduce `span_label` method to add a label
- Remove EndSpan mode and replace with a fn to get the last
  character of a span.
- Stop using `Option<MultiSpan>` and just use an empty `MultiSpan`
- and probably a bunch of other stuff :)
Diffstat (limited to 'src/libsyntax/errors')
-rw-r--r--src/libsyntax/errors/emitter.rs796
-rw-r--r--src/libsyntax/errors/mod.rs175
2 files changed, 322 insertions, 649 deletions
diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs
index 0b5234769b2..e963a5f794c 100644
--- a/src/libsyntax/errors/emitter.rs
+++ b/src/libsyntax/errors/emitter.rs
@@ -16,6 +16,7 @@ use diagnostics;
 use errors::{Level, RenderSpan, CodeSuggestion, DiagnosticBuilder};
 use errors::RenderSpan::*;
 use errors::Level::*;
+use errors::snippet::{RenderedLineKind, SnippetData, Style};
 
 use std::{cmp, fmt};
 use std::io::prelude::*;
@@ -24,27 +25,15 @@ use std::rc::Rc;
 use term;
 
 pub trait Emitter {
-    fn emit(&mut self, span: Option<&MultiSpan>, msg: &str, code: Option<&str>, lvl: Level);
-    fn custom_emit(&mut self, sp: &RenderSpan, msg: &str, lvl: Level);
+    fn emit(&mut self, span: &MultiSpan, msg: &str, code: Option<&str>, lvl: Level);
 
     /// Emit a structured diagnostic.
-    fn emit_struct(&mut self, db: &DiagnosticBuilder) {
-        self.emit(db.span.as_ref(), &db.message, db.code.as_ref().map(|s| &**s), db.level);
-        for child in &db.children {
-            match child.render_span {
-                Some(ref sp) => self.custom_emit(sp, &child.message, child.level),
-                None => self.emit(child.span.as_ref(), &child.message, None, child.level),
-            }
-        }
-    }
+    fn emit_struct(&mut self, db: &DiagnosticBuilder);
 }
 
 /// maximum number of lines we will print for each error; arbitrary.
 pub const MAX_HIGHLIGHT_LINES: usize = 6;
 
-/// maximum number of lines we will print for each span; arbitrary.
-const MAX_SP_LINES: usize = 6;
-
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum ColorConfig {
     Auto,
@@ -70,19 +59,23 @@ pub struct BasicEmitter {
 
 impl Emitter for BasicEmitter {
     fn emit(&mut self,
-            msp: Option<&MultiSpan>,
+            msp: &MultiSpan,
             msg: &str,
             code: Option<&str>,
             lvl: Level) {
-        assert!(msp.is_none(), "BasicEmitter can't handle spans");
+        assert!(msp.primary_span().is_none(), "BasicEmitter can't handle spans");
+
         if let Err(e) = print_diagnostic(&mut self.dst, "", lvl, msg, code) {
             panic!("failed to print diagnostics: {:?}", e);
         }
-
     }
 
-    fn custom_emit(&mut self, _: &RenderSpan, _: &str, _: Level) {
-        panic!("BasicEmitter can't handle custom_emit");
+    fn emit_struct(&mut self, db: &DiagnosticBuilder) {
+        self.emit(&db.span, &db.message, db.code.as_ref().map(|s| &**s), db.level);
+        for child in &db.children {
+            assert!(child.render_span.is_none(), "BasicEmitter can't handle spans");
+            self.emit(&child.span, &child.message, None, child.level);
+        }
     }
 }
 
@@ -101,33 +94,31 @@ pub struct EmitterWriter {
     dst: Destination,
     registry: Option<diagnostics::registry::Registry>,
     cm: Rc<codemap::CodeMap>,
+    first: bool,
 }
 
 impl Emitter for EmitterWriter {
     fn emit(&mut self,
-            msp: Option<&MultiSpan>,
+            msp: &MultiSpan,
             msg: &str,
             code: Option<&str>,
             lvl: Level) {
-        let error = match msp.map(|s|(s.to_span_bounds(), s)) {
-            Some((COMMAND_LINE_SP, msp)) => {
-                self.emit_(&FileLine(msp.clone()), msg, code, lvl)
-            },
-            Some((DUMMY_SP, _)) | None => print_diagnostic(&mut self.dst, "", lvl, msg, code),
-            Some((_, msp)) => self.emit_(&FullSpan(msp.clone()), msg, code, lvl),
-        };
-
-        if let Err(e) = error {
-            panic!("failed to print diagnostics: {:?}", e);
-        }
+        self.emit_multispan(msp, msg, code, lvl, true);
     }
 
-    fn custom_emit(&mut self,
-                   rsp: &RenderSpan,
-                   msg: &str,
-                   lvl: Level) {
-        if let Err(e) = self.emit_(rsp, msg, None, lvl) {
-            panic!("failed to print diagnostics: {:?}", e);
+    fn emit_struct(&mut self, db: &DiagnosticBuilder) {
+        self.emit_multispan(&db.span, &db.message,
+            db.code.as_ref().map(|s| &**s), db.level, true);
+
+        for child in &db.children {
+            match child.render_span {
+                Some(ref sp) =>
+                    self.emit_renderspan(sp, &child.message,
+                        child.level),
+                None =>
+                    self.emit_multispan(&child.span,
+                        &child.message, None, child.level, false),
+            }
         }
     }
 }
@@ -153,9 +144,10 @@ impl EmitterWriter {
                   -> EmitterWriter {
         if color_config.use_color() {
             let dst = Destination::from_stderr();
-            EmitterWriter { dst: dst, registry: registry, cm: code_map }
+            EmitterWriter { dst: dst, registry: registry, cm: code_map, first: true }
         } else {
-            EmitterWriter { dst: Raw(Box::new(io::stderr())), registry: registry, cm: code_map }
+            EmitterWriter { dst: Raw(Box::new(io::stderr())),
+                registry: registry, cm: code_map, first: true }
         }
     }
 
@@ -163,7 +155,49 @@ impl EmitterWriter {
                registry: Option<diagnostics::registry::Registry>,
                code_map: Rc<codemap::CodeMap>)
                -> EmitterWriter {
-        EmitterWriter { dst: Raw(dst), registry: registry, cm: code_map }
+        EmitterWriter { dst: Raw(dst), registry: registry, cm: code_map, first: true }
+    }
+
+    fn emit_multispan(&mut self,
+                      span: &MultiSpan,
+                      msg: &str,
+                      code: Option<&str>,
+                      lvl: Level,
+                      is_header: bool) {
+        if is_header {
+            if self.first {
+                self.first = false;
+            } else {
+                match write!(self.dst, "\n") {
+                    Ok(_) => { }
+                    Err(e) => {
+                        panic!("failed to print diagnostics: {:?}", e)
+                    }
+                }
+            }
+        }
+
+        let error = match span.primary_span() {
+            Some(COMMAND_LINE_SP) => {
+                self.emit_(&FileLine(span.clone()), msg, code, lvl)
+            }
+            Some(DUMMY_SP) | None => {
+                print_diagnostic(&mut self.dst, "", lvl, msg, code)
+            }
+            Some(_) => {
+                self.emit_(&FullSpan(span.clone()), msg, code, lvl)
+            }
+        };
+
+        if let Err(e) = error {
+            panic!("failed to print diagnostics: {:?}", e);
+        }
+    }
+
+    fn emit_renderspan(&mut self, sp: &RenderSpan, msg: &str, lvl: Level) {
+        if let Err(e) = self.emit_(sp, msg, None, lvl) {
+            panic!("failed to print diagnostics: {:?}", e);
+        }
     }
 
     fn emit_(&mut self,
@@ -173,51 +207,43 @@ impl EmitterWriter {
              lvl: Level)
              -> io::Result<()> {
         let msp = rsp.span();
-        let bounds = msp.to_span_bounds();
-
-        let ss = if bounds == COMMAND_LINE_SP {
-            "<command line option>".to_string()
-        } else if let EndSpan(_) = *rsp {
-            let span_end = Span { lo: bounds.hi, hi: bounds.hi, expn_id: bounds.expn_id};
-            self.cm.span_to_string(span_end)
-        } else {
-            self.cm.span_to_string(bounds)
-        };
-
-        print_diagnostic(&mut self.dst, &ss[..], lvl, msg, code)?;
+        let primary_span = msp.primary_span();
+
+        match code {
+            Some(code) if self.registry.as_ref()
+                          .and_then(|registry| registry.find_description(code)).is_some() =>
+            {
+                let code_with_explain = String::from("--explain ") + code;
+                print_diagnostic(&mut self.dst, "", lvl, msg, Some(&code_with_explain))?
+            }
+            _ => print_diagnostic(&mut self.dst, "", lvl, msg, code)?
+        }
 
         match *rsp {
             FullSpan(_) => {
                 self.highlight_lines(msp, lvl)?;
-                self.print_macro_backtrace(bounds)?;
-            }
-            EndSpan(_) => {
-                self.end_highlight_lines(msp, lvl)?;
-                self.print_macro_backtrace(bounds)?;
+                if let Some(primary_span) = primary_span {
+                    self.print_macro_backtrace(primary_span)?;
+                }
             }
             Suggestion(ref suggestion) => {
                 self.highlight_suggestion(suggestion)?;
-                self.print_macro_backtrace(bounds)?;
+                if let Some(primary_span) = primary_span {
+                    self.print_macro_backtrace(primary_span)?;
+                }
             }
             FileLine(..) => {
                 // no source text in this case!
             }
         }
 
-        if let Some(code) = code {
-            if let Some(_) = self.registry.as_ref()
-                                          .and_then(|registry| registry.find_description(code)) {
-                print_diagnostic(&mut self.dst, &ss[..], Help,
-                                 &format!("run `rustc --explain {}` to see a \
-                                           detailed explanation", code), None)?;
-            }
-        }
         Ok(())
     }
 
     fn highlight_suggestion(&mut self, suggestion: &CodeSuggestion) -> io::Result<()>
     {
-        let lines = self.cm.span_to_lines(suggestion.msp.to_span_bounds()).unwrap();
+        let primary_span = suggestion.msp.primary_span().unwrap();
+        let lines = self.cm.span_to_lines(primary_span).unwrap();
         assert!(!lines.lines.is_empty());
 
         let complete = suggestion.splice_lines(&self.cm);
@@ -251,325 +277,21 @@ impl EmitterWriter {
                        lvl: Level)
                        -> io::Result<()>
     {
-        let lines = match self.cm.span_to_lines(msp.to_span_bounds()) {
-            Ok(lines) => lines,
-            Err(_) => {
-                write!(&mut self.dst, "(internal compiler error: unprintable span)\n")?;
-                return Ok(());
-            }
-        };
-
-        let fm = &*lines.file;
-        if let None = fm.src {
-            return Ok(());
-        }
-
-        let display_line_infos = &lines.lines[..];
-        assert!(display_line_infos.len() > 0);
-
-        // Calculate the widest number to format evenly and fix #11715
-        let digits = line_num_max_digits(display_line_infos.last().unwrap());
-        let first_line_index = display_line_infos.first().unwrap().line_index;
-
-        let skip = fm.name.chars().count() + digits + 2;
-
-        let mut spans = msp.spans.iter().peekable();
-        let mut lines = display_line_infos.iter();
-        let mut prev_line_index = first_line_index.wrapping_sub(1);
-
-        // Display at most MAX_HIGHLIGHT_LINES lines.
-        let mut remaining_err_lines = MAX_HIGHLIGHT_LINES;
-
-        // To emit a overflowed spans code-lines *AFTER* the rendered spans
-        let mut overflowed_buf = String::new();
-        let mut overflowed = false;
-
-        // FIXME (#8706)
-        'l: loop {
-            if remaining_err_lines <= 0 {
-                break;
-            }
-            let line = match lines.next() {
-                Some(l) => l,
-                None => break,
-            };
-
-            // Skip is the number of characters we need to skip because they are
-            // part of the 'filename:line ' part of the code line.
-            let mut s: String = ::std::iter::repeat(' ').take(skip).collect();
-            let mut col = skip;
-            let mut lastc = ' ';
-
-            let cur_line_str = fm.get_line(line.line_index).unwrap();
-            let mut line_chars = cur_line_str.chars().enumerate().peekable();
-            let mut line_spans = 0;
-
-            // Assemble spans for this line
-            loop {
-                // Peek here to preserve the span if it doesn't belong to this line
-                let sp = match spans.peek() {
-                    Some(sp) => **sp,
-                    None => break,
-                };
-                let lo = self.cm.lookup_char_pos(sp.lo);
-                let hi = self.cm.lookup_char_pos(sp.hi);
-                let line_num = line.line_index + 1;
-
-                if !(lo.line <= line_num && hi.line >= line_num) {
-                    // This line is not contained in the span
-                    if overflowed {
-                        // Never elide the final line of an overflowed span
-                        prev_line_index = line.line_index - 1;
-                        overflowed = false;
-                        break;
-                    }
-
-                    if line_spans == 0 {
-                        continue 'l;
-                    } else {
-                        // This line is finished, now render the spans we've assembled
-                        break;
-                    }
-                }
-                spans.next();
-                line_spans += 1;
-
-                if lo.line != hi.line {
-                    // Assemble extra code lines to be emitted after this lines spans
-                    // (substract `2` because the first and last line are rendered normally)
-                    let max_lines = cmp::min(remaining_err_lines, MAX_SP_LINES) - 2;
-                    prev_line_index = line.line_index;
-                    let count = cmp::min((hi.line - lo.line - 1), max_lines);
-                    for _ in 0..count {
-                        let line = match lines.next() {
-                            Some(l) => l,
-                            None => break,
-                        };
-                        let line_str = fm.get_line(line.line_index).unwrap();
-                        overflowed_buf.push_str(&format!("{}:{:>width$} {}\n",
-                                                       fm.name,
-                                                       line.line_index + 1,
-                                                       line_str,
-                                                       width=digits));
-                        remaining_err_lines -= 1;
-                        prev_line_index += 1
-                    }
-                    // Remember that the span overflowed to ensure
-                    // that we emit its last line exactly once
-                    // (other spans may, or may not, start on it)
-                    overflowed = true;
-                    break;
-                }
-
-                for (pos, ch) in line_chars.by_ref() {
-                    lastc = ch;
-                    if pos >= lo.col.to_usize() { break; }
-                    // Whenever a tab occurs on the code line, we insert one on
-                    // the error-point-squiggly-line as well (instead of a space).
-                    // That way the squiggly line will usually appear in the correct
-                    // position.
-                    match ch {
-                        '\t' => {
-                            col += 8 - col%8;
-                            s.push('\t');
-                        },
-                        _ => {
-                            col += 1;
-                            s.push(' ');
-                        },
-                    }
-                }
-
-                s.push('^');
-                let col_ptr = col;
-                let count = match lastc {
-                    // Most terminals have a tab stop every eight columns by default
-                    '\t' => 8 - col%8,
-                    _ => 1,
-                };
-                col += count;
-                s.extend(::std::iter::repeat('~').take(count));
-
-                let hi = self.cm.lookup_char_pos(sp.hi);
-                if hi.col != lo.col {
-                    let mut chars = line_chars.by_ref();
-                    loop {
-                        // We peek here to preserve the value for the next span
-                        let (pos, ch) = match chars.peek() {
-                            Some(elem) => *elem,
-                            None => break,
-                        };
-                        if pos >= hi.col.to_usize() { break; }
-                        let count = match ch {
-                            '\t' => 8 - col%8,
-                            _ => 1,
-                        };
-                        col += count;
-                        s.extend(::std::iter::repeat('~').take(count));
-
-                        chars.next();
-                    }
-                }
-                if (col - col_ptr) > 0 {
-                    // One extra squiggly is replaced by a "^"
-                    s.pop();
-                }
-            }
-
-            // If we elided something put an ellipsis.
-            if prev_line_index != line.line_index.wrapping_sub(1) && !overflowed {
-                write!(&mut self.dst, "{0:1$}...\n", "", skip)?;
-            }
-
-            // Print offending code-line
-            remaining_err_lines -= 1;
-            write!(&mut self.dst, "{}:{:>width$} {}\n",
-                   fm.name,
-                   line.line_index + 1,
-                   cur_line_str,
-                   width=digits)?;
-
-            if s.len() > skip {
-                // Render the spans we assembled previously (if any).
-                println_maybe_styled!(&mut self.dst, term::Attr::ForegroundColor(lvl.color()),
-                                      "{}", s)?;
-            }
-
-            if !overflowed_buf.is_empty() {
-                // Print code-lines trailing the rendered spans (when a span overflows)
-                write!(&mut self.dst, "{}", &overflowed_buf)?;
-                overflowed_buf.clear();
-            } else {
-                prev_line_index = line.line_index;
-            }
-        }
-
-        // If we elided something, put an ellipsis.
-        if lines.next().is_some() {
-            write!(&mut self.dst, "{0:1$}...\n", "", skip)?;
-        }
-        Ok(())
-    }
-
-    /// Here are the differences between this and the normal `highlight_lines`:
-    /// `end_highlight_lines` will always put arrow on the last byte of each
-    /// span (instead of the first byte). Also, when a span is too long (more
-    /// than 6 lines), `end_highlight_lines` will print the first line, then
-    /// dot dot dot, then last line, whereas `highlight_lines` prints the first
-    /// six lines.
-    #[allow(deprecated)]
-    fn end_highlight_lines(&mut self,
-                           msp: &MultiSpan,
-                           lvl: Level)
-                          -> io::Result<()> {
-        let lines = match self.cm.span_to_lines(msp.to_span_bounds()) {
-            Ok(lines) => lines,
-            Err(_) => {
-                write!(&mut self.dst, "(internal compiler error: unprintable span)\n")?;
-                return Ok(());
-            }
-        };
-
-        let fm = &*lines.file;
-        if let None = fm.src {
-            return Ok(());
+        let mut snippet_data = SnippetData::new(self.cm.clone(),
+                                                msp.primary_span());
+        for span_label in msp.span_labels() {
+            snippet_data.push(span_label.span,
+                              span_label.is_primary,
+                              span_label.label);
         }
-
-        let lines = &lines.lines[..];
-
-        // Calculate the widest number to format evenly
-        let first_line = lines.first().unwrap();
-        let last_line = lines.last().unwrap();
-        let digits = line_num_max_digits(last_line);
-
-        let skip = fm.name.chars().count() + digits + 2;
-
-        let mut spans = msp.spans.iter().peekable();
-        let mut lines = lines.iter();
-        let mut prev_line_index = first_line.line_index.wrapping_sub(1);
-
-        // Display at most MAX_HIGHLIGHT_LINES lines.
-        let mut remaining_err_lines = MAX_HIGHLIGHT_LINES;
-
-        'l: loop {
-            if remaining_err_lines <= 0 {
-                break;
-            }
-            let line = match lines.next() {
-                Some(line) => line,
-                None => break,
-            };
-
-            // Skip is the number of characters we need to skip because they are
-            // part of the 'filename:line ' part of the previous line.
-            let mut s: String = ::std::iter::repeat(' ').take(skip).collect();
-
-            let line_str = fm.get_line(line.line_index).unwrap();
-            let mut line_chars = line_str.chars().enumerate();
-            let mut line_spans = 0;
-
-            loop {
-                // Peek here to preserve the span if it doesn't belong to this line
-                let sp = match spans.peek() {
-                    Some(sp) => **sp,
-                    None => break,
-                };
-                let lo = self.cm.lookup_char_pos(sp.lo);
-                let hi = self.cm.lookup_char_pos(sp.hi);
-                let elide_sp = (hi.line - lo.line) >= MAX_SP_LINES;
-
-                let line_num = line.line_index + 1;
-                if !(lo.line <= line_num && hi.line >= line_num) {
-                    // This line is not contained in the span
-                    if line_spans == 0 {
-                        continue 'l;
-                    } else {
-                        // This line is finished, now render the spans we've assembled
-                        break
-                    }
-                } else if hi.line > line_num {
-                    if elide_sp && lo.line < line_num {
-                        // This line is inbetween the first and last line of the span,
-                        // so we may want to elide it.
-                        continue 'l;
-                    } else {
-                        break
-                    }
-                }
-                line_spans += 1;
-                spans.next();
-
-                for (pos, ch) in line_chars.by_ref() {
-                    // Span seems to use half-opened interval, so subtract 1
-                    if pos >= hi.col.to_usize() - 1 { break; }
-                    // Whenever a tab occurs on the previous line, we insert one on
-                    // the error-point-squiggly-line as well (instead of a space).
-                    // That way the squiggly line will usually appear in the correct
-                    // position.
-                    match ch {
-                        '\t' => s.push('\t'),
-                        _ => s.push(' '),
-                    }
-                }
-                s.push('^');
+        let rendered_lines = snippet_data.render_lines();
+        for rendered_line in &rendered_lines {
+            for styled_string in &rendered_line.text {
+                self.dst.apply_style(lvl, &rendered_line.kind, styled_string.style)?;
+                write!(&mut self.dst, "{}", styled_string.text)?;
+                self.dst.reset_attrs()?;
             }
-
-            if prev_line_index != line.line_index.wrapping_sub(1) {
-                // If we elided something, put an ellipsis.
-                write!(&mut self.dst, "{0:1$}...\n", "", skip)?;
-            }
-
-            // Print offending code-lines
-            write!(&mut self.dst, "{}:{:>width$} {}\n", fm.name,
-                   line.line_index + 1, line_str, width=digits)?;
-            remaining_err_lines -= 1;
-
-            if s.len() > skip {
-                // Render the spans we assembled previously (if any)
-                println_maybe_styled!(&mut self.dst, term::Attr::ForegroundColor(lvl.color()),
-                                      "{}", s)?;
-            }
-            prev_line_index = line.line_index;
+            write!(&mut self.dst, "\n")?;
         }
         Ok(())
     }
@@ -602,6 +324,7 @@ fn line_num_max_digits(line: &codemap::LineInfo) -> usize {
     digits
 }
 
+
 fn print_diagnostic(dst: &mut Destination,
                     topic: &str,
                     lvl: Level,
@@ -609,17 +332,22 @@ fn print_diagnostic(dst: &mut Destination,
                     code: Option<&str>)
                     -> io::Result<()> {
     if !topic.is_empty() {
-        write!(dst, "{} ", topic)?;
-    }
-
-    print_maybe_styled!(dst, term::Attr::ForegroundColor(lvl.color()),
-                        "{}: ", lvl.to_string())?;
-    print_maybe_styled!(dst, term::Attr::Bold, "{}", msg)?;
-
+        dst.start_attr(term::Attr::ForegroundColor(lvl.color()))?;
+        write!(dst, "{}: ", topic)?;
+        dst.reset_attrs()?;
+    }
+    dst.start_attr(term::Attr::Bold)?;
+    dst.start_attr(term::Attr::ForegroundColor(lvl.color()))?;
+    write!(dst, "{}", lvl.to_string())?;
+    dst.reset_attrs()?;
+    write!(dst, ": ")?;
+    dst.start_attr(term::Attr::Bold)?;
+    write!(dst, "{}", msg)?;
     if let Some(code) = code {
         let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA);
         print_maybe_styled!(dst, style, " [{}]", code.clone())?;
     }
+    dst.reset_attrs()?;
     write!(dst, "\n")?;
     Ok(())
 }
@@ -660,6 +388,52 @@ impl Destination {
         }
     }
 
+    fn apply_style(&mut self,
+                   lvl: Level,
+                   _kind: &RenderedLineKind,
+                   style: Style)
+                   -> io::Result<()> {
+        match style {
+            Style::FileNameStyle => {
+            }
+            Style::LineAndColumn => {
+            }
+            Style::LineNumber => {
+                self.start_attr(term::Attr::Bold)?;
+                self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?;
+            }
+            Style::Quotation => {
+            }
+            Style::UnderlinePrimary | Style::LabelPrimary => {
+                self.start_attr(term::Attr::Bold)?;
+                self.start_attr(term::Attr::ForegroundColor(lvl.color()))?;
+            }
+            Style::UnderlineSecondary | Style::LabelSecondary => {
+                self.start_attr(term::Attr::Bold)?;
+                self.start_attr(term::Attr::ForegroundColor(term::color::BRIGHT_BLUE))?;
+            }
+            Style::NoStyle => {
+            }
+        }
+        Ok(())
+    }
+
+    fn start_attr(&mut self, attr: term::Attr) -> io::Result<()> {
+        match *self {
+            Terminal(ref mut t) => { t.attr(attr)?; }
+            Raw(_) => { }
+        }
+        Ok(())
+    }
+
+    fn reset_attrs(&mut self) -> io::Result<()> {
+        match *self {
+            Terminal(ref mut t) => { t.reset()?; }
+            Raw(_) => { }
+        }
+        Ok(())
+    }
+
     fn print_maybe_styled(&mut self,
                           args: fmt::Arguments,
                           color: term::Attr,
@@ -741,7 +515,7 @@ mod test {
     /// that this can span lines and so on.
     fn span_from_selection(input: &str, selection: &str) -> Span {
         assert_eq!(input.len(), selection.len());
-        let left_index = selection.find('^').unwrap() as u32;
+        let left_index = selection.find('~').unwrap() as u32;
         let right_index = selection.rfind('~').map(|x|x as u32).unwrap_or(left_index);
         Span { lo: BytePos(left_index), hi: BytePos(right_index + 1), expn_id: NO_EXPANSION }
     }
@@ -777,12 +551,15 @@ mod test {
         let vec = data.lock().unwrap().clone();
         let vec: &[u8] = &vec;
         let str = from_utf8(vec).unwrap();
-        println!("{}", str);
-        assert_eq!(str, "dummy.txt: 8         line8\n\
-                         dummy.txt: 9         line9\n\
-                         dummy.txt:10         line10\n\
-                         dummy.txt:11         e-lä-vän\n\
-                         dummy.txt:12         tolv\n");
+        println!("r#\"\n{}\"#", str);
+        assert_eq!(str, &r#"
+   --> dummy.txt:8:1
+8   |>         line8
+    |> ^^^^^^^^^^^^^
+...
+11  |>         e-lä-vän
+    |> ^^^^^^^^^^^^^^^^
+"#[1..]);
     }
 
     #[test]
@@ -790,7 +567,7 @@ mod test {
         // Test that a `MultiSpan` containing a single span splices a substition correctly
         let cm = CodeMap::new();
         let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
-        let selection = "     \n    ^~\n~~~\n~~~~~     \n   \n";
+        let selection = "     \n    ~~\n~~~\n~~~~~     \n   \n";
         cm.new_filemap_and_lines("blork.rs", inputtext);
         let sp = span_from_selection(inputtext, selection);
         let msp: MultiSpan = sp.into();
@@ -808,51 +585,25 @@ mod test {
     }
 
     #[test]
-    fn test_multiple_span_splice() {
-        // Test that a `MultiSpan` containing multiple spans splices substitions on
-        // several lines correctly
+    fn test_multi_span_splice() {
+        // Test that a `MultiSpan` containing multiple spans splices a substition correctly
         let cm = CodeMap::new();
-        let inp = "aaaaabbbbBB\nZZ\nZZ\nCCCDDDDDdddddeee";
-        let sp1 = "     ^~~~~~\n  \n  \n                ";
-        let sp2 = "           \n  \n  \n^~~~~~          ";
-        let sp3 = "           \n  \n  \n        ^~~     ";
-        let sp4 = "           \n  \n  \n           ^~~~ ";
-
-        let span_eq = |sp, eq| assert_eq!(&cm.span_to_snippet(sp).unwrap(), eq);
-
-        cm.new_filemap_and_lines("blork.rs", inp);
-        let sp1 = span_from_selection(inp, sp1);
-        let sp2 = span_from_selection(inp, sp2);
-        let sp3 = span_from_selection(inp, sp3);
-        let sp4 = span_from_selection(inp, sp4);
-        span_eq(sp1, "bbbbBB");
-        span_eq(sp2, "CCCDDD");
-        span_eq(sp3, "ddd");
-        span_eq(sp4, "ddee");
-
-        let substitutes: Vec<String> = ["1", "2", "3", "4"].iter().map(|x|x.to_string()).collect();
-        let expected = "aaaaa1\nZZ\nZZ\n2DD34e";
-
-        let test = |msp| {
-            let suggest = CodeSuggestion {
-                msp: msp,
-                substitutes: substitutes.clone(),
-            };
-            let actual = suggest.splice_lines(&cm);
-            assert_eq!(actual, expected);
+        let inputtext  = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
+        let selection1 = "     \n      \n   \n          \n ~ \n"; // intentionally out of order
+        let selection2 = "     \n    ~~\n~~~\n~~~~~     \n   \n";
+        cm.new_filemap_and_lines("blork.rs", inputtext);
+        let sp1 = span_from_selection(inputtext, selection1);
+        let sp2 = span_from_selection(inputtext, selection2);
+        let msp: MultiSpan = MultiSpan::from_spans(vec![sp1, sp2]);
+
+        let expected = "bbbbZZZZZZddddd\neXYZe";
+        let suggest = CodeSuggestion {
+            msp: msp,
+            substitutes: vec!["ZZZZZZ".to_owned(),
+                              "XYZ".to_owned()]
         };
-        test(MultiSpan { spans: vec![sp1, sp2, sp3, sp4] });
-
-        // Test ordering and merging by `MultiSpan::push`
-        let mut msp = MultiSpan::new();
-        msp.push_merge(sp2);
-        msp.push_merge(sp1);
-        assert_eq!(&msp.spans, &[sp1, sp2]);
-        msp.push_merge(sp4);
-        assert_eq!(&msp.spans, &[sp1, sp2, sp4]);
-        msp.push_merge(sp3);
-        assert_eq!(&msp.spans, &[sp1, sp2, sp3, sp4]);
-        test(msp);
+
+        assert_eq!(suggest.splice_lines(&cm), expected);
     }
 
     #[test]
@@ -862,17 +613,17 @@ mod test {
         let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone());
 
         let inp =       "_____aaaaaa____bbbbbb__cccccdd_";
-        let sp1 =       "     ^~~~~~                    ";
-        let sp2 =       "               ^~~~~~          ";
-        let sp3 =       "                       ^~~~~   ";
-        let sp4 =       "                          ^~~~ ";
-        let sp34 =      "                       ^~~~~~~ ";
-        let sp4_end =   "                            ^~ ";
-
-        let expect_start = "dummy.txt:1 _____aaaaaa____bbbbbb__cccccdd_\n\
-                         \x20                ^~~~~~    ^~~~~~  ^~~~~~~\n";
-        let expect_end =   "dummy.txt:1 _____aaaaaa____bbbbbb__cccccdd_\n\
-                         \x20                     ^         ^      ^ ^\n";
+        let sp1 =       "     ~~~~~~                    ";
+        let sp2 =       "               ~~~~~~          ";
+        let sp3 =       "                       ~~~~~   ";
+        let sp4 =       "                          ~~~~ ";
+        let sp34 =      "                       ~~~~~~~ ";
+
+        let expect_start = &r#"
+ --> dummy.txt:1:6
+1 |> _____aaaaaa____bbbbbb__cccccdd_
+  |>      ^^^^^^    ^^^^^^  ^^^^^^^
+"#[1..];
 
         let span = |sp, expected| {
             let sp = span_from_selection(inp, sp);
@@ -885,7 +636,6 @@ mod test {
         let sp3 = span(sp3, "ccccc");
         let sp4 = span(sp4, "ccdd");
         let sp34 = span(sp34, "cccccdd");
-        let sp4_end = span(sp4_end, "dd");
 
         let spans = vec![sp1, sp2, sp3, sp4];
 
@@ -894,26 +644,17 @@ mod test {
             highlight();
             let vec = data.lock().unwrap().clone();
             let actual = from_utf8(&vec[..]).unwrap();
+            println!("actual=\n{}", actual);
             assert_eq!(actual, expected);
         };
 
-        let msp = MultiSpan { spans: vec![sp1, sp2, sp34] };
-        let msp_end = MultiSpan { spans: vec![sp1, sp2, sp3, sp4_end] };
+        let msp = MultiSpan::from_spans(vec![sp1, sp2, sp34]);
         test(expect_start, &mut || {
             diag.highlight_lines(&msp, Level::Error).unwrap();
         });
-        test(expect_end, &mut || {
-            diag.end_highlight_lines(&msp_end, Level::Error).unwrap();
-        });
         test(expect_start, &mut || {
-            for msp in cm.group_spans(spans.clone()) {
-                diag.highlight_lines(&msp, Level::Error).unwrap();
-            }
-        });
-        test(expect_end, &mut || {
-            for msp in cm.end_group_spans(spans.clone()) {
-                diag.end_highlight_lines(&msp, Level::Error).unwrap();
-            }
+            let msp = MultiSpan::from_spans(spans.clone());
+            diag.highlight_lines(&msp, Level::Error).unwrap();
         });
     }
 
@@ -950,75 +691,31 @@ mod test {
         let sp4 = span(10, 10, (2, 3));
         let sp5 = span(10, 10, (4, 6));
 
-        let expect0 = "dummy.txt: 5 ccccc\n\
-                       dummy.txt: 6 xxxxx\n\
-                       dummy.txt: 7 yyyyy\n\
-                    \x20            ...\n\
-                       dummy.txt: 9 ddd__eee_\n\
-                    \x20            ^~~  ^~~\n\
-                    \x20            ...\n\
-                       dummy.txt:11 __f_gg\n\
-                    \x20              ^ ^~\n";
-
-        let expect = "dummy.txt: 1 aaaaa\n\
-                      dummy.txt: 2 aaaaa\n\
-                      dummy.txt: 3 aaaaa\n\
-                      dummy.txt: 4 bbbbb\n\
-                      dummy.txt: 5 ccccc\n\
-                      dummy.txt: 6 xxxxx\n\
-                   \x20            ...\n";
-
-        let expect_g1 = "dummy.txt:1 aaaaa\n\
-                         dummy.txt:2 aaaaa\n\
-                         dummy.txt:3 aaaaa\n\
-                         dummy.txt:4 bbbbb\n\
-                         dummy.txt:5 ccccc\n\
-                         dummy.txt:6 xxxxx\n\
-                      \x20           ...\n";
-
-        let expect2 = "dummy.txt: 9 ddd__eee_\n\
-                    \x20            ^~~  ^~~\n\
-                    \x20            ...\n\
-                       dummy.txt:11 __f_gg\n\
-                    \x20              ^ ^~\n";
-
-
-        let expect_end = "dummy.txt: 1 aaaaa\n\
-                       \x20            ...\n\
-                          dummy.txt: 7 yyyyy\n\
-                       \x20                ^\n\
-                       \x20            ...\n\
-                          dummy.txt: 9 ddd__eee_\n\
-                       \x20              ^    ^\n\
-                       \x20            ...\n\
-                          dummy.txt:11 __f_gg\n\
-                       \x20              ^  ^\n";
-
-        let expect0_end = "dummy.txt: 5 ccccc\n\
-                           dummy.txt: 6 xxxxx\n\
-                           dummy.txt: 7 yyyyy\n\
-                        \x20                ^\n\
-                        \x20            ...\n\
-                           dummy.txt: 9 ddd__eee_\n\
-                        \x20              ^    ^\n\
-                        \x20            ...\n\
-                           dummy.txt:11 __f_gg\n\
-                        \x20              ^  ^\n";
-
-        let expect_end_g1 = "dummy.txt:1 aaaaa\n\
-                          \x20           ...\n\
-                             dummy.txt:7 yyyyy\n\
-                          \x20               ^\n";
-
-        let expect2_end = "dummy.txt: 9 ddd__eee_\n\
-                        \x20              ^    ^\n\
-                        \x20            ...\n\
-                           dummy.txt:11 __f_gg\n\
-                        \x20              ^  ^\n";
-
-        let expect_groups = [expect2, expect_g1];
-        let expect_end_groups = [expect2_end, expect_end_g1];
-        let spans = vec![sp3, sp1, sp4, sp2, sp5];
+        let expect0 = &r#"
+   --> dummy.txt:5:1
+5   |> ccccc
+    |> ^^^^^
+...
+8   |> _____
+9   |> ddd__eee_
+    |> ^^^  ^^^
+10  |> elided
+11  |> __f_gg
+    |>   ^ ^^
+"#[1..];
+
+        let expect = &r#"
+   --> dummy.txt:1:1
+1   |> aaaaa
+    |> ^^^^^
+...
+8   |> _____
+9   |> ddd__eee_
+    |> ^^^  ^^^
+10  |> elided
+11  |> __f_gg
+    |>   ^ ^^
+"#[1..];
 
         macro_rules! test {
             ($expected: expr, $highlight: expr) => ({
@@ -1034,37 +731,14 @@ mod test {
             });
         }
 
-        let msp0 = MultiSpan { spans: vec![sp0, sp2, sp3, sp4, sp5] };
-        let msp = MultiSpan { spans: vec![sp1, sp2, sp3, sp4, sp5] };
-        let msp2 = MultiSpan { spans: vec![sp2, sp3, sp4, sp5] };
+        let msp0 = MultiSpan::from_spans(vec![sp0, sp2, sp3, sp4, sp5]);
+        let msp = MultiSpan::from_spans(vec![sp1, sp2, sp3, sp4, sp5]);
 
         test!(expect0, || {
             diag.highlight_lines(&msp0, Level::Error).unwrap();
         });
-        test!(expect0_end, || {
-            diag.end_highlight_lines(&msp0, Level::Error).unwrap();
-        });
         test!(expect, || {
             diag.highlight_lines(&msp, Level::Error).unwrap();
         });
-        test!(expect_end, || {
-            diag.end_highlight_lines(&msp, Level::Error).unwrap();
-        });
-        test!(expect2, || {
-            diag.highlight_lines(&msp2, Level::Error).unwrap();
-        });
-        test!(expect2_end, || {
-            diag.end_highlight_lines(&msp2, Level::Error).unwrap();
-        });
-        for (msp, expect) in cm.group_spans(spans.clone()).iter().zip(expect_groups.iter()) {
-            test!(expect, || {
-                diag.highlight_lines(&msp, Level::Error).unwrap();
-            });
-        }
-        for (msp, expect) in cm.group_spans(spans.clone()).iter().zip(expect_end_groups.iter()) {
-            test!(expect, || {
-                diag.end_highlight_lines(&msp, Level::Error).unwrap();
-            });
-        }
     }
 }
diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs
index 792828b3054..abbc4eef7bf 100644
--- a/src/libsyntax/errors/mod.rs
+++ b/src/libsyntax/errors/mod.rs
@@ -13,7 +13,7 @@ pub use errors::emitter::ColorConfig;
 use self::Level::*;
 use self::RenderSpan::*;
 
-use codemap::{self, CodeMap, MultiSpan};
+use codemap::{self, CodeMap, MultiSpan, NO_EXPANSION, Span};
 use diagnostics;
 use errors::emitter::{Emitter, EmitterWriter};
 
@@ -24,6 +24,7 @@ use term;
 
 pub mod emitter;
 pub mod json;
+pub mod snippet;
 
 #[derive(Clone)]
 pub enum RenderSpan {
@@ -32,13 +33,6 @@ pub enum RenderSpan {
     /// the source code covered by the span.
     FullSpan(MultiSpan),
 
-    /// Similar to a FullSpan, but the cited position is the end of
-    /// the span, instead of the start. Used, at least, for telling
-    /// compiletest/runtest to look at the last line of the span
-    /// (since `end_highlight_lines` displays an arrow to the end
-    /// of the span).
-    EndSpan(MultiSpan),
-
     /// A suggestion renders with both with an initial line for the
     /// message, prefixed by file:linenum, followed by a summary
     /// of hypothetical source code, where each `String` is spliced
@@ -61,7 +55,6 @@ impl RenderSpan {
         match *self {
             FullSpan(ref msp) |
             Suggestion(CodeSuggestion { ref msp, .. }) |
-            EndSpan(ref msp) |
             FileLine(ref msp) =>
                 msp
         }
@@ -88,12 +81,24 @@ impl CodeSuggestion {
                 }
             }
         }
-        let bounds = self.msp.to_span_bounds();
-        let lines = cm.span_to_lines(bounds).unwrap();
-        assert!(!lines.lines.is_empty());
 
-        // This isn't strictly necessary, but would in all likelyhood be an error
-        assert_eq!(self.msp.spans.len(), self.substitutes.len());
+        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!("");
+        }
+
+        // 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);
+
+        // 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 bounding_span = Span { lo: lo, hi: hi, expn_id: NO_EXPANSION };
+        let lines = cm.span_to_lines(bounding_span).unwrap();
+        assert!(!lines.lines.is_empty());
 
         // To build up the result, we do this for each span:
         // - push the line segment trailing the previous span
@@ -105,13 +110,13 @@ impl CodeSuggestion {
         //
         // Finally push the trailing line segment of the last span
         let fm = &lines.file;
-        let mut prev_hi = cm.lookup_char_pos(bounds.lo);
+        let mut prev_hi = cm.lookup_char_pos(bounding_span.lo);
         prev_hi.col = CharPos::from_usize(0);
 
         let mut prev_line = fm.get_line(lines.lines[0].line_index);
         let mut buf = String::new();
 
-        for (sp, substitute) in self.msp.spans.iter().zip(self.substitutes.iter()) {
+        for (sp, substitute) in primary_spans.iter().zip(self.substitutes.iter()) {
             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));
@@ -183,7 +188,7 @@ pub struct DiagnosticBuilder<'a> {
     level: Level,
     message: String,
     code: Option<String>,
-    span: Option<MultiSpan>,
+    span: MultiSpan,
     children: Vec<SubDiagnostic>,
 }
 
@@ -192,7 +197,7 @@ pub struct DiagnosticBuilder<'a> {
 struct SubDiagnostic {
     level: Level,
     message: String,
-    span: Option<MultiSpan>,
+    span: MultiSpan,
     render_span: Option<RenderSpan>,
 }
 
@@ -228,37 +233,61 @@ impl<'a> DiagnosticBuilder<'a> {
         self.level == Level::Fatal
     }
 
-    pub fn note(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Note, msg, None, None);
+    /// Add a span/label to be included in the resulting snippet.
+    /// This is pushed onto the `MultiSpan` that was created when the
+    /// diagnostic was first built. If you don't call this function at
+    /// 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)
+                      -> DiagnosticBuilder<'a> {
+        self.span.push_span_label(span, format!("{}", label));
+        self
+    }
+
+    pub fn note_expected_found(mut self,
+                               label: &fmt::Display,
+                               expected: &fmt::Display,
+                               found: &fmt::Display)
+                               -> DiagnosticBuilder<'a>
+    {
+        // For now, just attach these as notes
+        self.note(&format!("expected {} `{}`", label, expected));
+        self.note(&format!("   found {} `{}`", label, found));
+        self
+    }
+
+    pub fn note(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> {
+        self.sub(Level::Note, msg, MultiSpan::new(), None);
         self
     }
     pub fn span_note<S: Into<MultiSpan>>(&mut self,
                                          sp: S,
                                          msg: &str)
                                          -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Note, msg, Some(sp.into()), None);
+        self.sub(Level::Note, msg, sp.into(), None);
         self
     }
     pub fn warn(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Warning, msg, None, None);
+        self.sub(Level::Warning, msg, MultiSpan::new(), None);
         self
     }
     pub fn span_warn<S: Into<MultiSpan>>(&mut self,
                                          sp: S,
                                          msg: &str)
                                          -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Warning, msg, Some(sp.into()), None);
+        self.sub(Level::Warning, msg, sp.into(), None);
         self
     }
     pub fn help(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Help, msg, None, None);
+        self.sub(Level::Help, msg, MultiSpan::new(), None);
         self
     }
     pub fn span_help<S: Into<MultiSpan>>(&mut self,
                                          sp: S,
                                          msg: &str)
                                          -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Help, msg, Some(sp.into()), None);
+        self.sub(Level::Help, msg, sp.into(), None);
         self
     }
     /// Prints out a message with a suggested edit of the code.
@@ -269,43 +298,15 @@ impl<'a> DiagnosticBuilder<'a> {
                                                msg: &str,
                                                suggestion: String)
                                                -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Help, msg, None, Some(Suggestion(CodeSuggestion {
+        self.sub(Level::Help, msg, MultiSpan::new(), Some(Suggestion(CodeSuggestion {
             msp: sp.into(),
             substitutes: vec![suggestion],
         })));
         self
     }
-    pub fn span_end_note<S: Into<MultiSpan>>(&mut self,
-                                             sp: S,
-                                             msg: &str)
-                                             -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Note, msg, None, Some(EndSpan(sp.into())));
-        self
-    }
-    pub fn fileline_warn<S: Into<MultiSpan>>(&mut self,
-                                             sp: S,
-                                             msg: &str)
-                                             -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Warning, msg, None, Some(FileLine(sp.into())));
-        self
-    }
-    pub fn fileline_note<S: Into<MultiSpan>>(&mut self,
-                                             sp: S,
-                                             msg: &str)
-                                             -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Note, msg, None, Some(FileLine(sp.into())));
-        self
-    }
-    pub fn fileline_help<S: Into<MultiSpan>>(&mut self,
-                                             sp: S,
-                                             msg: &str)
-                                             -> &mut DiagnosticBuilder<'a> {
-        self.sub(Level::Help, msg, None, Some(FileLine(sp.into())));
-        self
-    }
 
-    pub fn span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
-        self.span = Some(sp.into());
+    pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
+        self.span = sp.into();
         self
     }
 
@@ -324,7 +325,7 @@ impl<'a> DiagnosticBuilder<'a> {
             level: level,
             message: message.to_owned(),
             code: None,
-            span: None,
+            span: MultiSpan::new(),
             children: vec![],
         }
     }
@@ -334,7 +335,7 @@ impl<'a> DiagnosticBuilder<'a> {
     fn sub(&mut self,
            level: Level,
            message: &str,
-           span: Option<MultiSpan>,
+           span: MultiSpan,
            render_span: Option<RenderSpan>) {
         let sub = SubDiagnostic {
             level: level,
@@ -357,7 +358,10 @@ impl<'a> fmt::Debug for DiagnosticBuilder<'a> {
 impl<'a> Drop for DiagnosticBuilder<'a> {
     fn drop(&mut self) {
         if !self.cancelled() {
-            self.emitter.borrow_mut().emit(None, "Error constructed but not emitted", None, Bug);
+            self.emitter.borrow_mut().emit(&MultiSpan::new(),
+                                           "Error constructed but not emitted",
+                                           None,
+                                           Bug);
             panic!();
         }
     }
@@ -412,7 +416,7 @@ impl Handler {
                                                     msg: &str)
                                                     -> DiagnosticBuilder<'a> {
         let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
-        result.span(sp);
+        result.set_span(sp);
         if !self.can_emit_warnings {
             result.cancel();
         }
@@ -424,7 +428,7 @@ impl Handler {
                                                               code: &str)
                                                               -> DiagnosticBuilder<'a> {
         let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
-        result.span(sp);
+        result.set_span(sp);
         result.code(code.to_owned());
         if !self.can_emit_warnings {
             result.cancel();
@@ -444,7 +448,7 @@ impl Handler {
                                                    -> DiagnosticBuilder<'a> {
         self.bump_err_count();
         let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg);
-        result.span(sp);
+        result.set_span(sp);
         result
     }
     pub fn struct_span_err_with_code<'a, S: Into<MultiSpan>>(&'a self,
@@ -454,7 +458,7 @@ impl Handler {
                                                              -> DiagnosticBuilder<'a> {
         self.bump_err_count();
         let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg);
-        result.span(sp);
+        result.set_span(sp);
         result.code(code.to_owned());
         result
     }
@@ -468,7 +472,7 @@ impl Handler {
                                                      -> DiagnosticBuilder<'a> {
         self.bump_err_count();
         let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg);
-        result.span(sp);
+        result.set_span(sp);
         result
     }
     pub fn struct_span_fatal_with_code<'a, S: Into<MultiSpan>>(&'a self,
@@ -478,7 +482,7 @@ impl Handler {
                                                                -> DiagnosticBuilder<'a> {
         self.bump_err_count();
         let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg);
-        result.span(sp);
+        result.set_span(sp);
         result.code(code.to_owned());
         result
     }
@@ -499,7 +503,7 @@ impl Handler {
         if self.treat_err_as_bug {
             self.span_bug(sp, msg);
         }
-        self.emit(Some(&sp.into()), msg, Fatal);
+        self.emit(&sp.into(), msg, Fatal);
         self.bump_err_count();
         return FatalError;
     }
@@ -508,7 +512,7 @@ impl Handler {
         if self.treat_err_as_bug {
             self.span_bug(sp, msg);
         }
-        self.emit_with_code(Some(&sp.into()), msg, code, Fatal);
+        self.emit_with_code(&sp.into(), msg, code, Fatal);
         self.bump_err_count();
         return FatalError;
     }
@@ -516,24 +520,24 @@ impl Handler {
         if self.treat_err_as_bug {
             self.span_bug(sp, msg);
         }
-        self.emit(Some(&sp.into()), msg, Error);
+        self.emit(&sp.into(), msg, Error);
         self.bump_err_count();
     }
     pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
         if self.treat_err_as_bug {
             self.span_bug(sp, msg);
         }
-        self.emit_with_code(Some(&sp.into()), msg, code, Error);
+        self.emit_with_code(&sp.into(), msg, code, Error);
         self.bump_err_count();
     }
     pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
-        self.emit(Some(&sp.into()), msg, Warning);
+        self.emit(&sp.into(), msg, Warning);
     }
     pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: &str) {
-        self.emit_with_code(Some(&sp.into()), msg, code, Warning);
+        self.emit_with_code(&sp.into(), msg, code, Warning);
     }
     pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
-        self.emit(Some(&sp.into()), msg, Bug);
+        self.emit(&sp.into(), msg, Bug);
         panic!(ExplicitBug);
     }
     pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
@@ -541,11 +545,11 @@ impl Handler {
         *delayed = Some((sp.into(), msg.to_string()));
     }
     pub fn span_bug_no_panic<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
-        self.emit(Some(&sp.into()), msg, Bug);
+        self.emit(&sp.into(), msg, Bug);
         self.bump_err_count();
     }
     pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
-        self.emit.borrow_mut().emit(Some(&sp.into()), msg, None, Note);
+        self.emit.borrow_mut().emit(&sp.into(), msg, None, Note);
     }
     pub fn span_unimpl<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
         self.span_bug(sp, &format!("unimplemented {}", msg));
@@ -554,7 +558,7 @@ impl Handler {
         if self.treat_err_as_bug {
             self.bug(msg);
         }
-        self.emit.borrow_mut().emit(None, msg, None, Fatal);
+        self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Fatal);
         self.bump_err_count();
         FatalError
     }
@@ -562,17 +566,17 @@ impl Handler {
         if self.treat_err_as_bug {
             self.bug(msg);
         }
-        self.emit.borrow_mut().emit(None, msg, None, Error);
+        self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Error);
         self.bump_err_count();
     }
     pub fn warn(&self, msg: &str) {
-        self.emit.borrow_mut().emit(None, msg, None, Warning);
+        self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Warning);
     }
     pub fn note_without_error(&self, msg: &str) {
-        self.emit.borrow_mut().emit(None, msg, None, Note);
+        self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Note);
     }
     pub fn bug(&self, msg: &str) -> ! {
-        self.emit.borrow_mut().emit(None, msg, None, Bug);
+        self.emit.borrow_mut().emit(&MultiSpan::new(), msg, None, Bug);
         panic!(ExplicitBug);
     }
     pub fn unimpl(&self, msg: &str) -> ! {
@@ -614,25 +618,20 @@ impl Handler {
         panic!(self.fatal(&s));
     }
     pub fn emit(&self,
-                msp: Option<&MultiSpan>,
+                msp: &MultiSpan,
                 msg: &str,
                 lvl: Level) {
         if lvl == Warning && !self.can_emit_warnings { return }
-        self.emit.borrow_mut().emit(msp, msg, None, lvl);
+        self.emit.borrow_mut().emit(&msp, msg, None, lvl);
         if !self.continue_after_error.get() { self.abort_if_errors(); }
     }
     pub fn emit_with_code(&self,
-                          msp: Option<&MultiSpan>,
+                          msp: &MultiSpan,
                           msg: &str,
                           code: &str,
                           lvl: Level) {
         if lvl == Warning && !self.can_emit_warnings { return }
-        self.emit.borrow_mut().emit(msp, msg, Some(code), lvl);
-        if !self.continue_after_error.get() { self.abort_if_errors(); }
-    }
-    pub fn custom_emit(&self, rsp: RenderSpan, msg: &str, lvl: Level) {
-        if lvl == Warning && !self.can_emit_warnings { return }
-        self.emit.borrow_mut().custom_emit(&rsp, msg, lvl);
+        self.emit.borrow_mut().emit(&msp, msg, Some(code), lvl);
         if !self.continue_after_error.get() { self.abort_if_errors(); }
     }
 }