about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/attr.rs28
-rw-r--r--src/libsyntax/config.rs18
-rw-r--r--src/libsyntax/errors/emitter.rs (renamed from src/libsyntax/diagnostic.rs)678
-rw-r--r--src/libsyntax/errors/mod.rs338
-rw-r--r--src/libsyntax/ext/base.rs2
-rw-r--r--src/libsyntax/ext/expand.rs2
-rw-r--r--src/libsyntax/ext/tt/transcribe.rs8
-rw-r--r--src/libsyntax/feature_gate.rs24
-rw-r--r--src/libsyntax/lib.rs5
-rw-r--r--src/libsyntax/parse/lexer/comments.rs4
-rw-r--r--src/libsyntax/parse/lexer/mod.rs89
-rw-r--r--src/libsyntax/parse/mod.rs27
-rw-r--r--src/libsyntax/parse/obsolete.rs3
-rw-r--r--src/libsyntax/parse/parser.rs13
-rw-r--r--src/libsyntax/print/pprust.rs6
-rw-r--r--src/libsyntax/show_span.rs6
-rw-r--r--src/libsyntax/test.rs12
17 files changed, 703 insertions, 560 deletions
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index fcc7351deaa..26662605ba8 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -21,7 +21,7 @@ use ast::{Expr, Item, Local, Decl};
 use codemap::{Span, Spanned, spanned, dummy_spanned};
 use codemap::BytePos;
 use config::CfgDiag;
-use diagnostic::SpanHandler;
+use errors::Handler;
 use feature_gate::{GatedCfg, GatedCfgAttr};
 use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
 use parse::token::InternedString;
@@ -299,14 +299,14 @@ pub fn find_crate_name(attrs: &[Attribute]) -> Option<InternedString> {
 }
 
 /// Find the value of #[export_name=*] attribute and check its validity.
-pub fn find_export_name_attr(diag: &SpanHandler, attrs: &[Attribute]) -> Option<InternedString> {
+pub fn find_export_name_attr(diag: &Handler, attrs: &[Attribute]) -> Option<InternedString> {
     attrs.iter().fold(None, |ia,attr| {
         if attr.check_name("export_name") {
             if let s@Some(_) = attr.value_str() {
                 s
             } else {
                 diag.span_err(attr.span, "export_name attribute has invalid format");
-                diag.handler.help("use #[export_name=\"*\"]");
+                diag.help("use #[export_name=\"*\"]");
                 None
             }
         } else {
@@ -324,7 +324,7 @@ pub enum InlineAttr {
 }
 
 /// Determine what `#[inline]` attribute is present in `attrs`, if any.
-pub fn find_inline_attr(diagnostic: Option<&SpanHandler>, attrs: &[Attribute]) -> InlineAttr {
+pub fn find_inline_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> InlineAttr {
     attrs.iter().fold(InlineAttr::None, |ia,attr| {
         match attr.node.value.node {
             MetaWord(ref n) if *n == "inline" => {
@@ -426,7 +426,7 @@ impl StabilityLevel {
     pub fn is_stable(&self) -> bool { if let Stable {..} = *self { true } else { false }}
 }
 
-fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
+fn find_stability_generic<'a, I>(diagnostic: &Handler,
                                  attrs_iter: I,
                                  item_sp: Span)
                                  -> Option<Stability>
@@ -612,10 +612,10 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
     stab
 }
 
-fn find_deprecation_generic<'a, I>(diagnostic: &SpanHandler,
-                                 attrs_iter: I,
-                                 item_sp: Span)
-                                 -> Option<Deprecation>
+fn find_deprecation_generic<'a, I>(diagnostic: &Handler,
+                                   attrs_iter: I,
+                                   item_sp: Span)
+                                   -> Option<Deprecation>
     where I: Iterator<Item = &'a Attribute>
 {
     let mut depr: Option<Deprecation> = None;
@@ -672,18 +672,18 @@ fn find_deprecation_generic<'a, I>(diagnostic: &SpanHandler,
 }
 
 /// Find the first stability attribute. `None` if none exists.
-pub fn find_stability(diagnostic: &SpanHandler, attrs: &[Attribute],
+pub fn find_stability(diagnostic: &Handler, attrs: &[Attribute],
                       item_sp: Span) -> Option<Stability> {
     find_stability_generic(diagnostic, attrs.iter(), item_sp)
 }
 
 /// Find the deprecation attribute. `None` if none exists.
-pub fn find_deprecation(diagnostic: &SpanHandler, attrs: &[Attribute],
-                      item_sp: Span) -> Option<Deprecation> {
+pub fn find_deprecation(diagnostic: &Handler, attrs: &[Attribute],
+                        item_sp: Span) -> Option<Deprecation> {
     find_deprecation_generic(diagnostic, attrs.iter(), item_sp)
 }
 
-pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[P<MetaItem>]) {
+pub fn require_unique_names(diagnostic: &Handler, metas: &[P<MetaItem>]) {
     let mut set = HashSet::new();
     for meta in metas {
         let name = meta.name();
@@ -702,7 +702,7 @@ pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[P<MetaItem>]) {
 /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
 /// the same discriminant size that the corresponding C enum would or C
 /// structure layout, and `packed` to remove padding.
-pub fn find_repr_attrs(diagnostic: &SpanHandler, attr: &Attribute) -> Vec<ReprAttr> {
+pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr> {
     let mut acc = Vec::new();
     match attr.node.value.node {
         ast::MetaList(ref s, ref items) if *s == "repr" => {
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index 1209c58fd5e..64b16538f05 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use attr::AttrMetaMethods;
-use diagnostic::SpanHandler;
+use errors::Handler;
 use feature_gate::GatedCfgAttr;
 use fold::Folder;
 use {ast, fold, attr};
@@ -23,12 +23,12 @@ use util::small_vector::SmallVector;
 /// configuration.
 struct Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool {
     in_cfg: F,
-    diagnostic: &'a SpanHandler,
+    diagnostic: &'a Handler,
 }
 
 // Support conditional compilation by transforming the AST, stripping out
 // any items that do not belong in the current configuration
-pub fn strip_unconfigured_items(diagnostic: &SpanHandler, krate: ast::Crate,
+pub fn strip_unconfigured_items(diagnostic: &Handler, krate: ast::Crate,
                                 feature_gated_cfgs: &mut Vec<GatedCfgAttr>)
                                 -> ast::Crate
 {
@@ -83,7 +83,7 @@ impl<'a, F> fold::Folder for Context<'a, F> where F: FnMut(&[ast::Attribute]) ->
     }
 }
 
-pub fn strip_items<'a, F>(diagnostic: &'a SpanHandler,
+pub fn strip_items<'a, F>(diagnostic: &'a Handler,
                           krate: ast::Crate, in_cfg: F) -> ast::Crate where
     F: FnMut(&[ast::Attribute]) -> bool,
 {
@@ -291,7 +291,7 @@ struct CfgAttrFolder<'a, T> {
 }
 
 // Process `#[cfg_attr]`.
-fn process_cfg_attr(diagnostic: &SpanHandler, krate: ast::Crate,
+fn process_cfg_attr(diagnostic: &Handler, krate: ast::Crate,
                     feature_gated_cfgs: &mut Vec<GatedCfgAttr>) -> ast::Crate {
     let mut fld = CfgAttrFolder {
         diag: CfgDiagReal {
@@ -463,17 +463,17 @@ impl<'v, 'a, 'b> visit::Visitor<'v> for StmtExprAttrFeatureVisitor<'a, 'b> {
 }
 
 pub trait CfgDiag {
-    fn emit_error<F>(&mut self, f: F) where F: FnMut(&SpanHandler);
+    fn emit_error<F>(&mut self, f: F) where F: FnMut(&Handler);
     fn flag_gated<F>(&mut self, f: F) where F: FnMut(&mut Vec<GatedCfgAttr>);
 }
 
 pub struct CfgDiagReal<'a, 'b> {
-    pub diag: &'a SpanHandler,
+    pub diag: &'a Handler,
     pub feature_gated_cfgs: &'b mut Vec<GatedCfgAttr>,
 }
 
 impl<'a, 'b> CfgDiag for CfgDiagReal<'a, 'b> {
-    fn emit_error<F>(&mut self, mut f: F) where F: FnMut(&SpanHandler) {
+    fn emit_error<F>(&mut self, mut f: F) where F: FnMut(&Handler) {
         f(self.diag)
     }
     fn flag_gated<F>(&mut self, mut f: F) where F: FnMut(&mut Vec<GatedCfgAttr>) {
@@ -486,7 +486,7 @@ struct CfgDiagSilent {
 }
 
 impl CfgDiag for CfgDiagSilent {
-    fn emit_error<F>(&mut self, _: F) where F: FnMut(&SpanHandler) {
+    fn emit_error<F>(&mut self, _: F) where F: FnMut(&Handler) {
         self.error = true;
     }
     fn flag_gated<F>(&mut self, _: F) where F: FnMut(&mut Vec<GatedCfgAttr>) {}
diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/errors/emitter.rs
index b854a2f2a0a..7fef85a833e 100644
--- a/src/libsyntax/diagnostic.rs
+++ b/src/libsyntax/errors/emitter.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -8,423 +8,156 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub use self::Level::*;
-pub use self::RenderSpan::*;
-pub use self::ColorConfig::*;
 use self::Destination::*;
 
 use codemap::{self, COMMAND_LINE_SP, COMMAND_LINE_EXPN, Pos, Span};
 use diagnostics;
 
-use std::cell::{RefCell, Cell};
-use std::{cmp, error, fmt};
+use errors::{Level, RenderSpan};
+use errors::RenderSpan::*;
+use errors::Level::*;
+
+use std::{cmp, fmt};
 use std::io::prelude::*;
 use std::io;
+use std::rc::Rc;
 use term;
 
-/// maximum number of lines we will print for each error; arbitrary.
-const MAX_LINES: usize = 6;
 
-#[derive(Clone)]
-pub enum RenderSpan {
-    /// A FullSpan renders with both with an initial line for the
-    /// message, prefixed by file:linenum, followed by a summary of
-    /// the source code covered by the span.
-    FullSpan(Span),
-
-    /// 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(Span),
-
-    /// 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 the `String` is spliced
-    /// into the lines in place of the code covered by the span.
-    Suggestion(Span, String),
-
-    /// A FileLine renders with just a line for the message prefixed
-    /// by file:linenum.
-    FileLine(Span),
+pub trait Emitter {
+    fn emit(&mut self, span: Option<Span>, msg: &str, code: Option<&str>, lvl: Level);
+    fn custom_emit(&mut self, sp: RenderSpan, msg: &str, lvl: Level);
 }
 
-impl RenderSpan {
-    fn span(&self) -> Span {
-        match *self {
-            FullSpan(s) |
-            Suggestion(s, _) |
-            EndSpan(s) |
-            FileLine(s) =>
-                s
-        }
-    }
-}
+/// maximum number of lines we will print for each error; arbitrary.
+const MAX_LINES: usize = 6;
 
 #[derive(Clone, Copy)]
 pub enum ColorConfig {
     Auto,
     Always,
-    Never
+    Never,
 }
 
-pub trait Emitter {
-    fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, Span)>,
-            msg: &str, code: Option<&str>, lvl: Level);
-    fn custom_emit(&mut self, cm: &codemap::CodeMap,
-                   sp: RenderSpan, msg: &str, lvl: Level);
-}
-
-/// Used as a return value to signify a fatal error occurred. (It is also
-/// used as the argument to panic at the moment, but that will eventually
-/// not be true.)
-#[derive(Copy, Clone, Debug)]
-#[must_use]
-pub struct FatalError;
-
-impl fmt::Display for FatalError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-        write!(f, "parser fatal error")
+impl ColorConfig {
+    fn use_color(&self) -> bool {
+        match *self {
+            ColorConfig::Always => true,
+            ColorConfig::Never  => false,
+            ColorConfig::Auto   => stderr_isatty(),
+        }
     }
 }
 
-impl error::Error for FatalError {
-    fn description(&self) -> &str {
-        "The parser has encountered a fatal error"
-    }
+// A basic emitter for when we don't have access to a codemap or registry. Used
+// for reporting very early errors, etc.
+pub struct BasicEmitter {
+    dst: Destination,
 }
 
-/// Signifies that the compiler died with an explicit call to `.bug`
-/// or `.span_bug` rather than a failed assertion, etc.
-#[derive(Copy, Clone, Debug)]
-pub struct ExplicitBug;
-
-impl fmt::Display for ExplicitBug {
-    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-        write!(f, "parser internal bug")
-    }
-}
+impl Emitter for BasicEmitter {
+    fn emit(&mut self,
+            sp: Option<Span>,
+            msg: &str,
+            code: Option<&str>,
+            lvl: Level) {
+        assert!(sp.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);
+        }
 
-impl error::Error for ExplicitBug {
-    fn description(&self) -> &str {
-        "The parser has encountered an internal bug"
     }
-}
 
-/// A span-handler is like a handler but also
-/// accepts span information for source-location
-/// reporting.
-pub struct SpanHandler {
-    pub handler: Handler,
-    pub cm: codemap::CodeMap,
-}
-
-impl SpanHandler {
-    pub fn new(handler: Handler, cm: codemap::CodeMap) -> SpanHandler {
-        SpanHandler {
-            handler: handler,
-            cm: cm,
-        }
+    fn custom_emit(&mut self, _: RenderSpan, _: &str, _: Level) {
+        panic!("BasicEmitter can't handle custom_emit");
     }
-    pub fn span_fatal(&self, sp: Span, msg: &str) -> FatalError {
-        self.handler.emit(Some((&self.cm, sp)), msg, Fatal);
-        return FatalError;
-    }
-    pub fn span_fatal_with_code(&self, sp: Span, msg: &str, code: &str) -> FatalError {
-        self.handler.emit_with_code(Some((&self.cm, sp)), msg, code, Fatal);
-        return FatalError;
-    }
-    pub fn span_err(&self, sp: Span, msg: &str) {
-        self.handler.emit(Some((&self.cm, sp)), msg, Error);
-        self.handler.bump_err_count();
-    }
-    pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) {
-        self.handler.emit_with_code(Some((&self.cm, sp)), msg, code, Error);
-        self.handler.bump_err_count();
-    }
-    pub fn span_warn(&self, sp: Span, msg: &str) {
-        self.handler.emit(Some((&self.cm, sp)), msg, Warning);
-    }
-    pub fn span_warn_with_code(&self, sp: Span, msg: &str, code: &str) {
-        self.handler.emit_with_code(Some((&self.cm, sp)), msg, code, Warning);
-    }
-    pub fn span_note(&self, sp: Span, msg: &str) {
-        self.handler.emit(Some((&self.cm, sp)), msg, Note);
-    }
-    pub fn span_end_note(&self, sp: Span, msg: &str) {
-        self.handler.custom_emit(&self.cm, EndSpan(sp), msg, Note);
-    }
-    pub fn span_help(&self, sp: Span, msg: &str) {
-        self.handler.emit(Some((&self.cm, sp)), msg, Help);
-    }
-    /// Prints out a message with a suggested edit of the code.
-    ///
-    /// See `diagnostic::RenderSpan::Suggestion` for more information.
-    pub fn span_suggestion(&self, sp: Span, msg: &str, suggestion: String) {
-        self.handler.custom_emit(&self.cm, Suggestion(sp, suggestion), msg, Help);
-    }
-    pub fn fileline_note(&self, sp: Span, msg: &str) {
-        self.handler.custom_emit(&self.cm, FileLine(sp), msg, Note);
-    }
-    pub fn fileline_help(&self, sp: Span, msg: &str) {
-        self.handler.custom_emit(&self.cm, FileLine(sp), msg, Help);
-    }
-    pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
-        self.handler.emit(Some((&self.cm, sp)), msg, Bug);
-        panic!(ExplicitBug);
-    }
-    pub fn span_bug_no_panic(&self, sp: Span, msg: &str) {
-        self.handler.emit(Some((&self.cm, sp)), msg, Bug);
-        self.handler.bump_err_count();
-    }
-    pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! {
-        self.span_bug(sp, &format!("unimplemented {}", msg));
-    }
-    pub fn handler<'a>(&'a self) -> &'a Handler {
-        &self.handler
-    }
-}
-
-/// A handler deals with errors; certain errors
-/// (fatal, bug, unimpl) may cause immediate exit,
-/// others log errors for later reporting.
-pub struct Handler {
-    err_count: Cell<usize>,
-    emit: RefCell<Box<Emitter + Send>>,
-    pub can_emit_warnings: bool
 }
 
-impl Handler {
-    pub fn new(color_config: ColorConfig,
-               registry: Option<diagnostics::registry::Registry>,
-               can_emit_warnings: bool) -> Handler {
-        let emitter = Box::new(EmitterWriter::stderr(color_config, registry));
-        Handler::with_emitter(can_emit_warnings, emitter)
-    }
-    pub fn with_emitter(can_emit_warnings: bool, e: Box<Emitter + Send>) -> Handler {
-        Handler {
-            err_count: Cell::new(0),
-            emit: RefCell::new(e),
-            can_emit_warnings: can_emit_warnings
-        }
-    }
-    pub fn fatal(&self, msg: &str) -> FatalError {
-        self.emit.borrow_mut().emit(None, msg, None, Fatal);
-        FatalError
-    }
-    pub fn err(&self, msg: &str) {
-        self.emit.borrow_mut().emit(None, msg, None, Error);
-        self.bump_err_count();
-    }
-    pub fn bump_err_count(&self) {
-        self.err_count.set(self.err_count.get() + 1);
-    }
-    pub fn err_count(&self) -> usize {
-        self.err_count.get()
-    }
-    pub fn has_errors(&self) -> bool {
-        self.err_count.get() > 0
-    }
-    pub fn abort_if_errors(&self) {
-        let s;
-        match self.err_count.get() {
-            0 => return,
-            1 => s = "aborting due to previous error".to_string(),
-            _  => {
-                s = format!("aborting due to {} previous errors",
-                            self.err_count.get());
-            }
+impl BasicEmitter {
+    pub fn stderr(color_config: ColorConfig) -> BasicEmitter {
+        if color_config.use_color() {
+            let dst = Destination::from_stderr();
+            BasicEmitter { dst: dst }
+        } else {
+            BasicEmitter { dst: Raw(Box::new(io::stderr())) }
         }
-
-        panic!(self.fatal(&s[..]));
-    }
-    pub fn warn(&self, msg: &str) {
-        self.emit.borrow_mut().emit(None, msg, None, Warning);
-    }
-    pub fn note(&self, msg: &str) {
-        self.emit.borrow_mut().emit(None, msg, None, Note);
-    }
-    pub fn help(&self, msg: &str) {
-        self.emit.borrow_mut().emit(None, msg, None, Help);
-    }
-    pub fn bug(&self, msg: &str) -> ! {
-        self.emit.borrow_mut().emit(None, msg, None, Bug);
-        panic!(ExplicitBug);
-    }
-    pub fn unimpl(&self, msg: &str) -> ! {
-        self.bug(&format!("unimplemented {}", msg));
-    }
-    pub fn emit(&self,
-                cmsp: Option<(&codemap::CodeMap, Span)>,
-                msg: &str,
-                lvl: Level) {
-        if lvl == Warning && !self.can_emit_warnings { return }
-        self.emit.borrow_mut().emit(cmsp, msg, None, lvl);
-    }
-    pub fn emit_with_code(&self,
-                          cmsp: Option<(&codemap::CodeMap, Span)>,
-                          msg: &str,
-                          code: &str,
-                          lvl: Level) {
-        if lvl == Warning && !self.can_emit_warnings { return }
-        self.emit.borrow_mut().emit(cmsp, msg, Some(code), lvl);
-    }
-    pub fn custom_emit(&self, cm: &codemap::CodeMap,
-                       sp: RenderSpan, msg: &str, lvl: Level) {
-        if lvl == Warning && !self.can_emit_warnings { return }
-        self.emit.borrow_mut().custom_emit(cm, sp, msg, lvl);
     }
 }
 
-#[derive(Copy, PartialEq, Clone, Debug)]
-pub enum Level {
-    Bug,
-    Fatal,
-    Error,
-    Warning,
-    Note,
-    Help,
+pub struct EmitterWriter {
+    dst: Destination,
+    registry: Option<diagnostics::registry::Registry>,
+    cm: Rc<codemap::CodeMap>,
 }
 
-impl fmt::Display for Level {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use std::fmt::Display;
+impl Emitter for EmitterWriter {
+    fn emit(&mut self,
+            sp: Option<Span>,
+            msg: &str,
+            code: Option<&str>,
+            lvl: Level) {
+        let error = match sp {
+            Some(COMMAND_LINE_SP) => self.emit_(FileLine(COMMAND_LINE_SP), msg, code, lvl),
+            Some(sp) => self.emit_(FullSpan(sp), msg, code, lvl),
+            None => print_diagnostic(&mut self.dst, "", lvl, msg, code),
+        };
 
-        match *self {
-            Bug => "error: internal compiler error".fmt(f),
-            Fatal | Error => "error".fmt(f),
-            Warning => "warning".fmt(f),
-            Note => "note".fmt(f),
-            Help => "help".fmt(f),
+        if let Err(e) = error {
+            panic!("failed to print diagnostics: {:?}", e);
         }
     }
-}
 
-impl Level {
-    fn color(self) -> term::color::Color {
-        match self {
-            Bug | Fatal | Error => term::color::BRIGHT_RED,
-            Warning => term::color::BRIGHT_YELLOW,
-            Note => term::color::BRIGHT_GREEN,
-            Help => term::color::BRIGHT_CYAN,
+    fn custom_emit(&mut self,
+                   sp: RenderSpan,
+                   msg: &str,
+                   lvl: Level) {
+        match self.emit_(sp, msg, None, lvl) {
+            Ok(()) => {}
+            Err(e) => panic!("failed to print diagnostics: {:?}", e),
         }
     }
 }
 
-pub struct EmitterWriter {
-    dst: Destination,
-    registry: Option<diagnostics::registry::Registry>
-}
-
-enum Destination {
-    Terminal(Box<term::StderrTerminal>),
-    Raw(Box<Write + Send>),
-}
-
 /// 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 {
-    ($writer: expr, $style: expr, $($arg: tt)*) => {
-        $writer.print_maybe_styled(format_args!($($arg)*), $style, false)
+    ($dst: expr, $style: expr, $($arg: tt)*) => {
+        $dst.print_maybe_styled(format_args!($($arg)*), $style, false)
     }
 }
 
 macro_rules! println_maybe_styled {
-    ($writer: expr, $style: expr, $($arg: tt)*) => {
-        $writer.print_maybe_styled(format_args!($($arg)*), $style, true)
+    ($dst: expr, $style: expr, $($arg: tt)*) => {
+        $dst.print_maybe_styled(format_args!($($arg)*), $style, true)
     }
 }
 
 impl EmitterWriter {
     pub fn stderr(color_config: ColorConfig,
-                  registry: Option<diagnostics::registry::Registry>) -> EmitterWriter {
-        let stderr = io::stderr();
-
-        let use_color = match color_config {
-            Always => true,
-            Never  => false,
-            Auto   => stderr_isatty(),
-        };
-
-        if use_color {
-            let dst = match term::stderr() {
-                Some(t) => Terminal(t),
-                None    => Raw(Box::new(stderr)),
-            };
-            EmitterWriter { dst: dst, registry: registry }
+                  registry: Option<diagnostics::registry::Registry>,
+                  code_map: Rc<codemap::CodeMap>)
+                  -> EmitterWriter {
+        if color_config.use_color() {
+            let dst = Destination::from_stderr();
+            EmitterWriter { dst: dst, registry: registry, cm: code_map }
         } else {
-            EmitterWriter { dst: Raw(Box::new(stderr)), registry: registry }
+            EmitterWriter { dst: Raw(Box::new(io::stderr())), registry: registry, cm: code_map }
         }
     }
 
     pub fn new(dst: Box<Write + Send>,
-               registry: Option<diagnostics::registry::Registry>) -> EmitterWriter {
-        EmitterWriter { dst: Raw(dst), registry: registry }
-    }
-
-    fn print_maybe_styled(&mut self,
-                          args: fmt::Arguments,
-                          color: term::Attr,
-                          print_newline_at_end: bool) -> io::Result<()> {
-        match self.dst {
-            Terminal(ref mut t) => {
-                try!(t.attr(color));
-                // If `msg` ends in a newline, we need to reset the color before
-                // the newline. We're making the assumption that we end up writing
-                // to a `LineBufferedWriter`, which means that emitting the reset
-                // after the newline ends up buffering the reset until we print
-                // another line or exit. Buffering the reset is a problem if we're
-                // sharing the terminal with any other programs (e.g. other rustc
-                // instances via `make -jN`).
-                //
-                // Note that if `msg` contains any internal newlines, this will
-                // result in the `LineBufferedWriter` flushing twice instead of
-                // once, which still leaves the opportunity for interleaved output
-                // to be miscolored. We assume this is rare enough that we don't
-                // have to worry about it.
-                try!(t.write_fmt(args));
-                try!(t.reset());
-                if print_newline_at_end {
-                    t.write_all(b"\n")
-                } else {
-                    Ok(())
-                }
-            }
-            Raw(ref mut w) => {
-                try!(w.write_fmt(args));
-                if print_newline_at_end {
-                    w.write_all(b"\n")
-                } else {
-                    Ok(())
-                }
-            }
-        }
-    }
-
-    fn print_diagnostic(&mut self, topic: &str, lvl: Level,
-                        msg: &str, code: Option<&str>) -> io::Result<()> {
-        if !topic.is_empty() {
-            try!(write!(&mut self.dst, "{} ", topic));
-        }
-
-        try!(print_maybe_styled!(self, term::Attr::ForegroundColor(lvl.color()),
-                                 "{}: ", lvl.to_string()));
-        try!(print_maybe_styled!(self, term::Attr::Bold, "{}", msg));
-
-        match code {
-            Some(code) => {
-                let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA);
-                try!(print_maybe_styled!(self, style, " [{}]", code.clone()));
-            }
-            None => ()
-        }
-        try!(write!(&mut self.dst, "\n"));
-        Ok(())
+               registry: Option<diagnostics::registry::Registry>,
+               code_map: Rc<codemap::CodeMap>)
+               -> EmitterWriter {
+        EmitterWriter { dst: Raw(dst), registry: registry, cm: code_map }
     }
 
-    fn emit_(&mut self, cm: &codemap::CodeMap, rsp: RenderSpan,
-             msg: &str, code: Option<&str>, lvl: Level) -> io::Result<()> {
+    fn emit_(&mut self,
+             rsp: RenderSpan,
+             msg: &str,
+             code: Option<&str>,
+             lvl: Level)
+             -> io::Result<()> {
         let sp = rsp.span();
 
         // We cannot check equality directly with COMMAND_LINE_SP
@@ -433,25 +166,27 @@ impl EmitterWriter {
             "<command line option>".to_string()
         } else if let EndSpan(_) = rsp {
             let span_end = Span { lo: sp.hi, hi: sp.hi, expn_id: sp.expn_id};
-            cm.span_to_string(span_end)
+            self.cm.span_to_string(span_end)
         } else {
-            cm.span_to_string(sp)
+            self.cm.span_to_string(sp)
         };
 
-        try!(self.print_diagnostic(&ss[..], lvl, msg, code));
+        try!(print_diagnostic(&mut self.dst, &ss[..], lvl, msg, code));
 
         match rsp {
             FullSpan(_) => {
-                try!(self.highlight_lines(cm, sp, lvl, cm.span_to_lines(sp)));
-                try!(self.print_macro_backtrace(cm, sp));
+                let lines = self.cm.span_to_lines(sp);
+                try!(self.highlight_lines(sp, lvl, lines));
+                try!(self.print_macro_backtrace(sp));
             }
             EndSpan(_) => {
-                try!(self.end_highlight_lines(cm, sp, lvl, cm.span_to_lines(sp)));
-                try!(self.print_macro_backtrace(cm, sp));
+                let lines = self.cm.span_to_lines(sp);
+                try!(self.end_highlight_lines(sp, lvl, lines));
+                try!(self.print_macro_backtrace(sp));
             }
             Suggestion(_, ref suggestion) => {
-                try!(self.highlight_suggestion(cm, sp, suggestion));
-                try!(self.print_macro_backtrace(cm, sp));
+                try!(self.highlight_suggestion(sp, suggestion));
+                try!(self.print_macro_backtrace(sp));
             }
             FileLine(..) => {
                 // no source text in this case!
@@ -462,9 +197,9 @@ impl EmitterWriter {
             Some(code) =>
                 match self.registry.as_ref().and_then(|registry| registry.find_description(code)) {
                     Some(_) => {
-                        try!(self.print_diagnostic(&ss[..], Help,
-                                                   &format!("run `rustc --explain {}` to see a \
-                                                             detailed explanation", code), None));
+                        try!(print_diagnostic(&mut self.dst, &ss[..], Help,
+                                              &format!("run `rustc --explain {}` to see a \
+                                                       detailed explanation", code), None));
                     }
                     None => ()
                 },
@@ -474,12 +209,11 @@ impl EmitterWriter {
     }
 
     fn highlight_suggestion(&mut self,
-                            cm: &codemap::CodeMap,
                             sp: Span,
                             suggestion: &str)
                             -> io::Result<()>
     {
-        let lines = cm.span_to_lines(sp).unwrap();
+        let lines = self.cm.span_to_lines(sp).unwrap();
         assert!(!lines.lines.is_empty());
 
         // To build up the result, we want to take the snippet from the first
@@ -521,7 +255,6 @@ impl EmitterWriter {
     }
 
     fn highlight_lines(&mut self,
-                       cm: &codemap::CodeMap,
                        sp: Span,
                        lvl: Level,
                        lines: codemap::FileLinesResult)
@@ -581,7 +314,7 @@ impl EmitterWriter {
         // FIXME (#3260)
         // If there's one line at fault we can easily point to the problem
         if lines.lines.len() == 1 {
-            let lo = cm.lookup_char_pos(sp.lo);
+            let lo = self.cm.lookup_char_pos(sp.lo);
             let mut digits = 0;
             let mut num = (lines.lines[0].line_index + 1) / 10;
 
@@ -628,7 +361,7 @@ impl EmitterWriter {
                 col += count;
                 s.extend(::std::iter::repeat('~').take(count));
 
-                let hi = cm.lookup_char_pos(sp.hi);
+                let hi = self.cm.lookup_char_pos(sp.hi);
                 if hi.col != lo.col {
                     for (pos, ch) in iter {
                         if pos >= hi.col.to_usize() { break; }
@@ -646,7 +379,7 @@ impl EmitterWriter {
                     s.pop();
                 }
 
-                try!(println_maybe_styled!(self, term::Attr::ForegroundColor(lvl.color()),
+                try!(println_maybe_styled!(&mut self.dst, term::Attr::ForegroundColor(lvl.color()),
                                            "{}", s));
             }
         }
@@ -661,7 +394,6 @@ impl EmitterWriter {
     /// six lines.
     #[allow(deprecated)]
     fn end_highlight_lines(&mut self,
-                           cm: &codemap::CodeMap,
                            sp: Span,
                            lvl: Level,
                            lines: codemap::FileLinesResult)
@@ -697,7 +429,7 @@ impl EmitterWriter {
             }
         }
         let last_line_start = format!("{}:{} ", fm.name, lines[lines.len()-1].line_index + 1);
-        let hi = cm.lookup_char_pos(sp.hi);
+        let hi = self.cm.lookup_char_pos(sp.hi);
         let skip = last_line_start.chars().count();
         let mut s = String::new();
         for _ in 0..skip {
@@ -719,55 +451,82 @@ impl EmitterWriter {
             }
         }
         s.push('^');
-        println_maybe_styled!(self, term::Attr::ForegroundColor(lvl.color()),
+        println_maybe_styled!(&mut self.dst, term::Attr::ForegroundColor(lvl.color()),
                               "{}", s)
     }
 
     fn print_macro_backtrace(&mut self,
-                             cm: &codemap::CodeMap,
                              sp: Span)
                              -> io::Result<()> {
         let mut last_span = codemap::DUMMY_SP;
-        let mut sp_opt = Some(sp);
-
-        while let Some(sp) = sp_opt {
-            sp_opt = try!(cm.with_expn_info(sp.expn_id, |expn_info| -> io::Result<_> {
-                match expn_info {
-                    Some(ei) => {
-                        let (pre, post) = match ei.callee.format {
-                            codemap::MacroAttribute(..) => ("#[", "]"),
-                            codemap::MacroBang(..) => ("", "!"),
-                        };
-                        // Don't print recursive invocations
-                        if ei.call_site != last_span {
-                            last_span = ei.call_site;
-
-                            let mut diag_string = format!("in this expansion of {}{}{}",
-                                                          pre,
-                                                          ei.callee.name(),
-                                                          post);
-
-                            if let Some(def_site_span) = ei.callee.span {
-                                diag_string.push_str(&format!(" (defined in {})",
-                                                              cm.span_to_filename(def_site_span)));
-                            }
-
-                            try!(self.print_diagnostic(&cm.span_to_string(ei.call_site),
-                                                       Note,
-                                                       &diag_string,
-                                                       None));
-                        }
-                        Ok(Some(ei.call_site))
-                    }
-                    None => Ok(None)
+        let mut span = sp;
+
+        loop {
+            let span_name_span = self.cm.with_expn_info(span.expn_id, |expn_info| {
+                expn_info.map(|ei| {
+                    let (pre, post) = match ei.callee.format {
+                        codemap::MacroAttribute(..) => ("#[", "]"),
+                        codemap::MacroBang(..) => ("", "!"),
+                    };
+                    let macro_decl_name = format!("in this expansion of {}{}{}",
+                                                  pre,
+                                                  ei.callee.name(),
+                                                  post);
+                    let def_site_span = ei.callee.span;
+                    (ei.call_site, macro_decl_name, def_site_span)
+                })
+            });
+            let (macro_decl_name, def_site_span) = match span_name_span {
+                None => break,
+                Some((sp, macro_decl_name, def_site_span)) => {
+                    span = sp;
+                    (macro_decl_name, def_site_span)
                 }
-            }));
+            };
+
+            // Don't print recursive invocations
+            if span != last_span {
+                let mut diag_string = macro_decl_name;
+                if let Some(def_site_span) = def_site_span {
+                    diag_string.push_str(&format!(" (defined in {})",
+                                                  self.cm.span_to_filename(def_site_span)));
+                }
+
+                let snippet = self.cm.span_to_string(span);
+                try!(print_diagnostic(&mut self.dst, &snippet, Note, &diag_string, None));
+            }
+            last_span = span;
         }
 
         Ok(())
     }
 }
 
+fn print_diagnostic(dst: &mut Destination,
+                    topic: &str,
+                    lvl: Level,
+                    msg: &str,
+                    code: Option<&str>)
+                    -> io::Result<()> {
+    if !topic.is_empty() {
+        try!(write!(dst, "{} ", topic));
+    }
+
+    try!(print_maybe_styled!(dst, term::Attr::ForegroundColor(lvl.color()),
+                             "{}: ", lvl.to_string()));
+    try!(print_maybe_styled!(dst, term::Attr::Bold, "{}", msg));
+
+    match code {
+        Some(code) => {
+            let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA);
+            try!(print_maybe_styled!(dst, style, " [{}]", code.clone()));
+        }
+        None => ()
+    }
+    try!(write!(dst, "\n"));
+    Ok(())
+}
+
 #[cfg(unix)]
 fn stderr_isatty() -> bool {
     use libc;
@@ -791,6 +550,60 @@ fn stderr_isatty() -> bool {
     }
 }
 
+enum Destination {
+    Terminal(Box<term::StderrTerminal>),
+    Raw(Box<Write + Send>),
+}
+
+impl Destination {
+    fn from_stderr() -> Destination {
+        match term::stderr() {
+            Some(t) => Terminal(t),
+            None    => Raw(Box::new(io::stderr())),
+        }
+    }
+
+    fn print_maybe_styled(&mut self,
+                          args: fmt::Arguments,
+                          color: term::Attr,
+                          print_newline_at_end: bool)
+                          -> io::Result<()> {
+        match *self {
+            Terminal(ref mut t) => {
+                try!(t.attr(color));
+                // If `msg` ends in a newline, we need to reset the color before
+                // the newline. We're making the assumption that we end up writing
+                // to a `LineBufferedWriter`, which means that emitting the reset
+                // after the newline ends up buffering the reset until we print
+                // another line or exit. Buffering the reset is a problem if we're
+                // sharing the terminal with any other programs (e.g. other rustc
+                // instances via `make -jN`).
+                //
+                // Note that if `msg` contains any internal newlines, this will
+                // result in the `LineBufferedWriter` flushing twice instead of
+                // once, which still leaves the opportunity for interleaved output
+                // to be miscolored. We assume this is rare enough that we don't
+                // have to worry about it.
+                try!(t.write_fmt(args));
+                try!(t.reset());
+                if print_newline_at_end {
+                    t.write_all(b"\n")
+                } else {
+                    Ok(())
+                }
+            }
+            Raw(ref mut w) => {
+                try!(w.write_fmt(args));
+                if print_newline_at_end {
+                    w.write_all(b"\n")
+                } else {
+                    Ok(())
+                }
+            }
+        }
+    }
+}
+
 impl Write for Destination {
     fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
         match *self {
@@ -806,49 +619,16 @@ impl Write for Destination {
     }
 }
 
-impl Emitter for EmitterWriter {
-    fn emit(&mut self,
-            cmsp: Option<(&codemap::CodeMap, Span)>,
-            msg: &str, code: Option<&str>, lvl: Level) {
-        let error = match cmsp {
-            Some((cm, COMMAND_LINE_SP)) => self.emit_(cm,
-                                                FileLine(COMMAND_LINE_SP),
-                                                msg, code, lvl),
-            Some((cm, sp)) => self.emit_(cm, FullSpan(sp), msg, code, lvl),
-            None => self.print_diagnostic("", lvl, msg, code),
-        };
-
-        match error {
-            Ok(()) => {}
-            Err(e) => panic!("failed to print diagnostics: {:?}", e),
-        }
-    }
-
-    fn custom_emit(&mut self, cm: &codemap::CodeMap,
-                   sp: RenderSpan, msg: &str, lvl: Level) {
-        match self.emit_(cm, sp, msg, None, lvl) {
-            Ok(()) => {}
-            Err(e) => panic!("failed to print diagnostics: {:?}", e),
-        }
-    }
-}
-
-pub fn expect<T, M>(diag: &SpanHandler, opt: Option<T>, msg: M) -> T where
-    M: FnOnce() -> String,
-{
-    match opt {
-        Some(t) => t,
-        None => diag.handler().bug(&msg()),
-    }
-}
 
 #[cfg(test)]
 mod test {
-    use super::{EmitterWriter, Level};
+    use errors::Level;
+    use super::EmitterWriter;
     use codemap::{mk_sp, CodeMap};
     use std::sync::{Arc, Mutex};
     use std::io::{self, Write};
     use std::str::from_utf8;
+    use std::rc::Rc;
 
     // Diagnostic doesn't align properly in span where line number increases by one digit
     #[test]
@@ -861,8 +641,8 @@ mod test {
             fn flush(&mut self) -> io::Result<()> { Ok(()) }
         }
         let data = Arc::new(Mutex::new(Vec::new()));
-        let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())), None);
-        let cm = CodeMap::new();
+        let cm = Rc::new(CodeMap::new());
+        let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone());
         let content = "abcdefg
         koksi
         line3
@@ -885,7 +665,7 @@ mod test {
         println!("span_to_lines");
         let lines = cm.span_to_lines(sp);
         println!("highlight_lines");
-        ew.highlight_lines(&cm, sp, lvl, lines).unwrap();
+        ew.highlight_lines(sp, lvl, lines).unwrap();
         println!("done");
         let vec = data.lock().unwrap().clone();
         let vec: &[u8] = &vec;
diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs
new file mode 100644
index 00000000000..f2e61090ba2
--- /dev/null
+++ b/src/libsyntax/errors/mod.rs
@@ -0,0 +1,338 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub use errors::emitter::ColorConfig;
+
+use self::Level::*;
+use self::RenderSpan::*;
+
+use codemap::{self, Span};
+use diagnostics;
+use errors::emitter::{Emitter, EmitterWriter};
+
+use std::cell::{RefCell, Cell};
+use std::{error, fmt};
+use std::io::prelude::*;
+use std::rc::Rc;
+use term;
+
+pub mod emitter;
+
+#[derive(Clone)]
+pub enum RenderSpan {
+    /// A FullSpan renders with both with an initial line for the
+    /// message, prefixed by file:linenum, followed by a summary of
+    /// the source code covered by the span.
+    FullSpan(Span),
+
+    /// 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(Span),
+
+    /// 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 the `String` is spliced
+    /// into the lines in place of the code covered by the span.
+    Suggestion(Span, String),
+
+    /// A FileLine renders with just a line for the message prefixed
+    /// by file:linenum.
+    FileLine(Span),
+}
+
+impl RenderSpan {
+    fn span(&self) -> Span {
+        match *self {
+            FullSpan(s) |
+            Suggestion(s, _) |
+            EndSpan(s) |
+            FileLine(s) =>
+                s
+        }
+    }
+}
+
+/// Used as a return value to signify a fatal error occurred. (It is also
+/// used as the argument to panic at the moment, but that will eventually
+/// not be true.)
+#[derive(Copy, Clone, Debug)]
+#[must_use]
+pub struct FatalError;
+
+impl fmt::Display for FatalError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        write!(f, "parser fatal error")
+    }
+}
+
+impl error::Error for FatalError {
+    fn description(&self) -> &str {
+        "The parser has encountered a fatal error"
+    }
+}
+
+/// Signifies that the compiler died with an explicit call to `.bug`
+/// or `.span_bug` rather than a failed assertion, etc.
+#[derive(Copy, Clone, Debug)]
+pub struct ExplicitBug;
+
+impl fmt::Display for ExplicitBug {
+    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        write!(f, "parser internal bug")
+    }
+}
+
+impl error::Error for ExplicitBug {
+    fn description(&self) -> &str {
+        "The parser has encountered an internal bug"
+    }
+}
+
+/// A handler deals with errors; certain errors
+/// (fatal, bug, unimpl) may cause immediate exit,
+/// others log errors for later reporting.
+pub struct Handler {
+    err_count: Cell<usize>,
+    emit: RefCell<Box<Emitter>>,
+    pub can_emit_warnings: bool,
+    treat_err_as_bug: bool,
+    delayed_span_bug: RefCell<Option<(codemap::Span, String)>>,
+}
+
+impl Handler {
+    pub fn new(color_config: ColorConfig,
+               registry: Option<diagnostics::registry::Registry>,
+               can_emit_warnings: bool,
+               treat_err_as_bug: bool,
+               cm: Rc<codemap::CodeMap>)
+               -> Handler {
+        let emitter = Box::new(EmitterWriter::stderr(color_config, registry, cm));
+        Handler::with_emitter(can_emit_warnings, treat_err_as_bug, emitter)
+    }
+
+    pub fn with_emitter(can_emit_warnings: bool,
+                        treat_err_as_bug: bool,
+                        e: Box<Emitter>) -> Handler {
+        Handler {
+            err_count: Cell::new(0),
+            emit: RefCell::new(e),
+            can_emit_warnings: can_emit_warnings,
+            treat_err_as_bug: treat_err_as_bug,
+            delayed_span_bug: RefCell::new(None),
+        }
+    }
+
+    pub fn span_fatal(&self, sp: Span, msg: &str) -> FatalError {
+        if self.treat_err_as_bug {
+            self.span_bug(sp, msg);
+        }
+        self.emit(Some(sp), msg, Fatal);
+        return FatalError;
+    }
+    pub fn span_fatal_with_code(&self, sp: Span, msg: &str, code: &str) -> FatalError {
+        if self.treat_err_as_bug {
+            self.span_bug(sp, msg);
+        }
+        self.emit_with_code(Some(sp), msg, code, Fatal);
+        return FatalError;
+    }
+    pub fn span_err(&self, sp: Span, msg: &str) {
+        if self.treat_err_as_bug {
+            self.span_bug(sp, msg);
+        }
+        self.emit(Some(sp), msg, Error);
+        self.bump_err_count();
+    }
+    pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) {
+        if self.treat_err_as_bug {
+            self.span_bug(sp, msg);
+        }
+        self.emit_with_code(Some(sp), msg, code, Error);
+        self.bump_err_count();
+    }
+    pub fn span_warn(&self, sp: Span, msg: &str) {
+        self.emit(Some(sp), msg, Warning);
+    }
+    pub fn span_warn_with_code(&self, sp: Span, msg: &str, code: &str) {
+        self.emit_with_code(Some(sp), msg, code, Warning);
+    }
+    pub fn span_note(&self, sp: Span, msg: &str) {
+        self.emit(Some(sp), msg, Note);
+    }
+    pub fn span_end_note(&self, sp: Span, msg: &str) {
+        self.custom_emit(EndSpan(sp), msg, Note);
+    }
+    pub fn span_help(&self, sp: Span, msg: &str) {
+        self.emit(Some(sp), msg, Help);
+    }
+    /// Prints out a message with a suggested edit of the code.
+    ///
+    /// See `diagnostic::RenderSpan::Suggestion` for more information.
+    pub fn span_suggestion(&self, sp: Span, msg: &str, suggestion: String) {
+        self.custom_emit(Suggestion(sp, suggestion), msg, Help);
+    }
+    pub fn fileline_note(&self, sp: Span, msg: &str) {
+        self.custom_emit(FileLine(sp), msg, Note);
+    }
+    pub fn fileline_help(&self, sp: Span, msg: &str) {
+        self.custom_emit(FileLine(sp), msg, Help);
+    }
+    pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
+        self.emit(Some(sp), msg, Bug);
+        panic!(ExplicitBug);
+    }
+    pub fn delay_span_bug(&self, sp: Span, msg: &str) {
+        let mut delayed = self.delayed_span_bug.borrow_mut();
+        *delayed = Some((sp, msg.to_string()));
+    }
+    pub fn span_bug_no_panic(&self, sp: Span, msg: &str) {
+        self.emit(Some(sp), msg, Bug);
+        self.bump_err_count();
+    }
+    pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! {
+        self.span_bug(sp, &format!("unimplemented {}", msg));
+    }
+    pub fn fatal(&self, msg: &str) -> FatalError {
+        if self.treat_err_as_bug {
+            self.bug(msg);
+        }
+        self.emit.borrow_mut().emit(None, msg, None, Fatal);
+        FatalError
+    }
+    pub fn err(&self, msg: &str) {
+        if self.treat_err_as_bug {
+            self.bug(msg);
+        }
+        self.emit.borrow_mut().emit(None, msg, None, Error);
+        self.bump_err_count();
+    }
+    pub fn warn(&self, msg: &str) {
+        self.emit.borrow_mut().emit(None, msg, None, Warning);
+    }
+    pub fn note(&self, msg: &str) {
+        self.emit.borrow_mut().emit(None, msg, None, Note);
+    }
+    pub fn help(&self, msg: &str) {
+        self.emit.borrow_mut().emit(None, msg, None, Help);
+    }
+    pub fn bug(&self, msg: &str) -> ! {
+        self.emit.borrow_mut().emit(None, msg, None, Bug);
+        panic!(ExplicitBug);
+    }
+    pub fn unimpl(&self, msg: &str) -> ! {
+        self.bug(&format!("unimplemented {}", msg));
+    }
+
+    pub fn bump_err_count(&self) {
+        self.err_count.set(self.err_count.get() + 1);
+    }
+
+    pub fn err_count(&self) -> usize {
+        self.err_count.get()
+    }
+
+    pub fn has_errors(&self) -> bool {
+        self.err_count.get() > 0
+    }
+
+    pub fn abort_if_errors(&self) {
+        let s;
+        match self.err_count.get() {
+            0 => {
+                let delayed_bug = self.delayed_span_bug.borrow();
+                match *delayed_bug {
+                    Some((span, ref errmsg)) => {
+                        self.span_bug(span, errmsg);
+                    },
+                    _ => {}
+                }
+
+                return;
+            }
+            1 => s = "aborting due to previous error".to_string(),
+            _  => {
+                s = format!("aborting due to {} previous errors",
+                            self.err_count.get());
+            }
+        }
+
+        panic!(self.fatal(&s[..]));
+    }
+
+    pub fn emit(&self,
+                sp: Option<Span>,
+                msg: &str,
+                lvl: Level) {
+        if lvl == Warning && !self.can_emit_warnings { return }
+        self.emit.borrow_mut().emit(sp, msg, None, lvl);
+    }
+
+    pub fn emit_with_code(&self,
+                          sp: Option<Span>,
+                          msg: &str,
+                          code: &str,
+                          lvl: Level) {
+        if lvl == Warning && !self.can_emit_warnings { return }
+        self.emit.borrow_mut().emit(sp, msg, Some(code), lvl);
+    }
+
+    pub fn custom_emit(&self, sp: RenderSpan, msg: &str, lvl: Level) {
+        if lvl == Warning && !self.can_emit_warnings { return }
+        self.emit.borrow_mut().custom_emit(sp, msg, lvl);
+    }
+}
+
+
+#[derive(Copy, PartialEq, Clone, Debug)]
+pub enum Level {
+    Bug,
+    Fatal,
+    Error,
+    Warning,
+    Note,
+    Help,
+}
+
+impl fmt::Display for Level {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use std::fmt::Display;
+
+        match *self {
+            Bug => "error: internal compiler error".fmt(f),
+            Fatal | Error => "error".fmt(f),
+            Warning => "warning".fmt(f),
+            Note => "note".fmt(f),
+            Help => "help".fmt(f),
+        }
+    }
+}
+
+impl Level {
+    fn color(self) -> term::color::Color {
+        match self {
+            Bug | Fatal | Error => term::color::BRIGHT_RED,
+            Warning => term::color::BRIGHT_YELLOW,
+            Note => term::color::BRIGHT_GREEN,
+            Help => term::color::BRIGHT_CYAN,
+        }
+    }
+}
+
+pub fn expect<T, M>(diag: &Handler, opt: Option<T>, msg: M) -> T where
+    M: FnOnce() -> String,
+{
+    match opt {
+        Some(t) => t,
+        None => diag.bug(&msg()),
+    }
+}
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 9d62e407cb9..f198460137d 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -720,7 +720,7 @@ impl<'a> ExtCtxt<'a> {
         self.parse_sess.span_diagnostic.fileline_help(sp, msg);
     }
     pub fn bug(&self, msg: &str) -> ! {
-        self.parse_sess.span_diagnostic.handler().bug(msg);
+        self.parse_sess.span_diagnostic.bug(msg);
     }
     pub fn trace_macros(&self) -> bool {
         self.ecfg.trace_mac
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 02d318e5b53..743bcda18de 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -1301,7 +1301,7 @@ pub fn expand_crate(mut cx: ExtCtxt,
 
         let mut ret = expander.fold_crate(c);
         ret.exported_macros = expander.cx.exported_macros.clone();
-        cx.parse_sess.span_diagnostic.handler().abort_if_errors();
+        cx.parse_sess.span_diagnostic.abort_if_errors();
         ret
     };
     return (ret, cx.syntax_env.names);
diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs
index d5993c81f24..8878c606d6a 100644
--- a/src/libsyntax/ext/tt/transcribe.rs
+++ b/src/libsyntax/ext/tt/transcribe.rs
@@ -12,7 +12,7 @@ use self::LockstepIterSize::*;
 use ast;
 use ast::{TokenTree, Ident, Name};
 use codemap::{Span, DUMMY_SP};
-use diagnostic::SpanHandler;
+use errors::Handler;
 use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
 use parse::token::{DocComment, MatchNt, SubstNt};
 use parse::token::{Token, NtIdent, SpecialMacroVar};
@@ -34,7 +34,7 @@ struct TtFrame {
 
 #[derive(Clone)]
 pub struct TtReader<'a> {
-    pub sp_diag: &'a SpanHandler,
+    pub sp_diag: &'a Handler,
     /// the unzipped tree:
     stack: Vec<TtFrame>,
     /* for MBE-style macro transcription */
@@ -55,7 +55,7 @@ pub struct TtReader<'a> {
 /// This can do Macro-By-Example transcription. On the other hand, if
 /// `src` contains no `TokenTree::Sequence`s, `MatchNt`s or `SubstNt`s, `interp` can
 /// (and should) be None.
-pub fn new_tt_reader<'a>(sp_diag: &'a SpanHandler,
+pub fn new_tt_reader<'a>(sp_diag: &'a Handler,
                          interp: Option<HashMap<Name, Rc<NamedMatch>>>,
                          imported_from: Option<Ident>,
                          src: Vec<ast::TokenTree>)
@@ -69,7 +69,7 @@ pub fn new_tt_reader<'a>(sp_diag: &'a SpanHandler,
 /// This can do Macro-By-Example transcription. On the other hand, if
 /// `src` contains no `TokenTree::Sequence`s, `MatchNt`s or `SubstNt`s, `interp` can
 /// (and should) be None.
-pub fn new_tt_reader_with_doc_flag<'a>(sp_diag: &'a SpanHandler,
+pub fn new_tt_reader_with_doc_flag<'a>(sp_diag: &'a Handler,
                                        interp: Option<HashMap<Name, Rc<NamedMatch>>>,
                                        imported_from: Option<Ident>,
                                        src: Vec<ast::TokenTree>,
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index f186aff6d36..89f318d0945 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -32,7 +32,7 @@ use ast;
 use attr;
 use attr::AttrMetaMethods;
 use codemap::{CodeMap, Span};
-use diagnostic::SpanHandler;
+use errors::Handler;
 use visit;
 use visit::{FnKind, Visitor};
 use parse::token::InternedString;
@@ -446,7 +446,7 @@ impl PartialOrd for GatedCfgAttr {
 }
 
 impl GatedCfgAttr {
-    pub fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) {
+    pub fn check_and_emit(&self, diagnostic: &Handler, features: &Features) {
         match *self {
             GatedCfgAttr::GatedCfg(ref cfg) => {
                 cfg.check_and_emit(diagnostic, features);
@@ -476,7 +476,7 @@ impl GatedCfg {
                       }
                   })
     }
-    fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) {
+    fn check_and_emit(&self, diagnostic: &Handler, features: &Features) {
         let (cfg, feature, has_feature) = GATED_CFGS[self.index];
         if !has_feature(features) {
             let explain = format!("`cfg({})` is experimental and subject to change", cfg);
@@ -595,21 +595,21 @@ const EXPLAIN_PUSHPOP_UNSAFE: &'static str =
 const EXPLAIN_STMT_ATTR_SYNTAX: &'static str =
     "attributes on non-item statements and expressions are experimental.";
 
-pub fn check_for_box_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) {
+pub fn check_for_box_syntax(f: Option<&Features>, diag: &Handler, span: Span) {
     if let Some(&Features { allow_box: true, .. }) = f {
         return;
     }
     emit_feature_err(diag, "box_syntax", span, GateIssue::Language, EXPLAIN_BOX_SYNTAX);
 }
 
-pub fn check_for_placement_in(f: Option<&Features>, diag: &SpanHandler, span: Span) {
+pub fn check_for_placement_in(f: Option<&Features>, diag: &Handler, span: Span) {
     if let Some(&Features { allow_placement_in: true, .. }) = f {
         return;
     }
     emit_feature_err(diag, "placement_in_syntax", span, GateIssue::Language, EXPLAIN_PLACEMENT_IN);
 }
 
-pub fn check_for_pushpop_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) {
+pub fn check_for_pushpop_syntax(f: Option<&Features>, diag: &Handler, span: Span) {
     if let Some(&Features { allow_pushpop_unsafe: true, .. }) = f {
         return;
     }
@@ -618,7 +618,7 @@ pub fn check_for_pushpop_syntax(f: Option<&Features>, diag: &SpanHandler, span:
 
 struct Context<'a> {
     features: Vec<&'static str>,
-    span_handler: &'a SpanHandler,
+    span_handler: &'a Handler,
     cm: &'a CodeMap,
     plugin_attributes: &'a [(String, AttributeType)],
 }
@@ -704,7 +704,7 @@ pub enum GateIssue {
     Library(Option<u32>)
 }
 
-pub fn emit_feature_err(diag: &SpanHandler, feature: &str, span: Span, issue: GateIssue,
+pub fn emit_feature_err(diag: &Handler, feature: &str, span: Span, issue: GateIssue,
                         explain: &str) {
     let issue = match issue {
         GateIssue::Language => find_lang_feature_issue(feature),
@@ -1064,7 +1064,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
     }
 }
 
-fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
+fn check_crate_inner<F>(cm: &CodeMap, span_handler: &Handler,
                         krate: &ast::Crate,
                         plugin_attributes: &[(String, AttributeType)],
                         check: F)
@@ -1161,13 +1161,13 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
     }
 }
 
-pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
+pub fn check_crate_macros(cm: &CodeMap, span_handler: &Handler, krate: &ast::Crate)
 -> Features {
     check_crate_inner(cm, span_handler, krate, &[] as &'static [_],
                       |ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
 }
 
-pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate,
+pub fn check_crate(cm: &CodeMap, span_handler: &Handler, krate: &ast::Crate,
                    plugin_attributes: &[(String, AttributeType)],
                    unstable: UnstableFeatures) -> Features
 {
@@ -1192,7 +1192,7 @@ pub enum UnstableFeatures {
     Cheat
 }
 
-fn maybe_stage_features(span_handler: &SpanHandler, krate: &ast::Crate,
+fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate,
                         unstable: UnstableFeatures) {
     let allow_features = match unstable {
         UnstableFeatures::Allow => true,
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index 73d7025b4f1..47340d31224 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -52,7 +52,7 @@ extern crate serialize as rustc_serialize; // used by deriving
 macro_rules! panictry {
     ($e:expr) => ({
         use std::result::Result::{Ok, Err};
-        use diagnostic::FatalError;
+        use errors::FatalError;
         match $e {
             Ok(e) => e,
             Err(FatalError) => panic!(FatalError)
@@ -78,6 +78,8 @@ pub mod diagnostics {
     pub mod metadata;
 }
 
+pub mod errors;
+
 pub mod syntax {
     pub use ext;
     pub use parse;
@@ -90,7 +92,6 @@ pub mod ast_util;
 pub mod attr;
 pub mod codemap;
 pub mod config;
-pub mod diagnostic;
 pub mod entry;
 pub mod feature_gate;
 pub mod fold;
diff --git a/src/libsyntax/parse/lexer/comments.rs b/src/libsyntax/parse/lexer/comments.rs
index e5e2c3a986d..d2156d7cb68 100644
--- a/src/libsyntax/parse/lexer/comments.rs
+++ b/src/libsyntax/parse/lexer/comments.rs
@@ -12,7 +12,7 @@ pub use self::CommentStyle::*;
 
 use ast;
 use codemap::{BytePos, CharPos, CodeMap, Pos};
-use diagnostic;
+use errors;
 use parse::lexer::is_block_doc_comment;
 use parse::lexer::{StringReader, TokenAndSpan};
 use parse::lexer::{is_whitespace, Reader};
@@ -334,7 +334,7 @@ pub struct Literal {
 
 // it appears this function is called only from pprust... that's
 // probably not a good thing.
-pub fn gather_comments_and_literals(span_diagnostic: &diagnostic::SpanHandler,
+pub fn gather_comments_and_literals(span_diagnostic: &errors::Handler,
                                     path: String,
                                     srdr: &mut Read)
                                  -> (Vec<Comment>, Vec<Literal>) {
diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs
index cb2181a0831..4619410ada7 100644
--- a/src/libsyntax/parse/lexer/mod.rs
+++ b/src/libsyntax/parse/lexer/mod.rs
@@ -11,8 +11,7 @@
 use ast;
 use codemap::{BytePos, CharPos, CodeMap, Pos, Span};
 use codemap;
-use diagnostic::FatalError;
-use diagnostic::SpanHandler;
+use errors::{FatalError, Handler};
 use ext::tt::transcribe::tt_next_token;
 use parse::token::str_to_ident;
 use parse::token;
@@ -58,7 +57,7 @@ pub struct TokenAndSpan {
 }
 
 pub struct StringReader<'a> {
-    pub span_diagnostic: &'a SpanHandler,
+    pub span_diagnostic: &'a Handler,
     /// The absolute offset within the codemap of the next character to read
     pub pos: BytePos,
     /// The absolute offset within the codemap of the last character read(curr)
@@ -128,10 +127,10 @@ impl<'a> Reader for TtReader<'a> {
 
 impl<'a> StringReader<'a> {
     /// For comments.rs, which hackily pokes into pos and curr
-    pub fn new_raw<'b>(span_diagnostic: &'b SpanHandler,
+    pub fn new_raw<'b>(span_diagnostic: &'b Handler,
                        filemap: Rc<codemap::FileMap>) -> StringReader<'b> {
         if filemap.src.is_none() {
-            span_diagnostic.handler.bug(&format!("Cannot lex filemap without source: {}",
+            span_diagnostic.bug(&format!("Cannot lex filemap without source: {}",
                                                  filemap.name)[..]);
         }
 
@@ -153,7 +152,7 @@ impl<'a> StringReader<'a> {
         sr
     }
 
-    pub fn new<'b>(span_diagnostic: &'b SpanHandler,
+    pub fn new<'b>(span_diagnostic: &'b Handler,
                    filemap: Rc<codemap::FileMap>) -> StringReader<'b> {
         let mut sr = StringReader::new_raw(span_diagnostic, filemap);
         sr.advance_token();
@@ -1423,28 +1422,30 @@ mod tests {
     use super::*;
 
     use codemap::{BytePos, CodeMap, Span, NO_EXPANSION};
-    use diagnostic;
+    use errors;
     use parse::token;
     use parse::token::{str_to_ident};
     use std::io;
+    use std::rc::Rc;
 
-    fn mk_sh() -> diagnostic::SpanHandler {
+    fn mk_sh(cm: Rc<CodeMap>) -> errors::Handler {
         // FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
-        let emitter = diagnostic::EmitterWriter::new(Box::new(io::sink()), None);
-        let handler = diagnostic::Handler::with_emitter(true, Box::new(emitter));
-        diagnostic::SpanHandler::new(handler, CodeMap::new())
+        let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()), None, cm);
+        errors::Handler::with_emitter(true, false, Box::new(emitter))
     }
 
     // open a string reader for the given string
-    fn setup<'a>(span_handler: &'a diagnostic::SpanHandler,
+    fn setup<'a>(cm: &CodeMap,
+                 span_handler: &'a errors::Handler,
                  teststr: String) -> StringReader<'a> {
-        let fm = span_handler.cm.new_filemap("zebra.rs".to_string(), teststr);
+        let fm = cm.new_filemap("zebra.rs".to_string(), teststr);
         StringReader::new(span_handler, fm)
     }
 
     #[test] fn t1 () {
-        let span_handler = mk_sh();
-        let mut string_reader = setup(&span_handler,
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        let mut string_reader = setup(&cm, &sh,
             "/* my source file */ \
              fn main() { println!(\"zebra\"); }\n".to_string());
         let id = str_to_ident("fn");
@@ -1482,21 +1483,27 @@ mod tests {
     }
 
     #[test] fn doublecolonparsing () {
-        check_tokenization(setup(&mk_sh(), "a b".to_string()),
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        check_tokenization(setup(&cm, &sh, "a b".to_string()),
                            vec![mk_ident("a", token::Plain),
                                 token::Whitespace,
                                 mk_ident("b", token::Plain)]);
     }
 
     #[test] fn dcparsing_2 () {
-        check_tokenization(setup(&mk_sh(), "a::b".to_string()),
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        check_tokenization(setup(&cm, &sh, "a::b".to_string()),
                            vec![mk_ident("a",token::ModName),
                                 token::ModSep,
                                 mk_ident("b", token::Plain)]);
     }
 
     #[test] fn dcparsing_3 () {
-        check_tokenization(setup(&mk_sh(), "a ::b".to_string()),
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        check_tokenization(setup(&cm, &sh, "a ::b".to_string()),
                            vec![mk_ident("a", token::Plain),
                                 token::Whitespace,
                                 token::ModSep,
@@ -1504,7 +1511,9 @@ mod tests {
     }
 
     #[test] fn dcparsing_4 () {
-        check_tokenization(setup(&mk_sh(), "a:: b".to_string()),
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        check_tokenization(setup(&cm, &sh, "a:: b".to_string()),
                            vec![mk_ident("a",token::ModName),
                                 token::ModSep,
                                 token::Whitespace,
@@ -1512,40 +1521,52 @@ mod tests {
     }
 
     #[test] fn character_a() {
-        assert_eq!(setup(&mk_sh(), "'a'".to_string()).next_token().tok,
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        assert_eq!(setup(&cm, &sh, "'a'".to_string()).next_token().tok,
                    token::Literal(token::Char(token::intern("a")), None));
     }
 
     #[test] fn character_space() {
-        assert_eq!(setup(&mk_sh(), "' '".to_string()).next_token().tok,
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        assert_eq!(setup(&cm, &sh, "' '".to_string()).next_token().tok,
                    token::Literal(token::Char(token::intern(" ")), None));
     }
 
     #[test] fn character_escaped() {
-        assert_eq!(setup(&mk_sh(), "'\\n'".to_string()).next_token().tok,
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        assert_eq!(setup(&cm, &sh, "'\\n'".to_string()).next_token().tok,
                    token::Literal(token::Char(token::intern("\\n")), None));
     }
 
     #[test] fn lifetime_name() {
-        assert_eq!(setup(&mk_sh(), "'abc".to_string()).next_token().tok,
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        assert_eq!(setup(&cm, &sh, "'abc".to_string()).next_token().tok,
                    token::Lifetime(token::str_to_ident("'abc")));
     }
 
     #[test] fn raw_string() {
-        assert_eq!(setup(&mk_sh(),
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        assert_eq!(setup(&cm, &sh,
                          "r###\"\"#a\\b\x00c\"\"###".to_string()).next_token()
                                                                  .tok,
                    token::Literal(token::StrRaw(token::intern("\"#a\\b\x00c\""), 3), None));
     }
 
     #[test] fn literal_suffixes() {
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
         macro_rules! test {
             ($input: expr, $tok_type: ident, $tok_contents: expr) => {{
-                assert_eq!(setup(&mk_sh(), format!("{}suffix", $input)).next_token().tok,
+                assert_eq!(setup(&cm, &sh, format!("{}suffix", $input)).next_token().tok,
                            token::Literal(token::$tok_type(token::intern($tok_contents)),
                                           Some(token::intern("suffix"))));
                 // with a whitespace separator:
-                assert_eq!(setup(&mk_sh(), format!("{} suffix", $input)).next_token().tok,
+                assert_eq!(setup(&cm, &sh, format!("{} suffix", $input)).next_token().tok,
                            token::Literal(token::$tok_type(token::intern($tok_contents)),
                                           None));
             }}
@@ -1561,13 +1582,13 @@ mod tests {
         test!("1.0", Float, "1.0");
         test!("1.0e10", Float, "1.0e10");
 
-        assert_eq!(setup(&mk_sh(), "2us".to_string()).next_token().tok,
+        assert_eq!(setup(&cm, &sh, "2us".to_string()).next_token().tok,
                    token::Literal(token::Integer(token::intern("2")),
                                   Some(token::intern("us"))));
-        assert_eq!(setup(&mk_sh(), "r###\"raw\"###suffix".to_string()).next_token().tok,
+        assert_eq!(setup(&cm, &sh, "r###\"raw\"###suffix".to_string()).next_token().tok,
                    token::Literal(token::StrRaw(token::intern("raw"), 3),
                                   Some(token::intern("suffix"))));
-        assert_eq!(setup(&mk_sh(), "br###\"raw\"###suffix".to_string()).next_token().tok,
+        assert_eq!(setup(&cm, &sh, "br###\"raw\"###suffix".to_string()).next_token().tok,
                    token::Literal(token::ByteStrRaw(token::intern("raw"), 3),
                                   Some(token::intern("suffix"))));
     }
@@ -1579,8 +1600,9 @@ mod tests {
     }
 
     #[test] fn nested_block_comments() {
-        let sh = mk_sh();
-        let mut lexer = setup(&sh, "/* /* */ */'a'".to_string());
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        let mut lexer = setup(&cm, &sh, "/* /* */ */'a'".to_string());
         match lexer.next_token().tok {
             token::Comment => { },
             _ => panic!("expected a comment!")
@@ -1589,8 +1611,9 @@ mod tests {
     }
 
     #[test] fn crlf_comments() {
-        let sh = mk_sh();
-        let mut lexer = setup(&sh, "// test\r\n/// test\r\n".to_string());
+        let cm = Rc::new(CodeMap::new());
+        let sh = mk_sh(cm.clone());
+        let mut lexer = setup(&cm, &sh, "// test\r\n/// test\r\n".to_string());
         let comment = lexer.next_token();
         assert_eq!(comment.tok, token::Comment);
         assert_eq!(comment.sp, ::codemap::mk_sp(BytePos(0), BytePos(7)));
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index e9c8173a4d9..ed87961e7f3 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -12,7 +12,7 @@
 
 use ast;
 use codemap::{self, Span, CodeMap, FileMap};
-use diagnostic::{SpanHandler, Handler, Auto, FatalError};
+use errors::{Handler, ColorConfig, FatalError};
 use parse::parser::Parser;
 use parse::token::InternedString;
 use ptr::P;
@@ -40,26 +40,29 @@ pub mod obsolete;
 
 /// Info about a parsing session.
 pub struct ParseSess {
-    pub span_diagnostic: SpanHandler, // better be the same as the one in the reader!
+    pub span_diagnostic: Handler, // better be the same as the one in the reader!
     /// Used to determine and report recursive mod inclusions
     included_mod_stack: RefCell<Vec<PathBuf>>,
+    code_map: Rc<CodeMap>,
 }
 
 impl ParseSess {
     pub fn new() -> ParseSess {
-        let handler = SpanHandler::new(Handler::new(Auto, None, true), CodeMap::new());
-        ParseSess::with_span_handler(handler)
+        let cm = Rc::new(CodeMap::new());
+        let handler = Handler::new(ColorConfig::Auto, None, true, false, cm.clone());
+        ParseSess::with_span_handler(handler, cm)
     }
 
-    pub fn with_span_handler(sh: SpanHandler) -> ParseSess {
+    pub fn with_span_handler(handler: Handler, code_map: Rc<CodeMap>) -> ParseSess {
         ParseSess {
-            span_diagnostic: sh,
-            included_mod_stack: RefCell::new(vec![])
+            span_diagnostic: handler,
+            included_mod_stack: RefCell::new(vec![]),
+            code_map: code_map
         }
     }
 
     pub fn codemap(&self) -> &CodeMap {
-        &self.span_diagnostic.cm
+        &self.code_map
     }
 }
 
@@ -235,7 +238,7 @@ fn file_to_filemap(sess: &ParseSess, path: &Path, spanopt: Option<Span>)
             let msg = format!("couldn't read {:?}: {}", path.display(), e);
             match spanopt {
                 Some(sp) => panic!(sess.span_diagnostic.span_fatal(sp, &msg)),
-                None => panic!(sess.span_diagnostic.handler().fatal(&msg))
+                None => panic!(sess.span_diagnostic.fatal(&msg))
             }
         }
     }
@@ -438,7 +441,7 @@ fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool {
 }
 
 fn filtered_float_lit(data: token::InternedString, suffix: Option<&str>,
-                      sd: &SpanHandler, sp: Span) -> ast::Lit_ {
+                      sd: &Handler, sp: Span) -> ast::Lit_ {
     debug!("filtered_float_lit: {}, {:?}", data, suffix);
     match suffix.as_ref().map(|s| &**s) {
         Some("f32") => ast::LitFloat(data, ast::TyF32),
@@ -459,7 +462,7 @@ fn filtered_float_lit(data: token::InternedString, suffix: Option<&str>,
     }
 }
 pub fn float_lit(s: &str, suffix: Option<InternedString>,
-                 sd: &SpanHandler, sp: Span) -> ast::Lit_ {
+                 sd: &Handler, sp: Span) -> ast::Lit_ {
     debug!("float_lit: {:?}, {:?}", s, suffix);
     // FIXME #2252: bounds checking float literals is deferred until trans
     let s = s.chars().filter(|&c| c != '_').collect::<String>();
@@ -561,7 +564,7 @@ pub fn byte_str_lit(lit: &str) -> Rc<Vec<u8>> {
 
 pub fn integer_lit(s: &str,
                    suffix: Option<InternedString>,
-                   sd: &SpanHandler,
+                   sd: &Handler,
                    sp: Span)
                    -> ast::Lit_ {
     // s can only be ascii, byte indexing is fine
diff --git a/src/libsyntax/parse/obsolete.rs b/src/libsyntax/parse/obsolete.rs
index bc355f70fb3..5dba1e189ab 100644
--- a/src/libsyntax/parse/obsolete.rs
+++ b/src/libsyntax/parse/obsolete.rs
@@ -66,10 +66,9 @@ impl<'a> ParserObsoleteMethods for parser::Parser<'a> {
         }
 
         if !self.obsolete_set.contains(&kind) &&
-            (error || self.sess.span_diagnostic.handler().can_emit_warnings) {
+            (error || self.sess.span_diagnostic.can_emit_warnings) {
             self.sess
                 .span_diagnostic
-                .handler()
                 .note(&format!("{}", desc));
             self.obsolete_set.insert(kind);
         }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 712f4e38012..b8c4d8d63b5 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -60,7 +60,7 @@ use attr::{ThinAttributes, ThinAttributesExt, AttributesExt};
 use ast;
 use ast_util::{self, ident_to_path};
 use codemap::{self, Span, BytePos, Spanned, spanned, mk_sp, CodeMap};
-use diagnostic;
+use errors::{self, FatalError};
 use ext::tt::macro_parser;
 use parse;
 use parse::classify;
@@ -75,7 +75,6 @@ use print::pprust;
 use ptr::P;
 use owned_slice::OwnedSlice;
 use parse::PResult;
-use diagnostic::FatalError;
 
 use std::collections::HashSet;
 use std::io::prelude::*;
@@ -983,16 +982,16 @@ impl<'a> Parser<'a> {
         }
         f(&self.buffer[((self.buffer_start + dist - 1) & 3) as usize].tok)
     }
-    pub fn fatal(&self, m: &str) -> diagnostic::FatalError {
+    pub fn fatal(&self, m: &str) -> errors::FatalError {
         self.sess.span_diagnostic.span_fatal(self.span, m)
     }
-    pub fn span_fatal(&self, sp: Span, m: &str) -> diagnostic::FatalError {
+    pub fn span_fatal(&self, sp: Span, m: &str) -> errors::FatalError {
         self.sess.span_diagnostic.span_fatal(sp, m)
     }
-    pub fn span_fatal_help(&self, sp: Span, m: &str, help: &str) -> diagnostic::FatalError {
+    pub fn span_fatal_help(&self, sp: Span, m: &str, help: &str) -> errors::FatalError {
         self.span_err(sp, m);
         self.fileline_help(sp, help);
-        diagnostic::FatalError
+        errors::FatalError
     }
     pub fn span_note(&self, sp: Span, m: &str) {
         self.sess.span_diagnostic.span_note(sp, m)
@@ -1022,7 +1021,7 @@ impl<'a> Parser<'a> {
         self.sess.span_diagnostic.span_bug(sp, m)
     }
     pub fn abort_if_errors(&self) {
-        self.sess.span_diagnostic.handler().abort_if_errors();
+        self.sess.span_diagnostic.abort_if_errors();
     }
 
     pub fn id_to_interned_str(&mut self, id: Ident) -> InternedString {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 4e2289cb7f4..08448f25187 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -20,7 +20,7 @@ use attr;
 use owned_slice::OwnedSlice;
 use attr::{AttrMetaMethods, AttributeMethods};
 use codemap::{self, CodeMap, BytePos};
-use diagnostic;
+use errors;
 use parse::token::{self, BinOpToken, Token, InternedString};
 use parse::lexer::comments;
 use parse;
@@ -99,7 +99,7 @@ pub const DEFAULT_COLUMNS: usize = 78;
 /// it can scan the input text for comments and literals to
 /// copy forward.
 pub fn print_crate<'a>(cm: &'a CodeMap,
-                       span_diagnostic: &diagnostic::SpanHandler,
+                       span_diagnostic: &errors::Handler,
                        krate: &ast::Crate,
                        filename: String,
                        input: &mut Read,
@@ -139,7 +139,7 @@ pub fn print_crate<'a>(cm: &'a CodeMap,
 
 impl<'a> State<'a> {
     pub fn new_from_input(cm: &'a CodeMap,
-                          span_diagnostic: &diagnostic::SpanHandler,
+                          span_diagnostic: &errors::Handler,
                           filename: String,
                           input: &mut Read,
                           out: Box<Write+'a>,
diff --git a/src/libsyntax/show_span.rs b/src/libsyntax/show_span.rs
index 6492cd4b095..014c7b2a68f 100644
--- a/src/libsyntax/show_span.rs
+++ b/src/libsyntax/show_span.rs
@@ -16,7 +16,7 @@
 use std::str::FromStr;
 
 use ast;
-use diagnostic;
+use errors;
 use visit;
 use visit::Visitor;
 
@@ -40,7 +40,7 @@ impl FromStr for Mode {
 }
 
 struct ShowSpanVisitor<'a> {
-    span_diagnostic: &'a diagnostic::SpanHandler,
+    span_diagnostic: &'a errors::Handler,
     mode: Mode,
 }
 
@@ -71,7 +71,7 @@ impl<'a, 'v> Visitor<'v> for ShowSpanVisitor<'a> {
     }
 }
 
-pub fn run(span_diagnostic: &diagnostic::SpanHandler,
+pub fn run(span_diagnostic: &errors::Handler,
            mode: &str,
            krate: &ast::Crate) {
     let mode = match mode.parse().ok() {
diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs
index 63fbe284a09..9e1d80b3f0d 100644
--- a/src/libsyntax/test.rs
+++ b/src/libsyntax/test.rs
@@ -23,7 +23,7 @@ use attr::AttrMetaMethods;
 use attr;
 use codemap::{DUMMY_SP, Span, ExpnInfo, NameAndSpan, MacroAttribute};
 use codemap;
-use diagnostic;
+use errors;
 use config;
 use entry::{self, EntryPointType};
 use ext::base::ExtCtxt;
@@ -55,7 +55,7 @@ struct Test {
 
 struct TestCtxt<'a> {
     sess: &'a ParseSess,
-    span_diagnostic: &'a diagnostic::SpanHandler,
+    span_diagnostic: &'a errors::Handler,
     path: Vec<ast::Ident>,
     ext_cx: ExtCtxt<'a>,
     testfns: Vec<Test>,
@@ -72,7 +72,7 @@ struct TestCtxt<'a> {
 pub fn modify_for_testing(sess: &ParseSess,
                           cfg: &ast::CrateConfig,
                           krate: ast::Crate,
-                          span_diagnostic: &diagnostic::SpanHandler) -> ast::Crate {
+                          span_diagnostic: &errors::Handler) -> ast::Crate {
     // We generate the test harness when building in the 'test'
     // configuration, either with the '--test' or '--cfg test'
     // command line options.
@@ -275,7 +275,7 @@ fn generate_test_harness(sess: &ParseSess,
                          reexport_test_harness_main: Option<InternedString>,
                          krate: ast::Crate,
                          cfg: &ast::CrateConfig,
-                         sd: &diagnostic::SpanHandler) -> ast::Crate {
+                         sd: &errors::Handler) -> ast::Crate {
     // Remove the entry points
     let mut cleaner = EntryPointCleaner { depth: 0 };
     let krate = cleaner.fold_crate(krate);
@@ -315,7 +315,7 @@ fn generate_test_harness(sess: &ParseSess,
     return res;
 }
 
-fn strip_test_functions(diagnostic: &diagnostic::SpanHandler, krate: ast::Crate)
+fn strip_test_functions(diagnostic: &errors::Handler, krate: ast::Crate)
                         -> ast::Crate {
     // When not compiling with --test we should not compile the
     // #[test] functions
@@ -688,7 +688,7 @@ fn mk_test_desc_and_fn_rec(cx: &TestCtxt, test: &Test) -> P<ast::Expr> {
         Some(id) => vec![id],
         None => {
             let diag = cx.span_diagnostic;
-            diag.handler.bug("expected to find top-level re-export name, but found None");
+            diag.bug("expected to find top-level re-export name, but found None");
         }
     };
     visible_path.extend(path);