about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-12-30 12:27:10 +0000
committerbors <bors@rust-lang.org>2015-12-30 12:27:10 +0000
commit176ee349a704a1aee9dfb79c27c5da20db7942a5 (patch)
tree99e1ead256ccd196b4135455ef037b14ab3094fa
parenta06bb977d86dcfe786d4265f4807a11c39b51141 (diff)
parent04d972906d05e6c27452e1ae35970c30e7cf6e6b (diff)
downloadrust-176ee349a704a1aee9dfb79c27c5da20db7942a5.tar.gz
rust-176ee349a704a1aee9dfb79c27c5da20db7942a5.zip
Auto merge of #30542 - nrc:errs-base, r=nagisa
As discussed [here](https://internals.rust-lang.org/t/more-structured-errors/3005)

r? @nikomatsakis or anyone else on the @rust-lang/compiler team
-rw-r--r--src/librustc/lint/context.rs86
-rw-r--r--src/librustc/lint/mod.rs2
-rw-r--r--src/librustc/middle/check_const.rs41
-rw-r--r--src/librustc/middle/check_match.rs31
-rw-r--r--src/librustc/middle/dependency_format.rs9
-rw-r--r--src/librustc/middle/entry.rs15
-rw-r--r--src/librustc/middle/infer/error_reporting.rs371
-rw-r--r--src/librustc/middle/infer/mod.rs61
-rw-r--r--src/librustc/middle/liveness.rs9
-rw-r--r--src/librustc/middle/resolve_lifetime.rs30
-rw-r--r--src/librustc/middle/stability.rs18
-rw-r--r--src/librustc/middle/traits/error_reporting.rs148
-rw-r--r--src/librustc/middle/ty/error.rs54
-rw-r--r--src/librustc/session/mod.rs95
-rw-r--r--src/librustc_borrowck/borrowck/check_loans.rs103
-rw-r--r--src/librustc_borrowck/borrowck/gather_loans/move_error.rs56
-rw-r--r--src/librustc_borrowck/borrowck/mod.rs144
-rw-r--r--src/librustc_driver/driver.rs5
-rw-r--r--src/librustc_driver/test.rs2
-rw-r--r--src/librustc_lint/builtin.rs12
-rw-r--r--src/librustc_metadata/creader.rs6
-rw-r--r--src/librustc_metadata/loader.rs115
-rw-r--r--src/librustc_plugin/build.rs5
-rw-r--r--src/librustc_privacy/lib.rs16
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs16
-rw-r--r--src/librustc_resolve/lib.rs419
-rw-r--r--src/librustc_resolve/resolve_imports.rs56
-rw-r--r--src/librustc_trans/back/archive.rs11
-rw-r--r--src/librustc_trans/back/link.rs21
-rw-r--r--src/librustc_trans/back/lto.rs5
-rw-r--r--src/librustc_trans/back/write.rs27
-rw-r--r--src/librustc_trans/trans/base.rs32
-rw-r--r--src/librustc_trans/trans/foreign.rs9
-rw-r--r--src/librustc_typeck/astconv.rs78
-rw-r--r--src/librustc_typeck/check/_match.rs17
-rw-r--r--src/librustc_typeck/check/callee.rs17
-rw-r--r--src/librustc_typeck/check/cast.rs33
-rw-r--r--src/librustc_typeck/check/dropck.rs29
-rw-r--r--src/librustc_typeck/check/method/suggest.rs87
-rw-r--r--src/librustc_typeck/check/mod.rs110
-rw-r--r--src/librustc_typeck/check/op.rs11
-rw-r--r--src/librustc_typeck/check/wfcheck.rs13
-rw-r--r--src/librustc_typeck/check/writeback.rs14
-rw-r--r--src/librustc_typeck/coherence/mod.rs11
-rw-r--r--src/librustc_typeck/coherence/orphan.rs8
-rw-r--r--src/librustc_typeck/coherence/overlap.rs13
-rw-r--r--src/librustc_typeck/collect.rs44
-rw-r--r--src/librustc_typeck/lib.rs5
-rw-r--r--src/libsyntax/attr.rs6
-rw-r--r--src/libsyntax/diagnostics/macros.rs52
-rw-r--r--src/libsyntax/diagnostics/plugin.rs6
-rw-r--r--src/libsyntax/errors/emitter.rs22
-rw-r--r--src/libsyntax/errors/mod.rs294
-rw-r--r--src/libsyntax/ext/base.rs36
-rw-r--r--src/libsyntax/ext/expand.rs15
-rw-r--r--src/libsyntax/ext/quote.rs2
-rw-r--r--src/libsyntax/ext/source_util.rs8
-rw-r--r--src/libsyntax/ext/tt/macro_parser.rs32
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs11
-rw-r--r--src/libsyntax/feature_gate.rs32
-rw-r--r--src/libsyntax/lib.rs17
-rw-r--r--src/libsyntax/parse/attr.rs24
-rw-r--r--src/libsyntax/parse/lexer/mod.rs63
-rw-r--r--src/libsyntax/parse/lexer/unicode_chars.rs7
-rw-r--r--src/libsyntax/parse/mod.rs50
-rw-r--r--src/libsyntax/parse/obsolete.rs13
-rw-r--r--src/libsyntax/parse/parser.rs468
-rw-r--r--src/libsyntax/show_span.rs6
-rw-r--r--src/libsyntax/util/parser_testing.rs20
-rw-r--r--src/libsyntax_ext/format.rs9
-rw-r--r--src/libsyntax_ext/lib.rs15
-rw-r--r--src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs42
72 files changed, 2253 insertions, 1517 deletions
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index db22000fd9f..464f29a3393 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -40,6 +40,7 @@ use std::mem;
 use syntax::ast_util::{self, IdVisitingOperation};
 use syntax::attr::{self, AttrMetaMethods};
 use syntax::codemap::Span;
+use syntax::errors::{self, DiagnosticBuilder};
 use syntax::parse::token::InternedString;
 use syntax::ast;
 use syntax::attr::ThinAttributesExt;
@@ -47,7 +48,6 @@ use rustc_front::hir;
 use rustc_front::util;
 use rustc_front::intravisit as hir_visit;
 use syntax::visit as ast_visit;
-use syntax::errors;
 
 /// Information about the registered lints.
 ///
@@ -363,10 +363,24 @@ pub fn gather_attrs(attrs: &[ast::Attribute])
 /// in trans that run after the main lint pass is finished. Most
 /// lints elsewhere in the compiler should call
 /// `Session::add_lint()` instead.
-pub fn raw_emit_lint(sess: &Session, lint: &'static Lint,
-                     lvlsrc: LevelSource, span: Option<Span>, msg: &str) {
+pub fn raw_emit_lint(sess: &Session,
+                     lint: &'static Lint,
+                     lvlsrc: LevelSource,
+                     span: Option<Span>,
+                     msg: &str) {
+    raw_struct_lint(sess, lint, lvlsrc, span, msg).emit();
+}
+
+pub fn raw_struct_lint<'a>(sess: &'a Session,
+                           lint: &'static Lint,
+                           lvlsrc: LevelSource,
+                           span: Option<Span>,
+                           msg: &str)
+                           -> DiagnosticBuilder<'a> {
     let (mut level, source) = lvlsrc;
-    if level == Allow { return }
+    if level == Allow {
+        return sess.diagnostic().struct_dummy();
+    }
 
     let name = lint.name_lower();
     let mut def = None;
@@ -391,17 +405,19 @@ pub fn raw_emit_lint(sess: &Session, lint: &'static Lint,
     // For purposes of printing, we can treat forbid as deny.
     if level == Forbid { level = Deny; }
 
-    match (level, span) {
-        (Warn, Some(sp)) => sess.span_warn(sp, &msg[..]),
-        (Warn, None)     => sess.warn(&msg[..]),
-        (Deny, Some(sp)) => sess.span_err(sp, &msg[..]),
-        (Deny, None)     => sess.err(&msg[..]),
+    let mut err = match (level, span) {
+        (Warn, Some(sp)) => sess.struct_span_warn(sp, &msg[..]),
+        (Warn, None)     => sess.struct_warn(&msg[..]),
+        (Deny, Some(sp)) => sess.struct_span_err(sp, &msg[..]),
+        (Deny, None)     => sess.struct_err(&msg[..]),
         _ => sess.bug("impossible level in raw_emit_lint"),
-    }
+    };
 
     if let Some(span) = def {
-        sess.span_note(span, "lint level defined here");
+        err.span_note(span, "lint level defined here");
     }
+
+    err
 }
 
 pub trait LintContext: Sized {
@@ -418,44 +434,74 @@ pub trait LintContext: Sized {
         self.lints().levels.get(&LintId::of(lint)).map_or(Allow, |&(lvl, _)| lvl)
     }
 
-    fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) {
-        let (level, src) = match self.lints().levels.get(&LintId::of(lint)) {
-            None => return,
-            Some(&(Warn, src)) => {
+    fn level_src(&self, lint: &'static Lint) -> Option<LevelSource> {
+        self.lints().levels.get(&LintId::of(lint)).map(|ls| match ls {
+            &(Warn, src) => {
                 let lint_id = LintId::of(builtin::WARNINGS);
                 (self.lints().get_level_source(lint_id).0, src)
             }
-            Some(&pair) => pair,
+            _ => *ls
+        })
+    }
+
+    fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) {
+        let (level, src) = match self.level_src(lint) {
+            None => return,
+            Some(pair) => pair,
         };
 
         raw_emit_lint(&self.sess(), lint, (level, src), span, msg);
     }
 
+    fn lookup(&self,
+              lint: &'static Lint,
+              span: Option<Span>,
+              msg: &str)
+              -> DiagnosticBuilder {
+        let (level, src) = match self.level_src(lint) {
+            None => return self.sess().diagnostic().struct_dummy(),
+            Some(pair) => pair,
+        };
+
+        raw_struct_lint(&self.sess(), lint, (level, src), span, msg)
+    }
+
     /// Emit a lint at the appropriate level, for a particular span.
     fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
         self.lookup_and_emit(lint, Some(span), msg);
     }
 
+    fn struct_span_lint(&self,
+                        lint: &'static Lint,
+                        span: Span,
+                        msg: &str)
+                        -> DiagnosticBuilder {
+        self.lookup(lint, Some(span), msg)
+    }
+
     /// Emit a lint and note at the appropriate level, for a particular span.
     fn span_lint_note(&self, lint: &'static Lint, span: Span, msg: &str,
                       note_span: Span, note: &str) {
-        self.span_lint(lint, span, msg);
+        let mut err = self.lookup(lint, Some(span), msg);
         if self.current_level(lint) != Level::Allow {
             if note_span == span {
-                self.sess().fileline_note(note_span, note)
+                err.fileline_note(note_span, note);
             } else {
-                self.sess().span_note(note_span, note)
+                err.span_note(note_span, note);
             }
         }
+        err.emit();
     }
 
     /// Emit a lint and help at the appropriate level, for a particular span.
     fn span_lint_help(&self, lint: &'static Lint, span: Span,
                       msg: &str, help: &str) {
+        let mut err = self.lookup(lint, Some(span), msg);
         self.span_lint(lint, span, msg);
         if self.current_level(lint) != Level::Allow {
-            self.sess().span_help(span, help)
+            err.span_help(span, help);
         }
+        err.emit();
     }
 
     /// Emit a lint at the appropriate level, with no associated span.
diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs
index 23be6117f19..9c689daab86 100644
--- a/src/librustc/lint/mod.rs
+++ b/src/librustc/lint/mod.rs
@@ -41,7 +41,7 @@ use rustc_front::hir;
 
 pub use lint::context::{LateContext, EarlyContext, LintContext, LintStore,
                         raw_emit_lint, check_crate, check_ast_crate, gather_attrs,
-                        GatherNodeLevels};
+                        raw_struct_lint, GatherNodeLevels};
 
 /// Specification of a single lint.
 #[derive(Copy, Clone, Debug)]
diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs
index d6f05ffd8a5..c2acd0e4795 100644
--- a/src/librustc/middle/check_const.rs
+++ b/src/librustc/middle/check_const.rs
@@ -224,14 +224,15 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
                 // this doesn't come from a macro that has #[allow_internal_unstable]
                 !self.tcx.sess.codemap().span_allows_unstable(expr.span)
             {
-                self.tcx.sess.span_err(
+                let mut err = self.tcx.sess.struct_span_err(
                     expr.span,
                     "const fns are an unstable feature");
                 fileline_help!(
-                    self.tcx.sess,
+                    &mut err,
                     expr.span,
                     "in Nightly builds, add `#![feature(const_fn)]` to the crate \
                      attributes to enable");
+                err.emit();
             }
 
             let qualif = self.fn_like(fn_like.kind(),
@@ -714,27 +715,27 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
             if !is_const {
                 v.add_qualif(ConstQualif::NOT_CONST);
                 if v.mode != Mode::Var {
-                    fn span_limited_call_error(tcx: &ty::ctxt, span: Span, s: &str) {
-                        span_err!(tcx.sess, span, E0015, "{}", s);
-                    }
-
                     // FIXME(#24111) Remove this check when const fn stabilizes
-                    if let UnstableFeatures::Disallow = v.tcx.sess.opts.unstable_features {
-                        span_limited_call_error(&v.tcx, e.span,
-                                                &format!("function calls in {}s are limited to \
-                                                          struct and enum constructors",
-                                                         v.msg()));
-                        v.tcx.sess.span_note(e.span,
-                                             "a limited form of compile-time function \
-                                              evaluation is available on a nightly \
-                                              compiler via `const fn`");
+                    let (msg, note) =
+                        if let UnstableFeatures::Disallow = v.tcx.sess.opts.unstable_features {
+                        (format!("function calls in {}s are limited to \
+                                  struct and enum constructors",
+                                 v.msg()),
+                         Some("a limited form of compile-time function \
+                               evaluation is available on a nightly \
+                               compiler via `const fn`"))
                     } else {
-                        span_limited_call_error(&v.tcx, e.span,
-                                                &format!("function calls in {}s are limited \
-                                                          to constant functions, \
-                                                          struct and enum constructors",
-                                                         v.msg()));
+                        (format!("function calls in {}s are limited \
+                                  to constant functions, \
+                                  struct and enum constructors",
+                                 v.msg()),
+                         None)
+                    };
+                    let mut err = struct_span_err!(v.tcx.sess, e.span, E0015, "{}", msg);
+                    if let Some(note) = note {
+                        err.span_note(e.span, note);
                     }
+                    err.emit();
                 }
             }
         }
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index cab7e45ee62..8439b439d76 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -215,12 +215,13 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &hir::Expr) {
             if inlined_arms.is_empty() {
                 if !pat_ty.is_empty(cx.tcx) {
                     // We know the type is inhabited, so this must be wrong
-                    span_err!(cx.tcx.sess, ex.span, E0002,
-                              "non-exhaustive patterns: type {} is non-empty",
-                              pat_ty);
-                    span_help!(cx.tcx.sess, ex.span,
+                    let mut err = struct_span_err!(cx.tcx.sess, ex.span, E0002,
+                                                   "non-exhaustive patterns: type {} is non-empty",
+                                                   pat_ty);
+                    span_help!(&mut err, ex.span,
                         "Please ensure that all possible cases are being handled; \
                          possibly adding wildcards or more match arms.");
+                    err.emit();
                 }
                 // If the type *is* empty, it's vacuously exhaustive
                 return;
@@ -251,14 +252,15 @@ fn check_for_bindings_named_the_same_as_variants(cx: &MatchCheckCtxt, pat: &Pat)
                                 && variant.kind() == VariantKind::Unit
                         ) {
                             let ty_path = cx.tcx.item_path_str(edef.did);
-                            span_warn!(cx.tcx.sess, p.span, E0170,
+                            let mut err = struct_span_warn!(cx.tcx.sess, p.span, E0170,
                                 "pattern binding `{}` is named the same as one \
                                  of the variants of the type `{}`",
                                 ident.node, ty_path);
-                            fileline_help!(cx.tcx.sess, p.span,
+                            fileline_help!(err, p.span,
                                 "if you meant to match on a variant, \
                                  consider making the path in the pattern qualified: `{}::{}`",
                                 ty_path, ident.node);
+                            err.emit();
                         }
                     }
                 }
@@ -282,13 +284,13 @@ fn check_for_static_nan(cx: &MatchCheckCtxt, pat: &Pat) {
                 Ok(_) => {}
 
                 Err(err) => {
-                    span_err!(cx.tcx.sess, err.span, E0471,
-                              "constant evaluation error: {}",
-                              err.description());
+                    let mut diag = struct_span_err!(cx.tcx.sess, err.span, E0471,
+                                                    "constant evaluation error: {}",
+                                                    err.description());
                     if !p.span.contains(err.span) {
-                        cx.tcx.sess.span_note(p.span,
-                                              "in pattern here")
+                        diag.span_note(p.span, "in pattern here");
                     }
+                    diag.emit();
                 }
             }
         }
@@ -1076,9 +1078,10 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
         } else if has_guard {
             span_err!(cx.tcx.sess, p.span, E0008, "cannot bind by-move into a pattern guard");
         } else if by_ref_span.is_some() {
-            span_err!(cx.tcx.sess, p.span, E0009,
-                "cannot bind by-move and by-ref in the same pattern");
-            span_note!(cx.tcx.sess, by_ref_span.unwrap(), "by-ref binding occurs here");
+            let mut err = struct_span_err!(cx.tcx.sess, p.span, E0009,
+                                           "cannot bind by-move and by-ref in the same pattern");
+            span_note!(&mut err, by_ref_span.unwrap(), "by-ref binding occurs here");
+            err.emit();
         }
     };
 
diff --git a/src/librustc/middle/dependency_format.rs b/src/librustc/middle/dependency_format.rs
index ab5153e1a61..aac6f1edc05 100644
--- a/src/librustc/middle/dependency_format.rs
+++ b/src/librustc/middle/dependency_format.rs
@@ -243,10 +243,11 @@ fn add_library(sess: &session::Session,
             // This error is probably a little obscure, but I imagine that it
             // can be refined over time.
             if link2 != link || link == RequireStatic {
-                sess.err(&format!("cannot satisfy dependencies so `{}` only \
-                                   shows up once", sess.cstore.crate_name(cnum)));
-                sess.help("having upstream crates all available in one format \
-                           will likely make this go away");
+                sess.struct_err(&format!("cannot satisfy dependencies so `{}` only \
+                                          shows up once", sess.cstore.crate_name(cnum)))
+                    .help("having upstream crates all available in one format \
+                           will likely make this go away")
+                    .emit();
             }
         }
         None => { m.insert(cnum, link); }
diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs
index ecf16aaed83..2d096f66e09 100644
--- a/src/librustc/middle/entry.rs
+++ b/src/librustc/middle/entry.rs
@@ -146,17 +146,20 @@ fn configure_main(this: &mut EntryContext) {
         this.session.entry_type.set(Some(config::EntryMain));
     } else {
         // No main function
-        this.session.err("main function not found");
+        let mut err = this.session.struct_err("main function not found");
         if !this.non_main_fns.is_empty() {
             // There were some functions named 'main' though. Try to give the user a hint.
-            this.session.note("the main function must be defined at the crate level \
-                               but you have one or more functions named 'main' that are not \
-                               defined at the crate level. Either move the definition or \
-                               attach the `#[main]` attribute to override this behavior.");
+            err.note("the main function must be defined at the crate level \
+                      but you have one or more functions named 'main' that are not \
+                      defined at the crate level. Either move the definition or \
+                      attach the `#[main]` attribute to override this behavior.");
             for &(_, span) in &this.non_main_fns {
-                this.session.span_note(span, "here is a function named 'main'");
+                err.span_note(span, "here is a function named 'main'");
             }
+            err.emit();
             this.session.abort_if_errors();
+        } else {
+            err.emit();
         }
     }
 }
diff --git a/src/librustc/middle/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs
index 181e0c0fbc8..d628227d5e6 100644
--- a/src/librustc/middle/infer/error_reporting.rs
+++ b/src/librustc/middle/infer/error_reporting.rs
@@ -90,12 +90,14 @@ use std::cell::{Cell, RefCell};
 use std::char::from_u32;
 use std::fmt;
 use syntax::ast;
+use syntax::errors::DiagnosticBuilder;
 use syntax::codemap::{self, Pos, Span};
 use syntax::parse::token;
 use syntax::ptr::P;
 
 impl<'tcx> ty::ctxt<'tcx> {
     pub fn note_and_explain_region(&self,
+                                   err: &mut DiagnosticBuilder,
                                    prefix: &str,
                                    region: ty::Region,
                                    suffix: &str) {
@@ -126,7 +128,10 @@ impl<'tcx> ty::ctxt<'tcx> {
                 };
                 let span = match scope.span(&self.region_maps, &self.map) {
                     Some(s) => s,
-                    None => return self.sess.note(&unknown_scope())
+                    None => {
+                        err.note(&unknown_scope());
+                        return;
+                    }
                 };
                 let tag = match self.map.find(scope.node_id(&self.region_maps)) {
                     Some(ast_map::NodeBlock(_)) => "block",
@@ -142,7 +147,8 @@ impl<'tcx> ty::ctxt<'tcx> {
                     Some(ast_map::NodeStmt(_)) => "statement",
                     Some(ast_map::NodeItem(it)) => item_scope_tag(&*it),
                     Some(_) | None => {
-                        return self.sess.span_note(span, &unknown_scope());
+                        err.span_note(span, &unknown_scope());
+                        return;
                     }
                 };
                 let scope_decorated_tag = match self.region_maps.code_extent_data(scope) {
@@ -214,9 +220,9 @@ impl<'tcx> ty::ctxt<'tcx> {
         };
         let message = format!("{}{}{}", prefix, description, suffix);
         if let Some(span) = span {
-            self.sess.span_note(span, &message);
+            err.span_note(span, &message);
         } else {
-            self.sess.note(&message);
+            err.note(&message);
         }
     }
 }
@@ -228,9 +234,15 @@ pub trait ErrorReporting<'tcx> {
     fn process_errors(&self, errors: &Vec<RegionResolutionError<'tcx>>)
                       -> Vec<RegionResolutionError<'tcx>>;
 
-    fn report_type_error(&self, trace: TypeTrace<'tcx>, terr: &TypeError<'tcx>);
+    fn report_type_error(&self,
+                         trace: TypeTrace<'tcx>,
+                         terr: &TypeError<'tcx>)
+                         -> DiagnosticBuilder<'tcx>;
 
-    fn check_and_note_conflicting_crates(&self, terr: &TypeError<'tcx>, sp: Span);
+    fn check_and_note_conflicting_crates(&self,
+                                         err: &mut DiagnosticBuilder,
+                                         terr: &TypeError<'tcx>,
+                                         sp: Span);
 
     fn report_and_explain_type_error(&self,
                                      trace: TypeTrace<'tcx>,
@@ -265,17 +277,20 @@ pub trait ErrorReporting<'tcx> {
                                trace_origin: &[(TypeTrace<'tcx>, TypeError<'tcx>)],
                                same_regions: &[SameRegions]);
 
-    fn give_suggestion(&self, same_regions: &[SameRegions]);
+    fn give_suggestion(&self, err: &mut DiagnosticBuilder, same_regions: &[SameRegions]);
 }
 
 trait ErrorReportingHelpers<'tcx> {
     fn report_inference_failure(&self,
-                                var_origin: RegionVariableOrigin);
+                                var_origin: RegionVariableOrigin)
+                                -> DiagnosticBuilder<'tcx>;
 
     fn note_region_origin(&self,
+                          err: &mut DiagnosticBuilder,
                           origin: &SubregionOrigin<'tcx>);
 
     fn give_expl_lifetime_param(&self,
+                                err: &mut DiagnosticBuilder,
                                 decl: &hir::FnDecl,
                                 unsafety: hir::Unsafety,
                                 constness: hir::Constness,
@@ -460,35 +475,47 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
         }
     }
 
-    fn report_type_error(&self, trace: TypeTrace<'tcx>, terr: &TypeError<'tcx>) {
+    fn report_type_error(&self,
+                         trace: TypeTrace<'tcx>,
+                         terr: &TypeError<'tcx>)
+                         -> DiagnosticBuilder<'tcx> {
         let expected_found_str = match self.values_str(&trace.values) {
             Some(v) => v,
             None => {
-                return; /* derived error */
+                return self.tcx.sess.diagnostic().struct_dummy(); /* derived error */
             }
         };
 
-        span_err!(self.tcx.sess, trace.origin.span(), E0308,
-            "{}: {} ({})",
-                 trace.origin,
-                 expected_found_str,
-                 terr);
+        let mut err = struct_span_err!(self.tcx.sess,
+                                       trace.origin.span(),
+                                       E0308,
+                                       "{}: {} ({})",
+                                       trace.origin,
+                                       expected_found_str,
+                                       terr);
 
-        self.check_and_note_conflicting_crates(terr, trace.origin.span());
+        self.check_and_note_conflicting_crates(&mut err, terr, trace.origin.span());
 
         match trace.origin {
             TypeOrigin::MatchExpressionArm(_, arm_span, source) => match source {
-                hir::MatchSource::IfLetDesugar{..} =>
-                    self.tcx.sess.span_note(arm_span, "`if let` arm with an incompatible type"),
-                _ => self.tcx.sess.span_note(arm_span, "match arm with an incompatible type"),
+                hir::MatchSource::IfLetDesugar{..} => {
+                    err.span_note(arm_span, "`if let` arm with an incompatible type");
+                }
+                _ => {
+                    err.span_note(arm_span, "match arm with an incompatible type");
+                }
             },
             _ => ()
         }
+        err
     }
 
     /// Adds a note if the types come from similarly named crates
-    fn check_and_note_conflicting_crates(&self, terr: &TypeError<'tcx>, sp: Span) {
-        let report_path_match = |did1: DefId, did2: DefId| {
+    fn check_and_note_conflicting_crates(&self,
+                                         err: &mut DiagnosticBuilder,
+                                         terr: &TypeError<'tcx>,
+                                         sp: Span) {
+        let report_path_match = |err: &mut DiagnosticBuilder, did1: DefId, did2: DefId| {
             // Only external crates, if either is from a local
             // module we could have false positives
             if !(did1.is_local() || did2.is_local()) && did1.krate != did2.krate {
@@ -502,9 +529,9 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                 // for imported and non-imported crates
                 if exp_path == found_path {
                     let crate_name = self.tcx.sess.cstore.crate_name(did1.krate);
-                    self.tcx.sess.span_note(sp, &format!("Perhaps two different versions \
-                                                          of crate `{}` are being used?",
-                                                          crate_name));
+                    err.span_note(sp, &format!("Perhaps two different versions \
+                                                of crate `{}` are being used?",
+                                               crate_name));
                 }
             }
         };
@@ -517,13 +544,13 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     (&ty::TyStruct(ref exp_adt, _), &ty::TyStruct(ref found_adt, _)) |
                     (&ty::TyEnum(ref exp_adt, _), &ty::TyStruct(ref found_adt, _)) |
                     (&ty::TyStruct(ref exp_adt, _), &ty::TyEnum(ref found_adt, _)) => {
-                        report_path_match(exp_adt.did, found_adt.did);
+                        report_path_match(err, exp_adt.did, found_adt.did);
                     },
                     _ => ()
                 }
             },
             TypeError::Traits(ref exp_found) => {
-                report_path_match(exp_found.expected, exp_found.found);
+                report_path_match(err, exp_found.expected, exp_found.found);
             },
             _ => () // FIXME(#22750) handle traits and stuff
         }
@@ -533,8 +560,9 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                                      trace: TypeTrace<'tcx>,
                                      terr: &TypeError<'tcx>) {
         let span = trace.origin.span();
-        self.report_type_error(trace, terr);
-        self.tcx.note_and_explain_type_err(terr, span);
+        let mut err = self.report_type_error(trace, terr);
+        self.tcx.note_and_explain_type_err(&mut err, terr, span);
+        err.emit();
     }
 
     /// Returns a string of the form "expected `{}`, found `{}`", or None if this is a derived
@@ -584,51 +612,56 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                 format!("the associated type `{}`", p),
         };
 
-        match sub {
+        let mut err = match sub {
             ty::ReFree(ty::FreeRegion {bound_region: ty::BrNamed(..), ..}) => {
                 // Does the required lifetime have a nice name we can print?
-                span_err!(
-                    self.tcx.sess, origin.span(), E0309,
-                    "{} may not live long enough", labeled_user_string);
-                self.tcx.sess.fileline_help(
-                    origin.span(),
-                    &format!(
-                        "consider adding an explicit lifetime bound `{}: {}`...",
-                        bound_kind,
-                        sub));
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               origin.span(),
+                                               E0309,
+                                               "{} may not live long enough",
+                                               labeled_user_string);
+                err.fileline_help(origin.span(),
+                                  &format!("consider adding an explicit lifetime bound `{}: {}`...",
+                                           bound_kind,
+                                           sub));
+                err
             }
 
             ty::ReStatic => {
                 // Does the required lifetime have a nice name we can print?
-                span_err!(
-                    self.tcx.sess, origin.span(), E0310,
-                    "{} may not live long enough", labeled_user_string);
-                self.tcx.sess.fileline_help(
-                    origin.span(),
-                    &format!(
-                        "consider adding an explicit lifetime bound `{}: 'static`...",
-                        bound_kind));
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               origin.span(),
+                                               E0310,
+                                               "{} may not live long enough",
+                                               labeled_user_string);
+                err.fileline_help(origin.span(),
+                                  &format!("consider adding an explicit lifetime \
+                                            bound `{}: 'static`...",
+                                           bound_kind));
+                err
             }
 
             _ => {
                 // If not, be less specific.
-                span_err!(
-                    self.tcx.sess, origin.span(), E0311,
-                    "{} may not live long enough",
-                    labeled_user_string);
-                self.tcx.sess.fileline_help(
-                    origin.span(),
-                    &format!(
-                        "consider adding an explicit lifetime bound for `{}`",
-                        bound_kind));
+                let mut err = struct_span_err!(self.tcx.sess,
+                                               origin.span(),
+                                               E0311,
+                                               "{} may not live long enough",
+                                               labeled_user_string);
+                err.fileline_help(origin.span(),
+                                  &format!("consider adding an explicit lifetime bound for `{}`",
+                                           bound_kind));
                 self.tcx.note_and_explain_region(
+                    &mut err,
                     &format!("{} must be valid for ", labeled_user_string),
                     sub,
                     "...");
+                err
             }
-        }
+        };
 
-        self.note_region_origin(&origin);
+        self.note_region_origin(&mut err, &origin);
+        err.emit();
     }
 
     fn report_concrete_failure(&self,
@@ -641,239 +674,262 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                 self.report_and_explain_type_error(trace, &terr);
             }
             infer::Reborrow(span) => {
-                span_err!(self.tcx.sess, span, E0312,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0312,
                     "lifetime of reference outlines \
                      lifetime of borrowed content...");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "...the reference is valid for ",
                     sub,
                     "...");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "...but the borrowed content is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::ReborrowUpvar(span, ref upvar_id) => {
-                span_err!(self.tcx.sess, span, E0313,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0313,
                     "lifetime of borrowed pointer outlives \
                             lifetime of captured variable `{}`...",
                             self.tcx.local_var_name_str(upvar_id.var_id));
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "...the borrowed pointer is valid for ",
                     sub,
                     "...");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     &format!("...but `{}` is only valid for ",
                              self.tcx.local_var_name_str(upvar_id.var_id)),
                     sup,
                     "");
+                err.emit();
             }
             infer::InfStackClosure(span) => {
-                span_err!(self.tcx.sess, span, E0314,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0314,
                     "closure outlives stack frame");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "...the closure must be valid for ",
                     sub,
                     "...");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "...but the closure's stack frame is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::InvokeClosure(span) => {
-                span_err!(self.tcx.sess, span, E0315,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0315,
                     "cannot invoke closure outside of its lifetime");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the closure is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::DerefPointer(span) => {
-                span_err!(self.tcx.sess, span, E0473,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0473,
                           "dereference of reference outside its lifetime");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the reference is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::FreeVariable(span, id) => {
-                span_err!(self.tcx.sess, span, E0474,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0474,
                           "captured variable `{}` does not outlive the enclosing closure",
                           self.tcx.local_var_name_str(id));
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "captured variable is valid for ",
                     sup,
                     "");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "closure is valid for ",
                     sub,
                     "");
+                err.emit();
             }
             infer::IndexSlice(span) => {
-                span_err!(self.tcx.sess, span, E0475,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0475,
                           "index of slice outside its lifetime");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the slice is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::RelateObjectBound(span) => {
-                span_err!(self.tcx.sess, span, E0476,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0476,
                           "lifetime of the source pointer does not outlive \
                            lifetime bound of the object type");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "object type is valid for ",
                     sub,
                     "");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "source pointer is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::RelateParamBound(span, ty) => {
-                span_err!(self.tcx.sess, span, E0477,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0477,
                           "the type `{}` does not fulfill the required lifetime",
                           self.ty_to_string(ty));
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                                         "type must outlive ",
                                         sub,
                                         "");
+                err.emit();
             }
             infer::RelateRegionParamBound(span) => {
-                span_err!(self.tcx.sess, span, E0478,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0478,
                           "lifetime bound not satisfied");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "lifetime parameter instantiated with ",
                     sup,
                     "");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "but lifetime parameter must outlive ",
                     sub,
                     "");
+                err.emit();
             }
             infer::RelateDefaultParamBound(span, ty) => {
-                span_err!(self.tcx.sess, span, E0479,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0479,
                           "the type `{}` (provided as the value of \
                            a type parameter) is not valid at this point",
                           self.ty_to_string(ty));
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                                         "type must outlive ",
                                         sub,
                                         "");
+                err.emit();
             }
             infer::CallRcvr(span) => {
-                span_err!(self.tcx.sess, span, E0480,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0480,
                           "lifetime of method receiver does not outlive \
                            the method call");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the receiver is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::CallArg(span) => {
-                span_err!(self.tcx.sess, span, E0481,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0481,
                           "lifetime of function argument does not outlive \
                            the function call");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the function argument is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::CallReturn(span) => {
-                span_err!(self.tcx.sess, span, E0482,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0482,
                           "lifetime of return value does not outlive \
                            the function call");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the return value is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::Operand(span) => {
-                span_err!(self.tcx.sess, span, E0483,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0483,
                           "lifetime of operand does not outlive \
                            the operation");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the operand is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::AddrOf(span) => {
-                span_err!(self.tcx.sess, span, E0484,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0484,
                           "reference is not valid at the time of borrow");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the borrow is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::AutoBorrow(span) => {
-                span_err!(self.tcx.sess, span, E0485,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0485,
                           "automatically reference is not valid \
                            at the time of borrow");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the automatic borrow is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::ExprTypeIsNotInScope(t, span) => {
-                span_err!(self.tcx.sess, span, E0486,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0486,
                           "type of expression contains references \
                            that are not valid during the expression: `{}`",
                           self.ty_to_string(t));
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "type is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::SafeDestructor(span) => {
-                span_err!(self.tcx.sess, span, E0487,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0487,
                           "unsafe use of destructor: destructor might be called \
                            while references are dead");
                 // FIXME (22171): terms "super/subregion" are suboptimal
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "superregion: ",
                     sup,
                     "");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "subregion: ",
                     sub,
                     "");
+                err.emit();
             }
             infer::BindingTypeIsNotValidAtDecl(span) => {
-                span_err!(self.tcx.sess, span, E0488,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0488,
                           "lifetime of variable does not enclose its declaration");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the variable is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
             infer::ParameterInScope(_, span) => {
-                span_err!(self.tcx.sess, span, E0489,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0489,
                           "type/lifetime parameter not in scope here");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the parameter is only valid for ",
                     sub,
                     "");
+                err.emit();
             }
             infer::DataBorrowed(ty, span) => {
-                span_err!(self.tcx.sess, span, E0490,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0490,
                           "a value of type `{}` is borrowed for too long",
                           self.ty_to_string(ty));
-                self.tcx.note_and_explain_region("the type is valid for ", sub, "");
-                self.tcx.note_and_explain_region("but the borrow lasts for ", sup, "");
+                self.tcx.note_and_explain_region(&mut err, "the type is valid for ", sub, "");
+                self.tcx.note_and_explain_region(&mut err, "but the borrow lasts for ", sup, "");
+                err.emit();
             }
             infer::ReferenceOutlivesReferent(ty, span) => {
-                span_err!(self.tcx.sess, span, E0491,
+                let mut err = struct_span_err!(self.tcx.sess, span, E0491,
                           "in type `{}`, reference has a longer lifetime \
                            than the data it references",
                           self.ty_to_string(ty));
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "the pointer is valid for ",
                     sub,
                     "");
-                self.tcx.note_and_explain_region(
+                self.tcx.note_and_explain_region(&mut err,
                     "but the referenced data is only valid for ",
                     sup,
                     "");
+                err.emit();
             }
         }
     }
@@ -884,37 +940,42 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                                sub_region: Region,
                                sup_origin: SubregionOrigin<'tcx>,
                                sup_region: Region) {
-        self.report_inference_failure(var_origin);
+        let mut err = self.report_inference_failure(var_origin);
 
-        self.tcx.note_and_explain_region(
+        self.tcx.note_and_explain_region(&mut err,
             "first, the lifetime cannot outlive ",
             sup_region,
             "...");
 
-        self.note_region_origin(&sup_origin);
+        self.note_region_origin(&mut err, &sup_origin);
 
-        self.tcx.note_and_explain_region(
+        self.tcx.note_and_explain_region(&mut err,
             "but, the lifetime must be valid for ",
             sub_region,
             "...");
 
-        self.note_region_origin(&sub_origin);
+        self.note_region_origin(&mut err, &sub_origin);
+        err.emit();
     }
 
     fn report_processed_errors(&self,
                                var_origins: &[RegionVariableOrigin],
                                trace_origins: &[(TypeTrace<'tcx>, TypeError<'tcx>)],
                                same_regions: &[SameRegions]) {
-        for vo in var_origins {
-            self.report_inference_failure(vo.clone());
+        for (i, vo) in var_origins.iter().enumerate() {
+            let mut err = self.report_inference_failure(vo.clone());
+            if i == var_origins.len() - 1 {
+                self.give_suggestion(&mut err, same_regions);
+            }
+            err.emit();
         }
-        self.give_suggestion(same_regions);
+
         for &(ref trace, ref terr) in trace_origins {
             self.report_and_explain_type_error(trace.clone(), terr);
         }
     }
 
-    fn give_suggestion(&self, same_regions: &[SameRegions]) {
+    fn give_suggestion(&self, err: &mut DiagnosticBuilder, same_regions: &[SameRegions]) {
         let scope_id = same_regions[0].scope_id;
         let parent = self.tcx.map.get_parent(scope_id);
         let parent_node = self.tcx.map.find(parent);
@@ -968,7 +1029,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
         let rebuilder = Rebuilder::new(self.tcx, fn_decl, expl_self,
                                        generics, same_regions, &life_giver);
         let (fn_decl, expl_self, generics) = rebuilder.rebuild();
-        self.give_expl_lifetime_param(&fn_decl, unsafety, constness, name,
+        self.give_expl_lifetime_param(err, &fn_decl, unsafety, constness, name,
                                       expl_self.as_ref(), &generics, span);
     }
 }
@@ -1520,6 +1581,7 @@ impl<'a, 'tcx> Rebuilder<'a, 'tcx> {
 
 impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
     fn give_expl_lifetime_param(&self,
+                                err: &mut DiagnosticBuilder,
                                 decl: &hir::FnDecl,
                                 unsafety: hir::Unsafety,
                                 constness: hir::Constness,
@@ -1531,11 +1593,12 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
                                                  opt_explicit_self, generics);
         let msg = format!("consider using an explicit lifetime \
                            parameter as shown: {}", suggested_fn);
-        self.tcx.sess.span_help(span, &msg[..]);
+        err.span_help(span, &msg[..]);
     }
 
     fn report_inference_failure(&self,
-                                var_origin: RegionVariableOrigin) {
+                                var_origin: RegionVariableOrigin)
+                                -> DiagnosticBuilder<'tcx> {
         let br_string = |br: ty::BoundRegion| {
             let mut s = br.to_string();
             if !s.is_empty() {
@@ -1574,13 +1637,13 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
             }
         };
 
-        span_err!(self.tcx.sess, var_origin.span(), E0495,
+        struct_span_err!(self.tcx.sess, var_origin.span(), E0495,
                   "cannot infer an appropriate lifetime{} \
                    due to conflicting requirements",
-                  var_description);
+                  var_description)
     }
 
-    fn note_region_origin(&self, origin: &SubregionOrigin<'tcx>) {
+    fn note_region_origin(&self, err: &mut DiagnosticBuilder, origin: &SubregionOrigin<'tcx>) {
         match *origin {
             infer::Subtype(ref trace) => {
                 let desc = match trace.origin {
@@ -1622,7 +1685,7 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
 
                 match self.values_str(&trace.values) {
                     Some(values_str) => {
-                        self.tcx.sess.span_note(
+                        err.span_note(
                             trace.origin.span(),
                             &format!("...so that {} ({})",
                                     desc, values_str));
@@ -1632,130 +1695,130 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
                         // all, since it is derived, but that would
                         // require more refactoring than I feel like
                         // doing right now. - nmatsakis
-                        self.tcx.sess.span_note(
+                        err.span_note(
                             trace.origin.span(),
                             &format!("...so that {}", desc));
                     }
                 }
             }
             infer::Reborrow(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that reference does not outlive \
                     borrowed content");
             }
             infer::ReborrowUpvar(span, ref upvar_id) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     &format!(
                         "...so that closure can access `{}`",
                         self.tcx.local_var_name_str(upvar_id.var_id)
-                            .to_string()))
+                            .to_string()));
             }
             infer::InfStackClosure(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that closure does not outlive its stack frame");
             }
             infer::InvokeClosure(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that closure is not invoked outside its lifetime");
             }
             infer::DerefPointer(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that pointer is not dereferenced \
                     outside its lifetime");
             }
             infer::FreeVariable(span, id) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     &format!("...so that captured variable `{}` \
                             does not outlive the enclosing closure",
                             self.tcx.local_var_name_str(id)));
             }
             infer::IndexSlice(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that slice is not indexed outside the lifetime");
             }
             infer::RelateObjectBound(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that it can be closed over into an object");
             }
             infer::CallRcvr(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that method receiver is valid for the method call");
             }
             infer::CallArg(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that argument is valid for the call");
             }
             infer::CallReturn(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that return value is valid for the call");
             }
             infer::Operand(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that operand is valid for operation");
             }
             infer::AddrOf(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that reference is valid \
                      at the time of borrow");
             }
             infer::AutoBorrow(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that auto-reference is valid \
                      at the time of borrow");
             }
             infer::ExprTypeIsNotInScope(t, span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     &format!("...so type `{}` of expression is valid during the \
                              expression",
                             self.ty_to_string(t)));
             }
             infer::BindingTypeIsNotValidAtDecl(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that variable is valid at time of its declaration");
             }
             infer::ParameterInScope(_, span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that a type/lifetime parameter is in scope here");
             }
             infer::DataBorrowed(ty, span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     &format!("...so that the type `{}` is not borrowed for too long",
                              self.ty_to_string(ty)));
             }
             infer::ReferenceOutlivesReferent(ty, span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     &format!("...so that the reference type `{}` \
                              does not outlive the data it points at",
                             self.ty_to_string(ty)));
             }
             infer::RelateParamBound(span, t) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     &format!("...so that the type `{}` \
                              will meet its required lifetime bounds",
                             self.ty_to_string(t)));
             }
             infer::RelateDefaultParamBound(span, t) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     &format!("...so that type parameter \
                              instantiated with `{}`, \
@@ -1763,16 +1826,16 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> {
                             self.ty_to_string(t)));
             }
             infer::RelateRegionParamBound(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that the declared lifetime parameter bounds \
                                 are satisfied");
             }
             infer::SafeDestructor(span) => {
-                self.tcx.sess.span_note(
+                err.span_note(
                     span,
                     "...so that references are valid when the destructor \
-                     runs")
+                     runs");
             }
         }
     }
diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs
index d0ffed56635..21a1ee37b7f 100644
--- a/src/librustc/middle/infer/mod.rs
+++ b/src/librustc/middle/infer/mod.rs
@@ -40,6 +40,7 @@ use std::fmt;
 use syntax::ast;
 use syntax::codemap;
 use syntax::codemap::{Span, DUMMY_SP};
+use syntax::errors::DiagnosticBuilder;
 use util::nodemap::{FnvHashMap, FnvHashSet, NodeMap};
 
 use self::combine::CombineFields;
@@ -1269,19 +1270,43 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                                      sp: Span,
                                      mk_msg: M,
                                      actual_ty: String,
-                                     err: Option<&TypeError<'tcx>>) where
-        M: FnOnce(Option<String>, String) -> String,
+                                     err: Option<&TypeError<'tcx>>)
+        where M: FnOnce(Option<String>, String) -> String,
     {
         self.type_error_message_str_with_expected(sp, mk_msg, None, actual_ty, err)
     }
 
+    pub fn type_error_struct_str<M>(&self,
+                                    sp: Span,
+                                    mk_msg: M,
+                                    actual_ty: String,
+                                    err: Option<&TypeError<'tcx>>)
+                                    -> DiagnosticBuilder<'tcx>
+        where M: FnOnce(Option<String>, String) -> String,
+    {
+        self.type_error_struct_str_with_expected(sp, mk_msg, None, actual_ty, err)
+    }
+
     pub fn type_error_message_str_with_expected<M>(&self,
                                                    sp: Span,
                                                    mk_msg: M,
                                                    expected_ty: Option<Ty<'tcx>>,
                                                    actual_ty: String,
-                                                   err: Option<&TypeError<'tcx>>) where
-        M: FnOnce(Option<String>, String) -> String,
+                                                   err: Option<&TypeError<'tcx>>)
+        where M: FnOnce(Option<String>, String) -> String,
+    {
+        self.type_error_struct_str_with_expected(sp, mk_msg, expected_ty, actual_ty, err)
+            .emit();
+    }
+
+    pub fn type_error_struct_str_with_expected<M>(&self,
+                                                  sp: Span,
+                                                  mk_msg: M,
+                                                  expected_ty: Option<Ty<'tcx>>,
+                                                  actual_ty: String,
+                                                  err: Option<&TypeError<'tcx>>)
+                                                  -> DiagnosticBuilder<'tcx>
+        where M: FnOnce(Option<String>, String) -> String,
     {
         debug!("hi! expected_ty = {:?}, actual_ty = {}", expected_ty, actual_ty);
 
@@ -1292,13 +1317,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 format!(" ({})", t_err)
             });
 
-            self.tcx.sess.span_err(sp, &format!("{}{}",
+            let mut db = self.tcx.sess.struct_span_err(sp, &format!("{}{}",
                 mk_msg(resolved_expected.map(|t| self.ty_to_string(t)), actual_ty),
                 error_str));
 
             if let Some(err) = err {
-                self.tcx.note_and_explain_type_err(err, sp)
+                self.tcx.note_and_explain_type_err(&mut db, err, sp);
             }
+            db
+        } else {
+            self.tcx.sess.diagnostic().struct_dummy()
         }
     }
 
@@ -1306,19 +1334,30 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                                  sp: Span,
                                  mk_msg: M,
                                  actual_ty: Ty<'tcx>,
-                                 err: Option<&TypeError<'tcx>>) where
-        M: FnOnce(String) -> String,
+                                 err: Option<&TypeError<'tcx>>)
+        where M: FnOnce(String) -> String,
+    {
+        self.type_error_struct(sp, mk_msg, actual_ty, err).emit();
+    }
+
+    pub fn type_error_struct<M>(&self,
+                                sp: Span,
+                                mk_msg: M,
+                                actual_ty: Ty<'tcx>,
+                                err: Option<&TypeError<'tcx>>)
+                                -> DiagnosticBuilder<'tcx>
+        where M: FnOnce(String) -> String,
     {
         let actual_ty = self.resolve_type_vars_if_possible(&actual_ty);
 
         // Don't report an error if actual type is TyError.
         if actual_ty.references_error() {
-            return;
+            return self.tcx.sess.diagnostic().struct_dummy();
         }
 
-        self.type_error_message_str(sp,
+        self.type_error_struct_str(sp,
             move |_e, a| { mk_msg(a) },
-            self.ty_to_string(actual_ty), err);
+            self.ty_to_string(actual_ty), err)
     }
 
     pub fn report_mismatched_types(&self,
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 540af4ae001..29299f01ed3 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -1500,7 +1500,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                             },
                         _ => false
                     };
-                    span_err!(self.ir.tcx.sess, sp, E0269, "not all control paths return a value");
+                    let mut err = struct_span_err!(self.ir.tcx.sess,
+                                                   sp,
+                                                   E0269,
+                                                   "not all control paths return a value");
                     if ends_with_stmt {
                         let last_stmt = body.stmts.first().unwrap();
                         let original_span = original_sp(self.ir.tcx.sess.codemap(),
@@ -1510,9 +1513,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                             hi: original_span.hi,
                             expn_id: original_span.expn_id
                         };
-                        self.ir.tcx.sess.span_help(
-                            span_semicolon, "consider removing this semicolon:");
+                        err.span_help(span_semicolon, "consider removing this semicolon:");
                     }
+                    err.emit();
                 }
             }
             ty::FnDiverging
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 9b133c54015..2c74f3a82e4 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -345,25 +345,25 @@ impl ShadowKind {
     }
 }
 
-fn signal_shadowing_problem(
-    sess: &Session, name: ast::Name, orig: Original, shadower: Shadower) {
-    if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) {
+fn signal_shadowing_problem(sess: &Session, name: ast::Name, orig: Original, shadower: Shadower) {
+    let mut err = if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) {
         // lifetime/lifetime shadowing is an error
-        span_err!(sess, shadower.span, E0496,
-                  "{} name `{}` shadows a \
-                   {} name that is already in scope",
-                  shadower.kind.desc(), name, orig.kind.desc());
+        struct_span_err!(sess, shadower.span, E0496,
+                         "{} name `{}` shadows a \
+                          {} name that is already in scope",
+                         shadower.kind.desc(), name, orig.kind.desc())
     } else {
         // shadowing involving a label is only a warning, due to issues with
         // labels and lifetimes not being macro-hygienic.
-        sess.span_warn(shadower.span,
-                      &format!("{} name `{}` shadows a \
-                                {} name that is already in scope",
-                               shadower.kind.desc(), name, orig.kind.desc()));
-    }
-    sess.span_note(orig.span,
-                   &format!("shadowed {} `{}` declared here",
-                            orig.kind.desc(), name));
+        sess.struct_span_warn(shadower.span,
+                              &format!("{} name `{}` shadows a \
+                                        {} name that is already in scope",
+                                       shadower.kind.desc(), name, orig.kind.desc()))
+    };
+    err.span_note(orig.span,
+                  &format!("shadowed {} `{}` declared here",
+                           orig.kind.desc(), name));
+    err.emit();
 }
 
 // Adds all labels in `b` to `ctxt.labels_in_fn`, signalling a warning
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index a41ee51fb55..f6af680d441 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -394,17 +394,19 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
                 // This is an 'unmarked' API, which should not exist
                 // in the standard library.
                 if self.tcx.sess.features.borrow().unmarked_api {
-                    self.tcx.sess.span_warn(span, "use of unmarked library feature");
-                    self.tcx.sess.span_note(span, "this is either a bug in the library you are \
+                    self.tcx.sess.struct_span_warn(span, "use of unmarked library feature")
+                                 .span_note(span, "this is either a bug in the library you are \
                                                    using or a bug in the compiler - please \
-                                                   report it in both places");
+                                                   report it in both places")
+                                 .emit()
                 } else {
-                    self.tcx.sess.span_err(span, "use of unmarked library feature");
-                    self.tcx.sess.span_note(span, "this is either a bug in the library you are \
+                    self.tcx.sess.struct_span_err(span, "use of unmarked library feature")
+                                 .span_note(span, "this is either a bug in the library you are \
                                                    using or a bug in the compiler - please \
-                                                   report it in both places");
-                    self.tcx.sess.span_note(span, "use #![feature(unmarked_api)] in the \
-                                                   crate attributes to override this");
+                                                   report it in both places")
+                                 .span_note(span, "use #![feature(unmarked_api)] in the \
+                                                   crate attributes to override this")
+                                 .emit()
                 }
             }
         }
diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs
index 9cf8043be3b..9193b1a09f9 100644
--- a/src/librustc/middle/traits/error_reporting.rs
+++ b/src/librustc/middle/traits/error_reporting.rs
@@ -31,8 +31,9 @@ use middle::ty::fold::TypeFoldable;
 use util::nodemap::{FnvHashMap, FnvHashSet};
 
 use std::fmt;
-use syntax::codemap::Span;
 use syntax::attr::{AttributeMethods, AttrMetaMethods};
+use syntax::codemap::Span;
+use syntax::errors::DiagnosticBuilder;
 
 #[derive(Debug, PartialEq, Eq, Hash)]
 pub struct TraitErrorKey<'tcx> {
@@ -94,12 +95,12 @@ pub fn report_projection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
     // then $X will be unified with TyError, but the error still needs to be
     // reported.
     if !infcx.tcx.sess.has_errors() || !predicate.references_error() {
-        span_err!(
-            infcx.tcx.sess, obligation.cause.span, E0271,
+        let mut err = struct_span_err!(infcx.tcx.sess, obligation.cause.span, E0271,
             "type mismatch resolving `{}`: {}",
             predicate,
             error.err);
-        note_obligation_cause(infcx, obligation);
+        note_obligation_cause(infcx, &mut err, obligation);
+        err.emit();
     }
 }
 
@@ -186,14 +187,15 @@ pub fn report_overflow_error<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
 {
     let predicate =
         infcx.resolve_type_vars_if_possible(&obligation.predicate);
-    span_err!(infcx.tcx.sess, obligation.cause.span, E0275,
-              "overflow evaluating the requirement `{}`",
-              predicate);
+    let mut err = struct_span_err!(infcx.tcx.sess, obligation.cause.span, E0275,
+                                   "overflow evaluating the requirement `{}`",
+                                   predicate);
 
-    suggest_new_overflow_limit(infcx.tcx, obligation.cause.span);
+    suggest_new_overflow_limit(infcx.tcx, &mut err, obligation.cause.span);
 
-    note_obligation_cause(infcx, obligation);
+    note_obligation_cause(infcx, &mut err, obligation);
 
+    err.emit();
     infcx.tcx.sess.abort_if_errors();
     unreachable!();
 }
@@ -218,7 +220,7 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
 
                         if !infcx.tcx.sess.has_errors() || !trait_predicate.references_error() {
                             let trait_ref = trait_predicate.to_poly_trait_ref();
-                            span_err!(
+                            let mut err = struct_span_err!(
                                 infcx.tcx.sess, obligation.cause.span, E0277,
                                 "the trait `{}` is not implemented for the type `{}`",
                                 trait_ref, trait_ref.self_ty());
@@ -228,9 +230,10 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                             let custom_note = report_on_unimplemented(infcx, &trait_ref.0,
                                                                       obligation.cause.span);
                             if let Some(s) = custom_note {
-                                infcx.tcx.sess.fileline_note(obligation.cause.span, &s);
+                                err.fileline_note(obligation.cause.span, &s);
                             }
-                            note_obligation_cause(infcx, obligation);
+                            note_obligation_cause(infcx, &mut err, obligation);
+                            err.emit();
                         }
                     }
 
@@ -238,44 +241,48 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                         let predicate = infcx.resolve_type_vars_if_possible(predicate);
                         let err = infcx.equality_predicate(obligation.cause.span,
                                                            &predicate).err().unwrap();
-                        span_err!(
+                        let mut err = struct_span_err!(
                             infcx.tcx.sess, obligation.cause.span, E0278,
                             "the requirement `{}` is not satisfied (`{}`)",
                             predicate,
                             err);
-                        note_obligation_cause(infcx, obligation);
+                        note_obligation_cause(infcx, &mut err, obligation);
+                        err.emit();
                     }
 
                     ty::Predicate::RegionOutlives(ref predicate) => {
                         let predicate = infcx.resolve_type_vars_if_possible(predicate);
                         let err = infcx.region_outlives_predicate(obligation.cause.span,
                                                                   &predicate).err().unwrap();
-                        span_err!(
+                        let mut err = struct_span_err!(
                             infcx.tcx.sess, obligation.cause.span, E0279,
                             "the requirement `{}` is not satisfied (`{}`)",
                             predicate,
                             err);
-                        note_obligation_cause(infcx, obligation);
+                        note_obligation_cause(infcx, &mut err, obligation);
+                        err.emit();
                     }
 
                     ty::Predicate::Projection(..) | ty::Predicate::TypeOutlives(..) => {
                         let predicate =
                             infcx.resolve_type_vars_if_possible(&obligation.predicate);
-                        span_err!(
+                        let mut err = struct_span_err!(
                             infcx.tcx.sess, obligation.cause.span, E0280,
                             "the requirement `{}` is not satisfied",
                             predicate);
-                        note_obligation_cause(infcx, obligation);
+                        note_obligation_cause(infcx, &mut err, obligation);
+                        err.emit();
                     }
 
                     ty::Predicate::ObjectSafe(trait_def_id) => {
                         let violations = object_safety_violations(
                             infcx.tcx, trait_def_id);
-                        report_object_safety_error(infcx.tcx,
-                                                   obligation.cause.span,
-                                                   trait_def_id,
-                                                   violations);
-                        note_obligation_cause(infcx, obligation);
+                        let mut err = report_object_safety_error(infcx.tcx,
+                                                                 obligation.cause.span,
+                                                                 trait_def_id,
+                                                                 violations);
+                        note_obligation_cause(infcx, &mut err, obligation);
+                        err.emit();
                     }
 
                     ty::Predicate::WellFormed(ty) => {
@@ -296,7 +303,7 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
             let expected_trait_ref = infcx.resolve_type_vars_if_possible(&*expected_trait_ref);
             let actual_trait_ref = infcx.resolve_type_vars_if_possible(&*actual_trait_ref);
             if !actual_trait_ref.self_ty().references_error() {
-                span_err!(
+                let mut err = struct_span_err!(
                     infcx.tcx.sess, obligation.cause.span, E0281,
                     "type mismatch: the type `{}` implements the trait `{}`, \
                      but the trait `{}` is required ({})",
@@ -304,14 +311,17 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                     expected_trait_ref,
                     actual_trait_ref,
                     e);
-                note_obligation_cause(infcx, obligation);
+                note_obligation_cause(infcx, &mut err, obligation);
+                err.emit();
             }
         }
 
         TraitNotObjectSafe(did) => {
             let violations = object_safety_violations(infcx.tcx, did);
-            report_object_safety_error(infcx.tcx, obligation.cause.span, did, violations);
-            note_obligation_cause(infcx, obligation);
+            let mut err = report_object_safety_error(infcx.tcx, obligation.cause.span, did,
+                                                     violations);
+            note_obligation_cause(infcx, &mut err, obligation);
+            err.emit();
         }
     }
 }
@@ -320,8 +330,9 @@ pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>,
                                         span: Span,
                                         trait_def_id: DefId,
                                         violations: Vec<ObjectSafetyViolation>)
+                                        -> DiagnosticBuilder<'tcx>
 {
-    span_err!(
+    let mut err = struct_span_err!(
         tcx.sess, span, E0038,
         "the trait `{}` cannot be made into an object",
         tcx.item_path_str(trait_def_id));
@@ -333,13 +344,13 @@ pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>,
         }
         match violation {
             ObjectSafetyViolation::SizedSelf => {
-                tcx.sess.fileline_note(
+                err.fileline_note(
                     span,
                     "the trait cannot require that `Self : Sized`");
             }
 
             ObjectSafetyViolation::SupertraitSelf => {
-                tcx.sess.fileline_note(
+                err.fileline_note(
                     span,
                     "the trait cannot use `Self` as a type parameter \
                      in the supertrait listing");
@@ -347,7 +358,7 @@ pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>,
 
             ObjectSafetyViolation::Method(method,
                                           MethodViolationCode::StaticMethod) => {
-                tcx.sess.fileline_note(
+                err.fileline_note(
                     span,
                     &format!("method `{}` has no receiver",
                              method.name));
@@ -355,7 +366,7 @@ pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>,
 
             ObjectSafetyViolation::Method(method,
                                           MethodViolationCode::ReferencesSelf) => {
-                tcx.sess.fileline_note(
+                err.fileline_note(
                     span,
                     &format!("method `{}` references the `Self` type \
                               in its arguments or return type",
@@ -364,13 +375,14 @@ pub fn report_object_safety_error<'tcx>(tcx: &ty::ctxt<'tcx>,
 
             ObjectSafetyViolation::Method(method,
                                           MethodViolationCode::Generic) => {
-                tcx.sess.fileline_note(
+                err.fileline_note(
                     span,
                     &format!("method `{}` has generic type parameters",
                              method.name));
             }
         }
     }
+    err
 }
 
 pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
@@ -424,10 +436,12 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
                     {
                         need_type_info(infcx, obligation.cause.span, self_ty);
                     } else {
-                        span_err!(infcx.tcx.sess, obligation.cause.span, E0283,
-                                "type annotations required: cannot resolve `{}`",
-                                predicate);
-                        note_obligation_cause(infcx, obligation);
+                        let mut err = struct_span_err!(infcx.tcx.sess, obligation.cause.span, E0283,
+                                                       "type annotations required: \
+                                                        cannot resolve `{}`",
+                                                       predicate);
+                        note_obligation_cause(infcx, &mut err, obligation);
+                        err.emit();
                     }
                 }
             }
@@ -443,10 +457,11 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
 
         _ => {
             if !infcx.tcx.sess.has_errors() {
-                span_err!(infcx.tcx.sess, obligation.cause.span, E0284,
-                        "type annotations required: cannot resolve `{}`",
-                        predicate);
-                note_obligation_cause(infcx, obligation);
+                let mut err = struct_span_err!(infcx.tcx.sess, obligation.cause.span, E0284,
+                                               "type annotations required: cannot resolve `{}`",
+                                               predicate);
+                note_obligation_cause(infcx, &mut err, obligation);
+                err.emit();
             }
         }
     }
@@ -463,16 +478,19 @@ fn need_type_info<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
 }
 
 fn note_obligation_cause<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
+                                      err: &mut DiagnosticBuilder,
                                       obligation: &Obligation<'tcx, T>)
     where T: fmt::Display
 {
     note_obligation_cause_code(infcx,
+                               err,
                                &obligation.predicate,
                                obligation.cause.span,
                                &obligation.cause.code);
 }
 
 fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
+                                           err: &mut DiagnosticBuilder,
                                            predicate: &T,
                                            cause_span: Span,
                                            cause_code: &ObligationCauseCode<'tcx>)
@@ -482,59 +500,59 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
     match *cause_code {
         ObligationCauseCode::MiscObligation => { }
         ObligationCauseCode::SliceOrArrayElem => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "slice and array elements must have `Sized` type");
         }
         ObligationCauseCode::ProjectionWf(data) => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!("required so that the projection `{}` is well-formed",
                          data));
         }
         ObligationCauseCode::ReferenceOutlivesReferent(ref_ty) => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!("required so that reference `{}` does not outlive its referent",
                          ref_ty));
         }
         ObligationCauseCode::ItemObligation(item_def_id) => {
             let item_name = tcx.item_path_str(item_def_id);
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!("required by `{}`", item_name));
         }
         ObligationCauseCode::ObjectCastObligation(object_ty) => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!(
                     "required for the cast to the object type `{}`",
                     infcx.ty_to_string(object_ty)));
         }
         ObligationCauseCode::RepeatVec => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "the `Copy` trait is required because the \
                  repeated element will be copied");
         }
         ObligationCauseCode::VariableType(_) => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "all local variables must have a statically known size");
         }
         ObligationCauseCode::ReturnType => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "the return type of a function must have a \
                  statically known size");
         }
         ObligationCauseCode::AssignmentLhsSized => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "the left-hand-side of an assignment must have a statically known size");
         }
         ObligationCauseCode::StructInitializerSized => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "structs must have a statically known size to be initialized");
         }
@@ -542,7 +560,7 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
             let def_id = tcx.lang_items.from_builtin_kind(builtin_bound).unwrap();
             let trait_name = tcx.item_path_str(def_id);
             let name = tcx.local_var_name_str(var_id);
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!("the closure that captures `{}` requires that all captured variables \
                           implement the trait `{}`",
@@ -550,37 +568,45 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
                          trait_name));
         }
         ObligationCauseCode::FieldSized => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "only the last field of a struct or enum variant \
                  may have a dynamically sized type");
         }
         ObligationCauseCode::SharedStatic => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 "shared static variables must have a type that implements `Sync`");
         }
         ObligationCauseCode::BuiltinDerivedObligation(ref data) => {
             let parent_trait_ref = infcx.resolve_type_vars_if_possible(&data.parent_trait_ref);
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!("required because it appears within the type `{}`",
                          parent_trait_ref.0.self_ty()));
             let parent_predicate = parent_trait_ref.to_predicate();
-            note_obligation_cause_code(infcx, &parent_predicate, cause_span, &*data.parent_code);
+            note_obligation_cause_code(infcx,
+                                       err,
+                                       &parent_predicate,
+                                       cause_span,
+                                       &*data.parent_code);
         }
         ObligationCauseCode::ImplDerivedObligation(ref data) => {
             let parent_trait_ref = infcx.resolve_type_vars_if_possible(&data.parent_trait_ref);
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!("required because of the requirements on the impl of `{}` for `{}`",
                          parent_trait_ref,
                          parent_trait_ref.0.self_ty()));
             let parent_predicate = parent_trait_ref.to_predicate();
-            note_obligation_cause_code(infcx, &parent_predicate, cause_span, &*data.parent_code);
+            note_obligation_cause_code(infcx,
+                                       err,
+                                       &parent_predicate,
+                                       cause_span,
+                                       &*data.parent_code);
         }
         ObligationCauseCode::CompareImplMethodObligation => {
-            tcx.sess.fileline_note(
+            err.fileline_note(
                 cause_span,
                 &format!("the requirement `{}` appears on the impl method \
                           but not on the corresponding trait method",
@@ -589,10 +615,10 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
     }
 }
 
-fn suggest_new_overflow_limit(tcx: &ty::ctxt, span: Span) {
+fn suggest_new_overflow_limit(tcx: &ty::ctxt, err:&mut DiagnosticBuilder, span: Span) {
     let current_limit = tcx.sess.recursion_limit.get();
     let suggested_limit = current_limit * 2;
-    tcx.sess.fileline_note(
+    err.fileline_note(
         span,
         &format!(
             "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
diff --git a/src/librustc/middle/ty/error.rs b/src/librustc/middle/ty/error.rs
index d9033eaa29f..ab48fd7fb86 100644
--- a/src/librustc/middle/ty/error.rs
+++ b/src/librustc/middle/ty/error.rs
@@ -17,6 +17,7 @@ use std::fmt;
 use syntax::abi;
 use syntax::ast::{self, Name};
 use syntax::codemap::Span;
+use syntax::errors::DiagnosticBuilder;
 
 use rustc_front::hir;
 
@@ -252,27 +253,30 @@ impl<'tcx> ty::TyS<'tcx> {
 }
 
 impl<'tcx> ty::ctxt<'tcx> {
-    pub fn note_and_explain_type_err(&self, err: &TypeError<'tcx>, sp: Span) {
+    pub fn note_and_explain_type_err(&self,
+                                     db: &mut DiagnosticBuilder,
+                                     err: &TypeError<'tcx>,
+                                     sp: Span) {
         use self::TypeError::*;
 
         match err.clone() {
             RegionsDoesNotOutlive(subregion, superregion) => {
-                self.note_and_explain_region("", subregion, "...");
-                self.note_and_explain_region("...does not necessarily outlive ",
+                self.note_and_explain_region(db, "", subregion, "...");
+                self.note_and_explain_region(db, "...does not necessarily outlive ",
                                            superregion, "");
             }
             RegionsNotSame(region1, region2) => {
-                self.note_and_explain_region("", region1, "...");
-                self.note_and_explain_region("...is not the same lifetime as ",
+                self.note_and_explain_region(db, "", region1, "...");
+                self.note_and_explain_region(db, "...is not the same lifetime as ",
                                            region2, "");
             }
             RegionsNoOverlap(region1, region2) => {
-                self.note_and_explain_region("", region1, "...");
-                self.note_and_explain_region("...does not overlap ",
+                self.note_and_explain_region(db, "", region1, "...");
+                self.note_and_explain_region(db, "...does not overlap ",
                                            region2, "");
             }
             RegionsInsufficientlyPolymorphic(_, conc_region) => {
-                self.note_and_explain_region("concrete lifetime that was found is ",
+                self.note_and_explain_region(db, "concrete lifetime that was found is ",
                                            conc_region, "");
             }
             RegionsOverlyPolymorphic(_, ty::ReVar(_)) => {
@@ -280,42 +284,40 @@ impl<'tcx> ty::ctxt<'tcx> {
                 // inference variables, it's not very illuminating.
             }
             RegionsOverlyPolymorphic(_, conc_region) => {
-                self.note_and_explain_region("expected concrete lifetime is ",
+                self.note_and_explain_region(db, "expected concrete lifetime is ",
                                            conc_region, "");
             }
             Sorts(values) => {
                 let expected_str = values.expected.sort_string(self);
                 let found_str = values.found.sort_string(self);
                 if expected_str == found_str && expected_str == "closure" {
-                    self.sess.span_note(sp,
+                    db.span_note(sp,
                         "no two closures, even if identical, have the same type");
-                    self.sess.span_help(sp,
+                    db.span_help(sp,
                         "consider boxing your closure and/or using it as a trait object");
                 }
             },
             TyParamDefaultMismatch(values) => {
                 let expected = values.expected;
                 let found = values.found;
-                self.sess.span_note(sp,
-                                    &format!("conflicting type parameter defaults `{}` and `{}`",
-                                             expected.ty,
-                                             found.ty));
+                db.span_note(sp, &format!("conflicting type parameter defaults `{}` and `{}`",
+                                          expected.ty,
+                                          found.ty));
 
                 match
                     self.map.as_local_node_id(expected.def_id)
                             .and_then(|node_id| self.map.opt_span(node_id))
                 {
                     Some(span) => {
-                        self.sess.span_note(span, "a default was defined here...");
+                        db.span_note(span, "a default was defined here...");
                     }
                     None => {
-                        self.sess.note(
-                            &format!("a default is defined on `{}`",
-                                     self.item_path_str(expected.def_id)));
+                        db.note(&format!("a default is defined on `{}`",
+                                         self.item_path_str(expected.def_id)));
                     }
                 }
 
-                self.sess.span_note(
+                db.span_note(
                     expected.origin_span,
                     "...that was applied to an unconstrained type variable here");
 
@@ -324,18 +326,16 @@ impl<'tcx> ty::ctxt<'tcx> {
                             .and_then(|node_id| self.map.opt_span(node_id))
                 {
                     Some(span) => {
-                        self.sess.span_note(span, "a second default was defined here...");
+                        db.span_note(span, "a second default was defined here...");
                     }
                     None => {
-                        self.sess.note(
-                            &format!("a second default is defined on `{}`",
-                                     self.item_path_str(found.def_id)));
+                        db.note(&format!("a second default is defined on `{}`",
+                                         self.item_path_str(found.def_id)));
                     }
                 }
 
-                self.sess.span_note(
-                    found.origin_span,
-                    "...that also applies to the same type variable here");
+                db.span_note(found.origin_span,
+                             "...that also applies to the same type variable here");
             }
             _ => {}
         }
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index a8587e83736..80b4c1916a8 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -16,7 +16,7 @@ use util::nodemap::{NodeMap, FnvHashMap};
 
 use syntax::ast::{NodeId, NodeIdAssigner, Name};
 use syntax::codemap::Span;
-use syntax::errors;
+use syntax::errors::{self, DiagnosticBuilder};
 use syntax::errors::emitter::{Emitter, BasicEmitter};
 use syntax::diagnostics;
 use syntax::feature_gate;
@@ -80,6 +80,61 @@ pub struct Session {
 }
 
 impl Session {
+    pub fn struct_span_warn<'a>(&'a self,
+                                sp: Span,
+                                msg: &str)
+                                -> DiagnosticBuilder<'a>  {
+        self.diagnostic().struct_span_warn(sp, msg)
+    }
+    pub fn struct_span_warn_with_code<'a>(&'a self,
+                                          sp: Span,
+                                          msg: &str,
+                                          code: &str)
+                                          -> DiagnosticBuilder<'a>  {
+        self.diagnostic().struct_span_warn_with_code(sp, msg, code)
+    }
+    pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a>  {
+        self.diagnostic().struct_warn(msg)
+    }
+    pub fn struct_span_err<'a>(&'a self,
+                               sp: Span,
+                               msg: &str)
+                               -> DiagnosticBuilder<'a>  {
+        match split_msg_into_multilines(msg) {
+            Some(ref msg) => self.diagnostic().struct_span_err(sp, msg),
+            None => self.diagnostic().struct_span_err(sp, msg),
+        }
+    }
+    pub fn struct_span_err_with_code<'a>(&'a self,
+                                         sp: Span,
+                                         msg: &str,
+                                         code: &str)
+                                         -> DiagnosticBuilder<'a>  {
+        match split_msg_into_multilines(msg) {
+            Some(ref msg) => self.diagnostic().struct_span_err_with_code(sp, msg, code),
+            None => self.diagnostic().struct_span_err_with_code(sp, msg, code),
+        }
+    }
+    pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a>  {
+        self.diagnostic().struct_err(msg)
+    }
+    pub fn struct_span_fatal<'a>(&'a self,
+                                 sp: Span,
+                                 msg: &str)
+                                 -> DiagnosticBuilder<'a>  {
+        self.diagnostic().struct_span_fatal(sp, msg)
+    }
+    pub fn struct_span_fatal_with_code<'a>(&'a self,
+                                           sp: Span,
+                                           msg: &str,
+                                           code: &str)
+                                           -> DiagnosticBuilder<'a>  {
+        self.diagnostic().struct_span_fatal_with_code(sp, msg, code)
+    }
+    pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a>  {
+        self.diagnostic().struct_fatal(msg)
+    }
+
     pub fn span_fatal(&self, sp: Span, msg: &str) -> ! {
         panic!(self.diagnostic().span_fatal(sp, msg))
     }
@@ -98,13 +153,13 @@ impl Session {
     }
     pub fn span_err(&self, sp: Span, msg: &str) {
         match split_msg_into_multilines(msg) {
-            Some(msg) => self.diagnostic().span_err(sp, &msg[..]),
+            Some(msg) => self.diagnostic().span_err(sp, &msg),
             None => self.diagnostic().span_err(sp, msg)
         }
     }
     pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) {
         match split_msg_into_multilines(msg) {
-            Some(msg) => self.diagnostic().span_err_with_code(sp, &msg[..], code),
+            Some(msg) => self.diagnostic().span_err_with_code(sp, &msg, code),
             None => self.diagnostic().span_err_with_code(sp, msg, code)
         }
     }
@@ -144,34 +199,6 @@ impl Session {
             None => self.warn(msg),
         }
     }
-    pub fn span_note(&self, sp: Span, msg: &str) {
-        self.diagnostic().span_note(sp, msg)
-    }
-    pub fn span_end_note(&self, sp: Span, msg: &str) {
-        self.diagnostic().span_end_note(sp, msg)
-    }
-
-    /// Prints out a message with a suggested edit of the code.
-    ///
-    /// See `errors::RenderSpan::Suggestion` for more information.
-    pub fn span_suggestion(&self, sp: Span, msg: &str, suggestion: String) {
-        self.diagnostic().span_suggestion(sp, msg, suggestion)
-    }
-    pub fn span_help(&self, sp: Span, msg: &str) {
-        self.diagnostic().span_help(sp, msg)
-    }
-    pub fn fileline_note(&self, sp: Span, msg: &str) {
-        self.diagnostic().fileline_note(sp, msg)
-    }
-    pub fn fileline_help(&self, sp: Span, msg: &str) {
-        self.diagnostic().fileline_help(sp, msg)
-    }
-    pub fn note(&self, msg: &str) {
-        self.diagnostic().note(msg)
-    }
-    pub fn help(&self, msg: &str) {
-        self.diagnostic().help(msg)
-    }
     pub fn opt_span_bug(&self, opt_sp: Option<Span>, msg: &str) -> ! {
         match opt_sp {
             Some(sp) => self.span_bug(sp, msg),
@@ -188,6 +215,12 @@ impl Session {
     pub fn bug(&self, msg: &str) -> ! {
         self.diagnostic().bug(msg)
     }
+    pub fn note_without_error(&self, msg: &str) {
+        self.diagnostic().note_without_error(msg)
+    }
+    pub fn span_note_without_error(&self, sp: Span, msg: &str) {
+        self.diagnostic().span_note_without_error(sp, msg)
+    }
     pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! {
         self.diagnostic().span_unimpl(sp, msg)
     }
diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs
index 3f48051029d..df181aec4ea 100644
--- a/src/librustc_borrowck/borrowck/check_loans.rs
+++ b/src/librustc_borrowck/borrowck/check_loans.rs
@@ -465,44 +465,44 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
                 format!("`{}`", ol)
             };
 
-            match (new_loan.kind, old_loan.kind) {
+            let mut err = match (new_loan.kind, old_loan.kind) {
                 (ty::MutBorrow, ty::MutBorrow) => {
-                    span_err!(self.bccx, new_loan.span, E0499,
-                              "cannot borrow `{}`{} as mutable \
-                               more than once at a time",
-                              nl, new_loan_msg);
+                    struct_span_err!(self.bccx, new_loan.span, E0499,
+                                     "cannot borrow `{}`{} as mutable \
+                                      more than once at a time",
+                                     nl, new_loan_msg)
                 }
 
                 (ty::UniqueImmBorrow, _) => {
-                    span_err!(self.bccx, new_loan.span, E0500,
-                              "closure requires unique access to `{}` \
-                               but {} is already borrowed{}",
-                              nl, ol_pronoun, old_loan_msg);
+                    struct_span_err!(self.bccx, new_loan.span, E0500,
+                                     "closure requires unique access to `{}` \
+                                      but {} is already borrowed{}",
+                                     nl, ol_pronoun, old_loan_msg)
                 }
 
                 (_, ty::UniqueImmBorrow) => {
-                    span_err!(self.bccx, new_loan.span, E0501,
-                              "cannot borrow `{}`{} as {} because \
-                               previous closure requires unique access",
-                              nl, new_loan_msg, new_loan.kind.to_user_str());
+                    struct_span_err!(self.bccx, new_loan.span, E0501,
+                                     "cannot borrow `{}`{} as {} because \
+                                      previous closure requires unique access",
+                                     nl, new_loan_msg, new_loan.kind.to_user_str())
                 }
 
                 (_, _) => {
-                    span_err!(self.bccx, new_loan.span, E0502,
-                              "cannot borrow `{}`{} as {} because \
-                               {} is also borrowed as {}{}",
-                              nl,
-                              new_loan_msg,
-                              new_loan.kind.to_user_str(),
-                              ol_pronoun,
-                              old_loan.kind.to_user_str(),
-                              old_loan_msg);
+                    struct_span_err!(self.bccx, new_loan.span, E0502,
+                                     "cannot borrow `{}`{} as {} because \
+                                      {} is also borrowed as {}{}",
+                                     nl,
+                                     new_loan_msg,
+                                     new_loan.kind.to_user_str(),
+                                     ol_pronoun,
+                                     old_loan.kind.to_user_str(),
+                                     old_loan_msg)
                 }
-            }
+            };
 
             match new_loan.cause {
                 euv::ClosureCapture(span) => {
-                    self.bccx.span_note(
+                    err.span_note(
                         span,
                         &format!("borrow occurs due to use of `{}` in closure",
                                 nl));
@@ -553,15 +553,15 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
                 }
             };
 
-            self.bccx.span_note(
+            err.span_note(
                 old_loan.span,
                 &format!("{}; {}", borrow_summary, rule_summary));
 
             let old_loan_span = self.tcx().map.span(
                 old_loan.kill_scope.node_id(&self.tcx().region_maps));
-            self.bccx.span_end_note(old_loan_span,
-                                    "previous borrow ends here");
-
+            err.span_end_note(old_loan_span,
+                              "previous borrow ends here");
+            err.emit();
             return false;
         }
 
@@ -616,14 +616,14 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
         match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) {
             UseOk => { }
             UseWhileBorrowed(loan_path, loan_span) => {
-                span_err!(self.bccx, span, E0503,
-                          "cannot use `{}` because it was mutably borrowed",
-                          &self.bccx.loan_path_to_string(copy_path));
-                self.bccx.span_note(
-                    loan_span,
-                    &format!("borrow of `{}` occurs here",
-                            &self.bccx.loan_path_to_string(&*loan_path))
-                    );
+                struct_span_err!(self.bccx, span, E0503,
+                                 "cannot use `{}` because it was mutably borrowed",
+                                 &self.bccx.loan_path_to_string(copy_path))
+                    .span_note(loan_span,
+                               &format!("borrow of `{}` occurs here",
+                                       &self.bccx.loan_path_to_string(&*loan_path))
+                               )
+                    .emit();
             }
         }
     }
@@ -639,24 +639,25 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
         match self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow) {
             UseOk => { }
             UseWhileBorrowed(loan_path, loan_span) => {
-                match move_kind {
+                let mut err = match move_kind {
                     move_data::Captured =>
-                        span_err!(self.bccx, span, E0504,
-                                  "cannot move `{}` into closure because it is borrowed",
-                                  &self.bccx.loan_path_to_string(move_path)),
+                        struct_span_err!(self.bccx, span, E0504,
+                                         "cannot move `{}` into closure because it is borrowed",
+                                         &self.bccx.loan_path_to_string(move_path)),
                     move_data::Declared |
                     move_data::MoveExpr |
                     move_data::MovePat =>
-                        span_err!(self.bccx, span, E0505,
-                                  "cannot move out of `{}` because it is borrowed",
-                                  &self.bccx.loan_path_to_string(move_path))
+                        struct_span_err!(self.bccx, span, E0505,
+                                         "cannot move out of `{}` because it is borrowed",
+                                         &self.bccx.loan_path_to_string(move_path))
                 };
 
-                self.bccx.span_note(
+                err.span_note(
                     loan_span,
                     &format!("borrow of `{}` occurs here",
                             &self.bccx.loan_path_to_string(&*loan_path))
                     );
+                err.emit();
             }
         }
     }
@@ -818,12 +819,12 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
                                    span: Span,
                                    loan_path: &LoanPath<'tcx>,
                                    loan: &Loan) {
-        span_err!(self.bccx, span, E0506,
-                  "cannot assign to `{}` because it is borrowed",
-                  self.bccx.loan_path_to_string(loan_path));
-        self.bccx.span_note(
-            loan.span,
-            &format!("borrow of `{}` occurs here",
-                    self.bccx.loan_path_to_string(loan_path)));
+        struct_span_err!(self.bccx, span, E0506,
+                         "cannot assign to `{}` because it is borrowed",
+                         self.bccx.loan_path_to_string(loan_path))
+            .span_note(loan.span,
+                       &format!("borrow of `{}` occurs here",
+                               self.bccx.loan_path_to_string(loan_path)))
+            .emit();
     }
 }
diff --git a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs
index a56a03174f6..4cb9673785e 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs
@@ -15,6 +15,7 @@ use rustc::middle::mem_categorization::InteriorOffsetKind as Kind;
 use rustc::middle::ty;
 use syntax::ast;
 use syntax::codemap;
+use syntax::errors::DiagnosticBuilder;
 use rustc_front::hir;
 
 pub struct MoveErrorCollector<'tcx> {
@@ -68,13 +69,14 @@ fn report_move_errors<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
                                 errors: &Vec<MoveError<'tcx>>) {
     let grouped_errors = group_errors_with_same_origin(errors);
     for error in &grouped_errors {
-        report_cannot_move_out_of(bccx, error.move_from.clone());
+        let mut err = report_cannot_move_out_of(bccx, error.move_from.clone());
         let mut is_first_note = true;
         for move_to in &error.move_to_places {
-            note_move_destination(bccx, move_to.span,
+            note_move_destination(&mut err, move_to.span,
                                   move_to.name, is_first_note);
             is_first_note = false;
         }
+        err.emit();
     }
 }
 
@@ -112,24 +114,28 @@ fn group_errors_with_same_origin<'tcx>(errors: &Vec<MoveError<'tcx>>)
 
 // (keep in sync with gather_moves::check_and_get_illegal_move_origin )
 fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
-                                       move_from: mc::cmt<'tcx>) {
+                                       move_from: mc::cmt<'tcx>)
+                                       -> DiagnosticBuilder<'a> {
     match move_from.cat {
         Categorization::Deref(_, _, mc::BorrowedPtr(..)) |
         Categorization::Deref(_, _, mc::Implicit(..)) |
         Categorization::Deref(_, _, mc::UnsafePtr(..)) |
         Categorization::StaticItem => {
-            span_err!(bccx, move_from.span, E0507,
-                      "cannot move out of {}",
-                      move_from.descriptive_string(bccx.tcx));
+            struct_span_err!(bccx, move_from.span, E0507,
+                             "cannot move out of {}",
+                             move_from.descriptive_string(bccx.tcx))
         }
 
         Categorization::Interior(ref b, mc::InteriorElement(Kind::Index, _)) => {
             let expr = bccx.tcx.map.expect_expr(move_from.id);
             if let hir::ExprIndex(..) = expr.node {
-                span_err!(bccx, move_from.span, E0508,
-                          "cannot move out of type `{}`, \
-                           a non-copy fixed-size array",
-                          b.ty);
+                struct_span_err!(bccx, move_from.span, E0508,
+                                 "cannot move out of type `{}`, \
+                                  a non-copy fixed-size array",
+                                 b.ty)
+            } else {
+                bccx.span_bug(move_from.span, "this path should not cause illegal move");
+                unreachable!();
             }
         }
 
@@ -138,39 +144,41 @@ fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
             match b.ty.sty {
                 ty::TyStruct(def, _) |
                 ty::TyEnum(def, _) if def.has_dtor() => {
-                    span_err!(bccx, move_from.span, E0509,
-                              "cannot move out of type `{}`, \
-                               which defines the `Drop` trait",
-                              b.ty);
+                    struct_span_err!(bccx, move_from.span, E0509,
+                                     "cannot move out of type `{}`, \
+                                      which defines the `Drop` trait",
+                                     b.ty)
                 },
                 _ => {
-                    bccx.span_bug(move_from.span, "this path should not cause illegal move")
+                    bccx.span_bug(move_from.span, "this path should not cause illegal move");
+                    unreachable!();
                 }
             }
         }
         _ => {
-            bccx.span_bug(move_from.span, "this path should not cause illegal move")
+            bccx.span_bug(move_from.span, "this path should not cause illegal move");
+            unreachable!();
         }
     }
 }
 
-fn note_move_destination(bccx: &BorrowckCtxt,
+fn note_move_destination(err: &mut DiagnosticBuilder,
                          move_to_span: codemap::Span,
                          pat_name: ast::Name,
                          is_first_note: bool) {
     if is_first_note {
-        bccx.span_note(
+        err.span_note(
             move_to_span,
             "attempting to move value to here");
-        bccx.fileline_help(
+        err.fileline_help(
             move_to_span,
             &format!("to prevent the move, \
-                     use `ref {0}` or `ref mut {0}` to capture value by \
-                     reference",
-                    pat_name));
+                      use `ref {0}` or `ref mut {0}` to capture value by \
+                      reference",
+                     pat_name));
     } else {
-        bccx.span_note(move_to_span,
-                       &format!("and here (use `ref {0}` or `ref mut {0}`)",
+        err.span_note(move_to_span,
+                      &format!("and here (use `ref {0}` or `ref mut {0}`)",
                                pat_name));
     }
 }
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
index 38ec728e9c1..0a2586755ce 100644
--- a/src/librustc_borrowck/borrowck/mod.rs
+++ b/src/librustc_borrowck/borrowck/mod.rs
@@ -40,6 +40,7 @@ use std::mem;
 use std::rc::Rc;
 use syntax::ast;
 use syntax::codemap::Span;
+use syntax::errors::DiagnosticBuilder;
 
 use rustc_front::hir;
 use rustc_front::hir::{FnDecl, Block};
@@ -591,10 +592,11 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
         }
 
         // General fallback.
-        self.span_err(
+        let mut db = self.struct_span_err(
             err.span,
             &self.bckerr_to_string(&err));
-        self.note_and_explain_bckerr(err);
+        self.note_and_explain_bckerr(&mut db, err);
+        db.emit();
     }
 
     pub fn report_use_of_moved_value<'b>(&self,
@@ -609,16 +611,17 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
             MovedInCapture => "capture",
         };
 
-        let (ol, moved_lp_msg) = match the_move.kind {
+        let (ol, moved_lp_msg, mut err) = match the_move.kind {
             move_data::Declared => {
-                span_err!(
+                let err = struct_span_err!(
                     self.tcx.sess, use_span, E0381,
                     "{} of possibly uninitialized variable: `{}`",
                     verb,
                     self.loan_path_to_string(lp));
 
                 (self.loan_path_to_string(moved_lp),
-                 String::new())
+                 String::new(),
+                 err)
             }
             _ => {
                 // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
@@ -653,11 +656,11 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                 let msg = if !has_fork && partial { "partially " }
                           else if has_fork && !has_common { "collaterally "}
                           else { "" };
-                span_err!(
+                let err = struct_span_err!(
                     self.tcx.sess, use_span, E0382,
                     "{} of {}moved value: `{}`",
                     verb, msg, nl);
-                (ol, moved_lp_msg)
+                (ol, moved_lp_msg, err)
             }
         };
 
@@ -684,7 +687,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                 // multiple times. Avoid printing the same span and adjust the wording so it makes
                 // more sense that it's from multiple evalutations.
                 if expr_span == use_span {
-                    self.tcx.sess.note(
+                    err.note(
                         &format!("`{}` was previously moved here{} because it has type `{}`, \
                                   which is {}",
                                  ol,
@@ -692,7 +695,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                                  expr_ty,
                                  suggestion));
                 } else {
-                    self.tcx.sess.span_note(
+                    err.span_note(
                         expr_span,
                         &format!("`{}` moved here{} because it has type `{}`, which is {}",
                                  ol,
@@ -705,7 +708,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
             move_data::MovePat => {
                 let pat_ty = self.tcx.node_id_to_type(the_move.id);
                 let span = self.tcx.map.span(the_move.id);
-                self.tcx.sess.span_note(span,
+                err.span_note(span,
                     &format!("`{}` moved here{} because it has type `{}`, \
                              which is moved by default",
                             ol,
@@ -713,14 +716,14 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                             pat_ty));
                 match self.tcx.sess.codemap().span_to_snippet(span) {
                     Ok(string) => {
-                        self.tcx.sess.span_suggestion(
+                        err.span_suggestion(
                             span,
                             &format!("if you would like to borrow the value instead, \
                                       use a `ref` binding as shown:"),
                             format!("ref {}", string));
                     },
                     Err(_) => {
-                        self.tcx.sess.fileline_help(span,
+                        err.fileline_help(span,
                             "use `ref` to override");
                     },
                 }
@@ -746,7 +749,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                                     expr_ty,
                                     ("moved by default",
                                      "make a copy and capture that instead to override"));
-                self.tcx.sess.span_note(
+                err.span_note(
                     expr_span,
                     &format!("`{}` moved into closure environment here{} because it \
                             has type `{}`, which is {}",
@@ -754,9 +757,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                             moved_lp_msg,
                             moved_lp.ty,
                             suggestion));
-                self.tcx.sess.fileline_help(expr_span, help);
+                err.fileline_help(expr_span, help);
             }
         }
+        err.emit();
 
         fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>,
                                     span: Span,
@@ -791,35 +795,36 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                                                 lp: &LoanPath<'tcx>,
                                                 assign:
                                                 &move_data::Assignment) {
-        span_err!(
+        struct_span_err!(
             self.tcx.sess, span, E0384,
             "re-assignment of immutable variable `{}`",
-            self.loan_path_to_string(lp));
-        self.tcx.sess.span_note(assign.span, "prior assignment occurs here");
+            self.loan_path_to_string(lp))
+            .span_note(assign.span, "prior assignment occurs here")
+            .emit();
     }
 
     pub fn span_err(&self, s: Span, m: &str) {
         self.tcx.sess.span_err(s, m);
     }
 
-    pub fn span_err_with_code(&self, s: Span, msg: &str, code: &str) {
-        self.tcx.sess.span_err_with_code(s, msg, code);
+    pub fn struct_span_err(&self, s: Span, m: &str) -> DiagnosticBuilder<'a> {
+        self.tcx.sess.struct_span_err(s, m)
     }
 
-    pub fn span_bug(&self, s: Span, m: &str) {
-        self.tcx.sess.span_bug(s, m);
-    }
-
-    pub fn span_note(&self, s: Span, m: &str) {
-        self.tcx.sess.span_note(s, m);
+    pub fn struct_span_err_with_code(&self,
+                                     s: Span,
+                                     msg: &str,
+                                     code: &str)
+                                     -> DiagnosticBuilder<'a> {
+        self.tcx.sess.struct_span_err_with_code(s, msg, code)
     }
 
-    pub fn span_end_note(&self, s: Span, m: &str) {
-        self.tcx.sess.span_end_note(s, m);
+    pub fn span_err_with_code(&self, s: Span, msg: &str, code: &str) {
+        self.tcx.sess.span_err_with_code(s, msg, code);
     }
 
-    pub fn fileline_help(&self, s: Span, m: &str) {
-        self.tcx.sess.fileline_help(s, m);
+    pub fn span_bug(&self, s: Span, m: &str) {
+        self.tcx.sess.span_bug(s, m);
     }
 
     pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String {
@@ -913,19 +918,19 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
             }
         };
 
-        match cause {
+        let mut err = match cause {
             mc::AliasableOther => {
-                span_err!(
+                struct_span_err!(
                     self.tcx.sess, span, E0385,
-                    "{} in an aliasable location", prefix);
+                    "{} in an aliasable location", prefix)
             }
             mc::AliasableReason::UnaliasableImmutable => {
-                span_err!(
+                struct_span_err!(
                     self.tcx.sess, span, E0386,
-                    "{} in an immutable container", prefix);
+                    "{} in an immutable container", prefix)
             }
             mc::AliasableClosure(id) => {
-                span_err!(
+                let mut err = struct_span_err!(
                     self.tcx.sess, span, E0387,
                     "{} in a captured outer variable in an `Fn` closure", prefix);
                 if let BorrowViolation(euv::ClosureCapture(_)) = kind {
@@ -933,31 +938,32 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                     // happen for nested closures, so we know the enclosing
                     // closure incorrectly accepts an `Fn` while it needs to
                     // be `FnMut`.
-                    span_help!(self.tcx.sess, self.tcx.map.span(id),
+                    span_help!(&mut err, self.tcx.map.span(id),
                            "consider changing this to accept closures that implement `FnMut`");
                 } else {
-                    span_help!(self.tcx.sess, self.tcx.map.span(id),
+                    span_help!(&mut err, self.tcx.map.span(id),
                            "consider changing this closure to take self by mutable reference");
                 }
+                err
             }
             mc::AliasableStatic |
             mc::AliasableStaticMut => {
-                span_err!(
+                struct_span_err!(
                     self.tcx.sess, span, E0388,
-                    "{} in a static location", prefix);
+                    "{} in a static location", prefix)
             }
             mc::AliasableBorrowed => {
-                span_err!(
+                struct_span_err!(
                     self.tcx.sess, span, E0389,
-                    "{} in a `&` reference", prefix);
+                    "{} in a `&` reference", prefix)
             }
-        }
+        };
 
         if is_closure {
-            self.tcx.sess.fileline_help(
-                span,
-                "closures behind references must be called via `&mut`");
+            err.fileline_help(span,
+                              "closures behind references must be called via `&mut`");
         }
+        err.emit();
     }
 
     fn report_out_of_scope_escaping_closure_capture(&self,
@@ -966,34 +972,30 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
     {
         let cmt_path_or_string = self.cmt_to_path_or_string(&err.cmt);
 
-        span_err!(
-            self.tcx.sess, err.span, E0373,
-            "closure may outlive the current function, \
-             but it borrows {}, \
-             which is owned by the current function",
-            cmt_path_or_string);
-
-        self.tcx.sess.span_note(
-            capture_span,
-            &format!("{} is borrowed here",
-                     cmt_path_or_string));
-
         let suggestion =
             match self.tcx.sess.codemap().span_to_snippet(err.span) {
                 Ok(string) => format!("move {}", string),
                 Err(_) => format!("move |<args>| <body>")
             };
 
-        self.tcx.sess.span_suggestion(
-            err.span,
-            &format!("to force the closure to take ownership of {} \
-                      (and any other referenced variables), \
-                      use the `move` keyword, as shown:",
-                     cmt_path_or_string),
-            suggestion);
+        struct_span_err!(self.tcx.sess, err.span, E0373,
+                         "closure may outlive the current function, \
+                          but it borrows {}, \
+                          which is owned by the current function",
+                         cmt_path_or_string)
+            .span_note(capture_span,
+                       &format!("{} is borrowed here",
+                                cmt_path_or_string))
+            .span_suggestion(err.span,
+                             &format!("to force the closure to take ownership of {} \
+                                       (and any other referenced variables), \
+                                       use the `move` keyword, as shown:",
+                                      cmt_path_or_string),
+                             suggestion)
+            .emit();
     }
 
-    pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) {
+    pub fn note_and_explain_bckerr(&self, db: &mut DiagnosticBuilder, err: BckError<'tcx>) {
         let code = err.code;
         match code {
             err_mutbl => {
@@ -1007,7 +1009,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                             _ => unreachable!()
                         };
                         if kind == ty::FnClosureKind {
-                            self.tcx.sess.span_help(
+                            db.span_help(
                                 self.tcx.map.span(upvar_id.closure_expr_id),
                                 "consider changing this closure to take \
                                  self by mutable reference");
@@ -1017,7 +1019,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                         if let Categorization::Local(local_id) = err.cmt.cat {
                             let span = self.tcx.map.span(local_id);
                             if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
-                                self.tcx.sess.span_suggestion(
+                                db.span_suggestion(
                                     span,
                                     &format!("to make the {} mutable, use `mut` as shown:",
                                              self.cmt_to_string(&err.cmt)),
@@ -1030,16 +1032,18 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
 
             err_out_of_scope(super_scope, sub_scope) => {
                 self.tcx.note_and_explain_region(
+                    db,
                     "reference must be valid for ",
                     sub_scope,
                     "...");
                 self.tcx.note_and_explain_region(
+                    db,
                     "...but borrowed value is only valid for ",
                     super_scope,
                     "");
                 if let Some(span) = statement_scope_span(self.tcx, super_scope) {
-                    self.tcx.sess.span_help(span,
-                        "consider using a `let` binding to increase its lifetime");
+                    db.span_help(span,
+                                 "consider using a `let` binding to increase its lifetime");
                 }
             }
 
@@ -1051,11 +1055,13 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                     None => self.cmt_to_string(&*err.cmt),
                 };
                 self.tcx.note_and_explain_region(
+                    db,
                     &format!("{} would have to be valid for ",
                             descr),
                     loan_scope,
                     "...");
                 self.tcx.note_and_explain_region(
+                    db,
                     &format!("...but {} is only valid for ", descr),
                     ptr_scope,
                     "");
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 1ac1b5978cf..27740b8fc5c 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -1001,8 +1001,9 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<c
                              None
                          }
                          _ => {
-                             session.span_err(a.span, "`crate_type` requires a value");
-                             session.note("for example: `#![crate_type=\"lib\"]`");
+                             session.struct_span_err(a.span, "`crate_type` requires a value")
+                                 .note("for example: `#![crate_type=\"lib\"]`")
+                                 .emit();
                              None
                          }
                      }
diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs
index 50a672a4b69..7bcf3276da7 100644
--- a/src/librustc_driver/test.rs
+++ b/src/librustc_driver/test.rs
@@ -60,7 +60,7 @@ struct ExpectErrorEmitter {
 fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) {
     match lvl {
         Level::Bug | Level::Fatal | Level::Error => {}
-        Level::Warning | Level::Note | Level::Help => {
+        _ => {
             return;
         }
     }
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index f27cc629791..09e6f454fb4 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -752,19 +752,19 @@ impl LateLintPass for UnconditionalRecursion {
         // no break */ }`) shouldn't be linted unless it actually
         // recurs.
         if !reached_exit_without_self_call && !self_call_spans.is_empty() {
-            cx.span_lint(UNCONDITIONAL_RECURSION, sp,
-                         "function cannot return without recurring");
+            let mut db = cx.struct_span_lint(UNCONDITIONAL_RECURSION, sp,
+                                             "function cannot return without recurring");
 
             // FIXME #19668: these could be span_lint_note's instead of this manual guard.
             if cx.current_level(UNCONDITIONAL_RECURSION) != Level::Allow {
-                let sess = cx.sess();
                 // offer some help to the programmer.
                 for call in &self_call_spans {
-                    sess.span_note(*call, "recursive call site")
+                    db.span_note(*call, "recursive call site");
                 }
-                sess.fileline_help(sp, "a `loop` may express intention \
-                                        better if this is on purpose")
+                db.fileline_help(sp, "a `loop` may express intention \
+                                      better if this is on purpose");
             }
+            db.emit();
         }
 
         // all done
diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs
index 4420da5f9b8..9122148a8cc 100644
--- a/src/librustc_metadata/creader.rs
+++ b/src/librustc_metadata/creader.rs
@@ -35,6 +35,7 @@ use syntax::codemap::{self, Span, mk_sp, Pos};
 use syntax::parse;
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
+use syntax::errors::FatalError;
 use syntax::parse::token::InternedString;
 use syntax::util::small_vector::SmallVector;
 use rustc_front::intravisit::Visitor;
@@ -504,7 +505,10 @@ impl<'a> CrateReader<'a> {
                 let lo = p.span.lo;
                 let body = match p.parse_all_token_trees() {
                     Ok(body) => body,
-                    Err(err) => panic!(err),
+                    Err(mut err) => {
+                        err.emit();
+                        panic!(FatalError);
+                    }
                 };
                 let span = mk_sp(lo, p.last_span.hi);
                 p.abort_if_errors();
diff --git a/src/librustc_metadata/loader.rs b/src/librustc_metadata/loader.rs
index d1892b87f8b..40665beaa5a 100644
--- a/src/librustc_metadata/loader.rs
+++ b/src/librustc_metadata/loader.rs
@@ -226,7 +226,7 @@ use rustc_llvm as llvm;
 use rustc_llvm::{False, ObjectFile, mk_section_iter};
 use rustc_llvm::archive_ro::ArchiveRO;
 use syntax::codemap::Span;
-use syntax::errors::Handler;
+use syntax::errors::DiagnosticBuilder;
 use rustc_back::target::Target;
 
 use std::cmp;
@@ -315,38 +315,38 @@ impl<'a> Context<'a> {
             &Some(ref r) => format!(" which `{}` depends on",
                                     r.ident)
         };
-        if !self.rejected_via_hash.is_empty() {
-            span_err!(self.sess, self.span, E0460,
-                      "found possibly newer version of crate `{}`{}",
-                      self.ident, add);
+        let mut err = if !self.rejected_via_hash.is_empty() {
+            struct_span_err!(self.sess, self.span, E0460,
+                             "found possibly newer version of crate `{}`{}",
+                             self.ident, add)
         } else if !self.rejected_via_triple.is_empty() {
-            span_err!(self.sess, self.span, E0461,
-                      "couldn't find crate `{}` with expected target triple {}{}",
-                      self.ident, self.triple, add);
+            struct_span_err!(self.sess, self.span, E0461,
+                             "couldn't find crate `{}` with expected target triple {}{}",
+                             self.ident, self.triple, add)
         } else if !self.rejected_via_kind.is_empty() {
-            span_err!(self.sess, self.span, E0462,
-                      "found staticlib `{}` instead of rlib or dylib{}",
-                      self.ident, add);
+            struct_span_err!(self.sess, self.span, E0462,
+                             "found staticlib `{}` instead of rlib or dylib{}",
+                             self.ident, add)
         } else {
-            span_err!(self.sess, self.span, E0463,
-                      "can't find crate for `{}`{}",
-                      self.ident, add);
-        }
+            struct_span_err!(self.sess, self.span, E0463,
+                             "can't find crate for `{}`{}",
+                             self.ident, add)
+        };
 
         if !self.rejected_via_triple.is_empty() {
             let mismatches = self.rejected_via_triple.iter();
             for (i, &CrateMismatch{ ref path, ref got }) in mismatches.enumerate() {
-                self.sess.fileline_note(self.span,
+                err.fileline_note(self.span,
                     &format!("crate `{}`, path #{}, triple {}: {}",
                             self.ident, i+1, got, path.display()));
             }
         }
         if !self.rejected_via_hash.is_empty() {
-            self.sess.span_note(self.span, "perhaps this crate needs \
+            err.span_note(self.span, "perhaps this crate needs \
                                             to be recompiled?");
             let mismatches = self.rejected_via_hash.iter();
             for (i, &CrateMismatch{ ref path, .. }) in mismatches.enumerate() {
-                self.sess.fileline_note(self.span,
+                err.fileline_note(self.span,
                     &format!("crate `{}` path #{}: {}",
                             self.ident, i+1, path.display()));
             }
@@ -354,7 +354,7 @@ impl<'a> Context<'a> {
                 &None => {}
                 &Some(ref r) => {
                     for (i, path) in r.paths().iter().enumerate() {
-                        self.sess.fileline_note(self.span,
+                        err.fileline_note(self.span,
                             &format!("crate `{}` path #{}: {}",
                                     r.ident, i+1, path.display()));
                     }
@@ -362,15 +362,17 @@ impl<'a> Context<'a> {
             }
         }
         if !self.rejected_via_kind.is_empty() {
-            self.sess.fileline_help(self.span, "please recompile this crate using \
-                                            --crate-type lib");
+            err.fileline_help(self.span, "please recompile this crate using \
+                                          --crate-type lib");
             let mismatches = self.rejected_via_kind.iter();
             for (i, &CrateMismatch { ref path, .. }) in mismatches.enumerate() {
-                self.sess.fileline_note(self.span,
-                                        &format!("crate `{}` path #{}: {}",
-                                                 self.ident, i+1, path.display()));
+                err.fileline_note(self.span,
+                                  &format!("crate `{}` path #{}: {}",
+                                           self.ident, i+1, path.display()));
             }
         }
+
+        err.emit();
         self.sess.abort_if_errors();
     }
 
@@ -480,29 +482,30 @@ impl<'a> Context<'a> {
             0 => None,
             1 => Some(libraries.into_iter().next().unwrap()),
             _ => {
-                span_err!(self.sess, self.span, E0464,
-                          "multiple matching crates for `{}`",
-                          self.crate_name);
-                self.sess.note("candidates:");
+                let mut err = struct_span_err!(self.sess, self.span, E0464,
+                                               "multiple matching crates for `{}`",
+                                               self.crate_name);
+                err.note("candidates:");
                 for lib in &libraries {
                     match lib.dylib {
                         Some((ref p, _)) => {
-                            self.sess.note(&format!("path: {}",
-                                                   p.display()));
+                            err.note(&format!("path: {}",
+                                              p.display()));
                         }
                         None => {}
                     }
                     match lib.rlib {
                         Some((ref p, _)) => {
-                            self.sess.note(&format!("path: {}",
-                                                    p.display()));
+                            err.note(&format!("path: {}",
+                                              p.display()));
                         }
                         None => {}
                     }
                     let data = lib.metadata.as_slice();
                     let name = decoder::get_crate_name(data);
-                    note_crate_name(self.sess.diagnostic(), &name);
+                    note_crate_name(&mut err, &name);
                 }
+                err.emit();
                 None
             }
         }
@@ -533,6 +536,7 @@ impl<'a> Context<'a> {
             }
         }
 
+        let mut err: Option<DiagnosticBuilder> = None;
         for (lib, kind) in m {
             info!("{} reading metadata from: {}", flavor, lib.display());
             let metadata = match get_metadata_section(self.target, &lib) {
@@ -555,27 +559,37 @@ impl<'a> Context<'a> {
             // candidates have the same hash, so they're not actually
             // duplicates that we should warn about.
             if ret.is_some() && self.hash.is_none() {
-                span_err!(self.sess, self.span, E0465,
-                          "multiple {} candidates for `{}` found",
-                          flavor, self.crate_name);
-                self.sess.span_note(self.span,
-                                    &format!(r"candidate #1: {}",
-                                            ret.as_ref().unwrap().0
-                                               .display()));
+                let mut e = struct_span_err!(self.sess, self.span, E0465,
+                                             "multiple {} candidates for `{}` found",
+                                             flavor, self.crate_name);
+                e.span_note(self.span,
+                            &format!(r"candidate #1: {}",
+                                     ret.as_ref().unwrap().0
+                                        .display()));
+                if let Some(ref mut e) = err {
+                    e.emit();
+                }
+                err = Some(e);
                 error = 1;
                 ret = None;
             }
             if error > 0 {
                 error += 1;
-                self.sess.span_note(self.span,
-                                    &format!(r"candidate #{}: {}", error,
-                                            lib.display()));
+                err.as_mut().unwrap().span_note(self.span,
+                                                &format!(r"candidate #{}: {}", error,
+                                                         lib.display()));
                 continue
             }
             *slot = Some(metadata);
             ret = Some((lib, kind));
         }
-        return if error > 0 {None} else {ret}
+
+        if error > 0 {
+            err.unwrap().emit();
+            None
+        } else {
+            ret
+        }
     }
 
     fn crate_matches(&mut self, crate_data: &[u8], libpath: &Path) -> bool {
@@ -662,10 +676,11 @@ impl<'a> Context<'a> {
                         return true
                     }
                 }
-                sess.err(&format!("extern location for {} is of an unknown type: {}",
-                                 self.crate_name, loc.display()));
-                sess.help(&format!("file name should be lib*.rlib or {}*.{}",
-                                   dylibname.0, dylibname.1));
+                sess.struct_err(&format!("extern location for {} is of an unknown type: {}",
+                                         self.crate_name, loc.display()))
+                    .help(&format!("file name should be lib*.rlib or {}*.{}",
+                                   dylibname.0, dylibname.1))
+                    .emit();
                 false
             });
 
@@ -699,8 +714,8 @@ impl<'a> Context<'a> {
     }
 }
 
-pub fn note_crate_name(diag: &Handler, name: &str) {
-    diag.note(&format!("crate name: {}", name));
+pub fn note_crate_name(err: &mut DiagnosticBuilder, name: &str) {
+    err.note(&format!("crate name: {}", name));
 }
 
 impl ArchiveMetadata {
diff --git a/src/librustc_plugin/build.rs b/src/librustc_plugin/build.rs
index 476425a75c2..5adde4304f5 100644
--- a/src/librustc_plugin/build.rs
+++ b/src/librustc_plugin/build.rs
@@ -46,10 +46,11 @@ pub fn find_plugin_registrar(diagnostic: &errors::Handler,
             Some(node_id)
         },
         _ => {
-            diagnostic.err("multiple plugin registration functions found");
+            let mut e = diagnostic.struct_err("multiple plugin registration functions found");
             for &(_, span) in &finder.registrars {
-                diagnostic.span_note(span, "one is here");
+                e.span_note(span, "one is here");
             }
+            e.emit();
             diagnostic.abort_if_errors();
             unreachable!();
         }
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index d1a894c61f7..1cc2482a39e 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -597,13 +597,11 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
         match result {
             None => true,
             Some((span, msg, note)) => {
-                self.tcx.sess.span_err(span, &msg[..]);
-                match note {
-                    Some((span, msg)) => {
-                        self.tcx.sess.span_note(span, &msg[..])
-                    }
-                    None => {},
+                let mut err = self.tcx.sess.struct_span_err(span, &msg[..]);
+                if let Some((span, msg)) = note {
+                    err.span_note(span, &msg[..]);
                 }
+                err.emit();
                 false
             },
         }
@@ -1028,10 +1026,12 @@ impl<'a, 'tcx> SanePrivacyVisitor<'a, 'tcx> {
     fn check_sane_privacy(&self, item: &hir::Item) {
         let check_inherited = |sp, vis, note: &str| {
             if vis != hir::Inherited {
-                span_err!(self.tcx.sess, sp, E0449, "unnecessary visibility qualifier");
+                let mut err = struct_span_err!(self.tcx.sess, sp, E0449,
+                                               "unnecessary visibility qualifier");
                 if !note.is_empty() {
-                    self.tcx.sess.span_note(sp, note);
+                    err.span_note(sp, note);
                 }
+                err.emit();
             }
         };
 
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index 11d09fa3e9a..1d3f2b79844 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -24,7 +24,7 @@ use {names_to_string, module_to_string};
 use ParentLink::{self, ModuleParentLink, BlockParentLink};
 use Resolver;
 use resolve_imports::Shadowable;
-use {resolve_error, ResolutionError};
+use {resolve_error, resolve_struct_error, ResolutionError};
 
 use self::DuplicateCheckingMode::*;
 
@@ -137,12 +137,16 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
 
                 // Record an error here by looking up the namespace that had the duplicate
                 let ns_str = match ns { TypeNS => "type or module", ValueNS => "value" };
-                resolve_error(self, sp, ResolutionError::DuplicateDefinition(ns_str, name));
+                let mut err = resolve_struct_error(self,
+                                                   sp,
+                                                   ResolutionError::DuplicateDefinition(ns_str,
+                                                                                        name));
 
                 if let Some(sp) = child[ns].span() {
                     let note = format!("first definition of {} `{}` here", ns_str, name);
-                    self.session.span_note(sp, &note);
+                    err.span_note(sp, &note);
                 }
+                err.emit();
                 child
             }
         }
@@ -253,13 +257,13 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                                                     })
                                                     .collect::<Vec<Span>>();
                         if mod_spans.len() > 1 {
-                            resolve_error(self,
+                            let mut e = resolve_struct_error(self,
                                           mod_spans[0],
                                           ResolutionError::SelfImportCanOnlyAppearOnceInTheList);
                             for other_span in mod_spans.iter().skip(1) {
-                                self.session
-                                    .span_note(*other_span, "another `self` import appears here");
+                                e.span_note(*other_span, "another `self` import appears here");
                             }
+                            e.emit();
                         }
 
                         for source_item in source_items {
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 3647fd5ce19..5a17f2528c8 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -63,8 +63,9 @@ use syntax::ast;
 use syntax::ast::{CRATE_NODE_ID, Name, NodeId, CrateNum, TyIs, TyI8, TyI16, TyI32, TyI64};
 use syntax::ast::{TyUs, TyU8, TyU16, TyU32, TyU64, TyF64, TyF32};
 use syntax::attr::AttrMetaMethods;
-use syntax::parse::token::{self, special_names, special_idents};
 use syntax::codemap::{self, Span, Pos};
+use syntax::errors::DiagnosticBuilder;
+use syntax::parse::token::{self, special_names, special_idents};
 use syntax::util::lev_distance::find_best_match_for_name;
 
 use rustc_front::intravisit::{self, FnKind, Visitor};
@@ -215,210 +216,221 @@ pub enum UnresolvedNameContext {
 fn resolve_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
                                        span: syntax::codemap::Span,
                                        resolution_error: ResolutionError<'b>) {
+    resolve_struct_error(resolver, span, resolution_error).emit();
+}
+
+fn resolve_struct_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
+                                              span: syntax::codemap::Span,
+                                              resolution_error: ResolutionError<'b>)
+                                              -> DiagnosticBuilder<'a> {
     if !resolver.emit_errors {
-        return;
+        return resolver.session.diagnostic().struct_dummy();
     }
+
     match resolution_error {
         ResolutionError::TypeParametersFromOuterFunction => {
-            span_err!(resolver.session,
-                      span,
-                      E0401,
-                      "can't use type parameters from outer function; try using a local type \
-                       parameter instead");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0401,
+                             "can't use type parameters from outer function; try using a local \
+                              type parameter instead")
         }
         ResolutionError::OuterTypeParameterContext => {
-            span_err!(resolver.session,
-                      span,
-                      E0402,
-                      "cannot use an outer type parameter in this context");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0402,
+                             "cannot use an outer type parameter in this context")
         }
         ResolutionError::NameAlreadyUsedInTypeParameterList(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0403,
-                      "the name `{}` is already used for a type parameter in this type parameter \
-                       list",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0403,
+                             "the name `{}` is already used for a type parameter in this type \
+                              parameter list",
+                             name)
         }
         ResolutionError::IsNotATrait(name) => {
-            span_err!(resolver.session, span, E0404, "`{}` is not a trait", name);
+            struct_span_err!(resolver.session, span, E0404, "`{}` is not a trait", name)
         }
         ResolutionError::UndeclaredTraitName(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0405,
-                      "use of undeclared trait name `{}`",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0405,
+                             "use of undeclared trait name `{}`",
+                             name)
         }
         ResolutionError::UndeclaredAssociatedType => {
-            span_err!(resolver.session, span, E0406, "undeclared associated type");
+            struct_span_err!(resolver.session, span, E0406, "undeclared associated type")
         }
         ResolutionError::MethodNotMemberOfTrait(method, trait_) => {
-            span_err!(resolver.session,
-                      span,
-                      E0407,
-                      "method `{}` is not a member of trait `{}`",
-                      method,
-                      trait_);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0407,
+                             "method `{}` is not a member of trait `{}`",
+                             method,
+                             trait_)
         }
         ResolutionError::TypeNotMemberOfTrait(type_, trait_) => {
-            span_err!(resolver.session,
-                      span,
-                      E0437,
-                      "type `{}` is not a member of trait `{}`",
-                      type_,
-                      trait_);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0437,
+                             "type `{}` is not a member of trait `{}`",
+                             type_,
+                             trait_)
         }
         ResolutionError::ConstNotMemberOfTrait(const_, trait_) => {
-            span_err!(resolver.session,
-                      span,
-                      E0438,
-                      "const `{}` is not a member of trait `{}`",
-                      const_,
-                      trait_);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0438,
+                             "const `{}` is not a member of trait `{}`",
+                             const_,
+                             trait_)
         }
         ResolutionError::VariableNotBoundInPattern(variable_name, pattern_number) => {
-            span_err!(resolver.session,
-                      span,
-                      E0408,
-                      "variable `{}` from pattern #1 is not bound in pattern #{}",
-                      variable_name,
-                      pattern_number);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0408,
+                             "variable `{}` from pattern #1 is not bound in pattern #{}",
+                             variable_name,
+                             pattern_number)
         }
         ResolutionError::VariableBoundWithDifferentMode(variable_name, pattern_number) => {
-            span_err!(resolver.session,
-                      span,
-                      E0409,
-                      "variable `{}` is bound with different mode in pattern #{} than in pattern \
-                       #1",
-                      variable_name,
-                      pattern_number);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0409,
+                             "variable `{}` is bound with different mode in pattern #{} than in \
+                              pattern #1",
+                             variable_name,
+                             pattern_number)
         }
         ResolutionError::VariableNotBoundInParentPattern(variable_name, pattern_number) => {
-            span_err!(resolver.session,
-                      span,
-                      E0410,
-                      "variable `{}` from pattern #{} is not bound in pattern #1",
-                      variable_name,
-                      pattern_number);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0410,
+                             "variable `{}` from pattern #{} is not bound in pattern #1",
+                             variable_name,
+                             pattern_number)
         }
         ResolutionError::SelfUsedOutsideImplOrTrait => {
-            span_err!(resolver.session,
-                      span,
-                      E0411,
-                      "use of `Self` outside of an impl or trait");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0411,
+                             "use of `Self` outside of an impl or trait")
         }
         ResolutionError::UseOfUndeclared(kind, name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0412,
-                      "use of undeclared {} `{}`",
-                      kind,
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0412,
+                             "use of undeclared {} `{}`",
+                             kind,
+                             name)
         }
         ResolutionError::DeclarationShadowsEnumVariantOrUnitLikeStruct(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0413,
-                      "declaration of `{}` shadows an enum variant or unit-like struct in scope",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0413,
+                             "declaration of `{}` shadows an enum variant \
+                              or unit-like struct in scope",
+                             name)
         }
         ResolutionError::OnlyIrrefutablePatternsAllowedHere(did, name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0414,
-                      "only irrefutable patterns allowed here");
-            resolver.session.span_note(span,
-                                       "there already is a constant in scope sharing the same \
-                                        name as this pattern");
+            let mut err = struct_span_err!(resolver.session,
+                                           span,
+                                           E0414,
+                                           "only irrefutable patterns allowed here");
+            err.span_note(span,
+                          "there already is a constant in scope sharing the same \
+                           name as this pattern");
             if let Some(sp) = resolver.ast_map.span_if_local(did) {
-                resolver.session.span_note(sp, "constant defined here");
+                err.span_note(sp, "constant defined here");
             }
             if let Some(directive) = resolver.current_module
                                              .import_resolutions
                                              .borrow()
                                              .get(&name) {
                 let item = resolver.ast_map.expect_item(directive.value_ns.id);
-                resolver.session.span_note(item.span, "constant imported here");
+                err.span_note(item.span, "constant imported here");
             }
+            err
         }
         ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => {
-            span_err!(resolver.session,
-                      span,
-                      E0415,
-                      "identifier `{}` is bound more than once in this parameter list",
-                      identifier);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0415,
+                             "identifier `{}` is bound more than once in this parameter list",
+                             identifier)
         }
         ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => {
-            span_err!(resolver.session,
-                      span,
-                      E0416,
-                      "identifier `{}` is bound more than once in the same pattern",
-                      identifier);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0416,
+                             "identifier `{}` is bound more than once in the same pattern",
+                             identifier)
         }
         ResolutionError::StaticVariableReference => {
-            span_err!(resolver.session,
-                      span,
-                      E0417,
-                      "static variables cannot be referenced in a pattern, use a `const` instead");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0417,
+                             "static variables cannot be referenced in a pattern, use a \
+                              `const` instead")
         }
         ResolutionError::NotAnEnumVariantStructOrConst(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0418,
-                      "`{}` is not an enum variant, struct or const",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0418,
+                             "`{}` is not an enum variant, struct or const",
+                             name)
         }
         ResolutionError::UnresolvedEnumVariantStructOrConst(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0419,
-                      "unresolved enum variant, struct or const `{}`",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0419,
+                             "unresolved enum variant, struct or const `{}`",
+                             name)
         }
         ResolutionError::NotAnAssociatedConst(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0420,
-                      "`{}` is not an associated const",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0420,
+                             "`{}` is not an associated const",
+                             name)
         }
         ResolutionError::UnresolvedAssociatedConst(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0421,
-                      "unresolved associated const `{}`",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0421,
+                             "unresolved associated const `{}`",
+                             name)
         }
         ResolutionError::DoesNotNameAStruct(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0422,
-                      "`{}` does not name a structure",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0422,
+                             "`{}` does not name a structure",
+                             name)
         }
         ResolutionError::StructVariantUsedAsFunction(path_name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0423,
-                      "`{}` is the name of a struct or struct variant, but this expression uses \
-                       it like a function name",
-                      path_name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0423,
+                             "`{}` is the name of a struct or struct variant, but this expression \
+                             uses it like a function name",
+                             path_name)
         }
         ResolutionError::SelfNotAvailableInStaticMethod => {
-            span_err!(resolver.session,
-                      span,
-                      E0424,
-                      "`self` is not available in a static method. Maybe a `self` argument is \
-                       missing?");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0424,
+                             "`self` is not available in a static method. Maybe a `self` \
+                             argument is missing?")
         }
         ResolutionError::UnresolvedName(path, msg, context) => {
-            span_err!(resolver.session,
-                      span,
-                      E0425,
-                      "unresolved name `{}`{}",
-                      path,
-                      msg);
+            let mut err = struct_span_err!(resolver.session,
+                                           span,
+                                           E0425,
+                                           "unresolved name `{}`{}",
+                                           path,
+                                           msg);
 
             match context {
                 UnresolvedNameContext::Other => {} // no help available
@@ -448,75 +460,77 @@ fn resolve_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>,
                     }
 
                     if !help_msg.is_empty() {
-                        resolver.session.fileline_help(span, &help_msg);
+                        err.fileline_help(span, &help_msg);
                     }
                 }
             }
+            err
         }
         ResolutionError::UndeclaredLabel(name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0426,
-                      "use of undeclared label `{}`",
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0426,
+                             "use of undeclared label `{}`",
+                             name)
         }
         ResolutionError::CannotUseRefBindingModeWith(descr) => {
-            span_err!(resolver.session,
-                      span,
-                      E0427,
-                      "cannot use `ref` binding mode with {}",
-                      descr);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0427,
+                             "cannot use `ref` binding mode with {}",
+                             descr)
         }
         ResolutionError::DuplicateDefinition(namespace, name) => {
-            span_err!(resolver.session,
-                      span,
-                      E0428,
-                      "duplicate definition of {} `{}`",
-                      namespace,
-                      name);
+            struct_span_err!(resolver.session,
+                             span,
+                             E0428,
+                             "duplicate definition of {} `{}`",
+                             namespace,
+                             name)
         }
         ResolutionError::SelfImportsOnlyAllowedWithin => {
-            span_err!(resolver.session,
-                      span,
-                      E0429,
-                      "{}",
-                      "`self` imports are only allowed within a { } list");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0429,
+                             "{}",
+                             "`self` imports are only allowed within a { } list")
         }
         ResolutionError::SelfImportCanOnlyAppearOnceInTheList => {
-            span_err!(resolver.session,
-                      span,
-                      E0430,
-                      "`self` import can only appear once in the list");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0430,
+                             "`self` import can only appear once in the list")
         }
         ResolutionError::SelfImportOnlyInImportListWithNonEmptyPrefix => {
-            span_err!(resolver.session,
-                      span,
-                      E0431,
-                      "`self` import can only appear in an import list with a non-empty prefix");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0431,
+                             "`self` import can only appear in an import list with a \
+                              non-empty prefix")
         }
         ResolutionError::UnresolvedImport(name) => {
             let msg = match name {
                 Some((n, p)) => format!("unresolved import `{}`{}", n, p),
                 None => "unresolved import".to_owned(),
             };
-            span_err!(resolver.session, span, E0432, "{}", msg);
+            struct_span_err!(resolver.session, span, E0432, "{}", msg)
         }
         ResolutionError::FailedToResolve(msg) => {
-            span_err!(resolver.session, span, E0433, "failed to resolve. {}", msg);
+            struct_span_err!(resolver.session, span, E0433, "failed to resolve. {}", msg)
         }
         ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => {
-            span_err!(resolver.session,
-                      span,
-                      E0434,
-                      "{}",
-                      "can't capture dynamic environment in a fn item; use the || { ... } \
-                       closure form instead");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0434,
+                             "{}",
+                             "can't capture dynamic environment in a fn item; use the || { ... } \
+                              closure form instead")
         }
         ResolutionError::AttemptToUseNonConstantValueInConstant => {
-            span_err!(resolver.session,
-                      span,
-                      E0435,
-                      "attempt to use a non-constant value in a constant");
+            struct_span_err!(resolver.session,
+                             span,
+                             E0435,
+                             "attempt to use a non-constant value in a constant")
         }
     }
 }
@@ -2180,16 +2194,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 debug!("(resolving trait) found trait def: {:?}", path_res);
                 Ok(path_res)
             } else {
-                resolve_error(self,
-                              trait_path.span,
-                              ResolutionError::IsNotATrait(&*path_names_to_string(trait_path,
-                                                                                  path_depth)));
+                let mut err =
+                    resolve_struct_error(self,
+                                  trait_path.span,
+                                  ResolutionError::IsNotATrait(&*path_names_to_string(trait_path,
+                                                                                      path_depth)));
 
                 // If it's a typedef, give a note
                 if let DefTy(..) = path_res.base_def {
-                    self.session
-                        .span_note(trait_path.span, "`type` aliases cannot be used for traits");
+                    err.span_note(trait_path.span,
+                                  "`type` aliases cannot be used for traits");
                 }
+                err.emit();
                 Err(())
             }
         } else {
@@ -3470,17 +3486,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     if let DefVariant(_, _, true) = path_res.base_def {
                         let path_name = path_names_to_string(path, 0);
 
-                        resolve_error(self,
-                                      expr.span,
-                                      ResolutionError::StructVariantUsedAsFunction(&*path_name));
+                        let mut err = resolve_struct_error(self,
+                                        expr.span,
+                                        ResolutionError::StructVariantUsedAsFunction(&*path_name));
 
                         let msg = format!("did you mean to write: `{} {{ /* fields */ }}`?",
                                           path_name);
                         if self.emit_errors {
-                            self.session.fileline_help(expr.span, &msg);
+                            err.fileline_help(expr.span, &msg);
                         } else {
-                            self.session.span_help(expr.span, &msg);
+                            err.span_help(expr.span, &msg);
                         }
+                        err.emit();
                         self.record_def(expr.id, err_path_resolution());
                     } else {
                         // Write the result into the def map.
@@ -3510,20 +3527,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     self.record_def(expr.id, err_path_resolution());
                     match type_res.map(|r| r.base_def) {
                         Some(DefTy(struct_id, _)) if self.structs.contains_key(&struct_id) => {
-                            resolve_error(
-                                    self,
-                                    expr.span,
-                                    ResolutionError::StructVariantUsedAsFunction(
-                                        &*path_name)
-                                );
+                            let mut err = resolve_struct_error(self,
+                                expr.span,
+                                ResolutionError::StructVariantUsedAsFunction(&*path_name));
 
                             let msg = format!("did you mean to write: `{} {{ /* fields */ }}`?",
                                               path_name);
                             if self.emit_errors {
-                                self.session.fileline_help(expr.span, &msg);
+                                err.fileline_help(expr.span, &msg);
                             } else {
-                                self.session.span_help(expr.span, &msg);
+                                err.span_help(expr.span, &msg);
                             }
+                            err.emit();
                         }
                         _ => {
                             // Keep reporting some errors even if they're ignored above.
diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs
index 8b35f4d68df..40bf55efde6 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -454,8 +454,9 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                         let note_msg = format!("Consider marking `{}` as `pub` in the imported \
                                                 module",
                                                source);
-                        span_err!(self.resolver.session, directive.span, E0364, "{}", &msg);
-                        self.resolver.session.span_note(directive.span, &note_msg);
+                        struct_span_err!(self.resolver.session, directive.span, E0364, "{}", &msg)
+                            .span_note(directive.span, &note_msg)
+                            .emit();
                         pub_err = true;
                     }
                     if directive.is_public && child_name_bindings.value_ns.
@@ -479,8 +480,9 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                         let msg = format!("`{}` is private, and cannot be reexported", source);
                         let note_msg = format!("Consider declaring module `{}` as a `pub mod`",
                                                source);
-                        span_err!(self.resolver.session, directive.span, E0365, "{}", &msg);
-                        self.resolver.session.span_note(directive.span, &note_msg);
+                        struct_span_err!(self.resolver.session, directive.span, E0365, "{}", &msg)
+                            .span_note(directive.span, &note_msg)
+                            .emit();
                     }
                     if !pub_err && directive.is_public && child_name_bindings.type_ns.
                                                     defined_with(DefModifiers::PRIVATE_VARIANT) {
@@ -959,19 +961,20 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                     }
                     ValueNS => "value",
                 };
-                span_err!(self.resolver.session,
-                          import_span,
-                          E0252,
-                          "a {} named `{}` has already been imported in this module",
-                          ns_word,
-                          name);
                 let use_id = import_resolution[namespace].id;
                 let item = self.resolver.ast_map.expect_item(use_id);
-                // item is syntax::ast::Item;
-                span_note!(self.resolver.session,
+                let mut err = struct_span_err!(self.resolver.session,
+                                               import_span,
+                                               E0252,
+                                               "a {} named `{}` has already been imported \
+                                                in this module",
+                                               ns_word,
+                                               name);
+                span_note!(&mut err,
                            item.span,
                            "previous import of `{}` here",
                            name);
+                err.emit();
             }
             Some(_) | None => {}
         }
@@ -1022,14 +1025,16 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
         match import.value_ns.target {
             Some(ref target) if target.shadowable != Shadowable::Always => {
                 if let Some(ref value) = *name_bindings.value_ns.borrow() {
-                    span_err!(self.resolver.session,
-                              import_span,
-                              E0255,
-                              "import `{}` conflicts with value in this module",
-                              name);
+                    let mut err = struct_span_err!(self.resolver.session,
+                                                   import_span,
+                                                   E0255,
+                                                   "import `{}` conflicts with \
+                                                    value in this module",
+                                                   name);
                     if let Some(span) = value.span {
-                        self.resolver.session.span_note(span, "conflicting value here");
+                        err.span_note(span, "conflicting value here");
                     }
+                    err.emit();
                 }
             }
             Some(_) | None => {}
@@ -1045,15 +1050,16 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> {
                             ("trait in this module", "note conflicting trait here"),
                         _ => ("type in this module", "note conflicting type here"),
                     };
-                    span_err!(self.resolver.session,
-                              import_span,
-                              E0256,
-                              "import `{}` conflicts with {}",
-                              name,
-                              what);
+                    let mut err = struct_span_err!(self.resolver.session,
+                                                   import_span,
+                                                   E0256,
+                                                   "import `{}` conflicts with {}",
+                                                   name,
+                                                   what);
                     if let Some(span) = ty.span {
-                        self.resolver.session.span_note(span, note);
+                        err.span_note(span, note);
                     }
+                    err.emit();
                 }
             }
             Some(_) | None => {}
diff --git a/src/librustc_trans/back/archive.rs b/src/librustc_trans/back/archive.rs
index f5431554a75..85060858823 100644
--- a/src/librustc_trans/back/archive.rs
+++ b/src/librustc_trans/back/archive.rs
@@ -406,11 +406,12 @@ impl<'a> ArchiveBuilder<'a> {
             Ok(prog) => {
                 let o = prog.wait_with_output().unwrap();
                 if !o.status.success() {
-                    sess.err(&format!("{:?} failed with: {}", cmd, o.status));
-                    sess.note(&format!("stdout ---\n{}",
-                                       str::from_utf8(&o.stdout).unwrap()));
-                    sess.note(&format!("stderr ---\n{}",
-                                       str::from_utf8(&o.stderr).unwrap()));
+                    sess.struct_err(&format!("{:?} failed with: {}", cmd, o.status))
+                        .note(&format!("stdout ---\n{}",
+                                       str::from_utf8(&o.stdout).unwrap()))
+                        .note(&format!("stderr ---\n{}",
+                                       str::from_utf8(&o.stderr).unwrap()))
+                        .emit();
                     sess.abort_if_errors();
                 }
                 o
diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs
index 1e5bac85ffc..8446db65a4c 100644
--- a/src/librustc_trans/back/link.rs
+++ b/src/librustc_trans/back/link.rs
@@ -817,10 +817,10 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path,
     ab.build();
 
     if !all_native_libs.is_empty() {
-        sess.note("link against the following native artifacts when linking against \
-                  this static library");
-        sess.note("the order and any duplication can be significant on some platforms, \
-                  and so may need to be preserved");
+        sess.note_without_error("link against the following native artifacts when linking against \
+                                 this static library");
+        sess.note_without_error("the order and any duplication can be significant on some \
+                                 platforms, and so may need to be preserved");
     }
 
     for &(kind, ref lib) in &all_native_libs {
@@ -829,7 +829,7 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path,
             NativeLibraryKind::NativeUnknown => "library",
             NativeLibraryKind::NativeFramework => "framework",
         };
-        sess.note(&format!("{}: {}", name, *lib));
+        sess.note_without_error(&format!("{}: {}", name, *lib));
     }
 }
 
@@ -902,13 +902,14 @@ fn link_natively(sess: &Session, dylib: bool,
                     })
             }
             if !prog.status.success() {
-                sess.err(&format!("linking with `{}` failed: {}",
-                                 pname,
-                                 prog.status));
-                sess.note(&format!("{:?}", &cmd));
                 let mut output = prog.stderr.clone();
                 output.extend_from_slice(&prog.stdout);
-                sess.note(&*escape_string(&output[..]));
+                sess.struct_err(&format!("linking with `{}` failed: {}",
+                                         pname,
+                                         prog.status))
+                    .note(&format!("{:?}", &cmd))
+                    .note(&*escape_string(&output[..]))
+                    .emit();
                 sess.abort_if_errors();
             }
             info!("linker stderr:\n{}", escape_string(&prog.stderr[..]));
diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs
index 8505c3968ee..85419a07250 100644
--- a/src/librustc_trans/back/lto.rs
+++ b/src/librustc_trans/back/lto.rs
@@ -29,8 +29,9 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
            name_extra: &str,
            output_names: &config::OutputFilenames) {
     if sess.opts.cg.prefer_dynamic {
-        sess.err("cannot prefer dynamic linking when performing LTO");
-        sess.note("only 'staticlib' and 'bin' outputs are supported with LTO");
+        sess.struct_err("cannot prefer dynamic linking when performing LTO")
+            .note("only 'staticlib' and 'bin' outputs are supported with LTO")
+            .emit();
         sess.abort_if_errors();
     }
 
diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs
index 67eff1ca19f..9d0a83fe363 100644
--- a/src/librustc_trans/back/write.rs
+++ b/src/librustc_trans/back/write.rs
@@ -359,8 +359,9 @@ unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext<'a>,
         }
 
         None => {
-            cgcx.handler.err(msg);
-            cgcx.handler.note("build without -C codegen-units for more exact errors");
+            cgcx.handler.struct_err(msg)
+                        .note("build without -C codegen-units for more exact errors")
+                        .emit();
         }
     }
 }
@@ -397,11 +398,11 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo
 
             if enabled {
                 let loc = llvm::debug_loc_to_string(llcx, opt.debug_loc);
-                cgcx.handler.note(&format!("optimization {} for {} at {}: {}",
-                                           opt.kind.describe(),
-                                           pass_name,
-                                           if loc.is_empty() { "[unknown]" } else { &*loc },
-                                           llvm::twine_to_string(opt.message)));
+                cgcx.handler.note_without_error(&format!("optimization {} for {} at {}: {}",
+                                                opt.kind.describe(),
+                                                pass_name,
+                                                if loc.is_empty() { "[unknown]" } else { &*loc },
+                                                llvm::twine_to_string(opt.message)));
             }
         }
 
@@ -931,13 +932,15 @@ pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
     match cmd.output() {
         Ok(prog) => {
             if !prog.status.success() {
-                sess.err(&format!("linking with `{}` failed: {}",
-                                 pname,
-                                 prog.status));
-                sess.note(&format!("{:?}", &cmd));
                 let mut note = prog.stderr.clone();
                 note.extend_from_slice(&prog.stdout);
-                sess.note(str::from_utf8(&note[..]).unwrap());
+
+                sess.struct_err(&format!("linking with `{}` failed: {}",
+                                         pname,
+                                         prog.status))
+                    .note(&format!("{:?}", &cmd))
+                    .note(str::from_utf8(&note[..]).unwrap())
+                    .emit();
                 sess.abort_if_errors();
             }
         },
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 838a5435d4f..996e72bdad3 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -2181,17 +2181,20 @@ fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &hir::EnumDef, sp: Span,
             }
     );
 
+    // FIXME(#30505) Should use logging for this.
     if print_info {
         let llty = type_of::sizing_type_of(ccx, ty);
 
         let sess = &ccx.tcx().sess;
-        sess.span_note(sp, &*format!("total size: {} bytes", llsize_of_real(ccx, llty)));
+        sess.span_note_without_error(sp,
+                                     &*format!("total size: {} bytes", llsize_of_real(ccx, llty)));
         match *avar {
             adt::General(..) => {
                 for (i, var) in enum_def.variants.iter().enumerate() {
                     ccx.tcx()
                        .sess
-                       .span_note(var.span, &*format!("variant data: {} bytes", sizes[i]));
+                       .span_note_without_error(var.span,
+                                                &*format!("variant data: {} bytes", sizes[i]));
                 }
             }
             _ => {}
@@ -2203,16 +2206,16 @@ fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &hir::EnumDef, sp: Span,
     if !is_allow && largest > slargest * 3 && slargest > 0 {
         // Use lint::raw_emit_lint rather than sess.add_lint because the lint-printing
         // pass for the latter already ran.
-        lint::raw_emit_lint(&ccx.tcx().sess,
-                            lint::builtin::VARIANT_SIZE_DIFFERENCES,
-                            *lvlsrc.unwrap(),
-                            Some(sp),
-                            &format!("enum variant is more than three times larger ({} bytes) \
-                                      than the next largest (ignoring padding)",
-                                     largest));
-
-        ccx.sess().span_note(enum_def.variants[largest_index].span,
-                             "this variant is the largest");
+        lint::raw_struct_lint(&ccx.tcx().sess,
+                              lint::builtin::VARIANT_SIZE_DIFFERENCES,
+                              *lvlsrc.unwrap(),
+                              Some(sp),
+                              &format!("enum variant is more than three times larger ({} bytes) \
+                                        than the next largest (ignoring padding)",
+                                       largest))
+            .span_note(enum_def.variants[largest_index].span,
+                       "this variant is the largest")
+            .emit();
     }
 }
 
@@ -2489,9 +2492,10 @@ pub fn create_entry_wrapper(ccx: &CrateContext, sp: Span, main_llfn: ValueRef) {
         let llfty = Type::func(&[ccx.int_type(), Type::i8p(ccx).ptr_to()], &ccx.int_type());
 
         let llfn = declare::define_cfn(ccx, "main", llfty, ccx.tcx().mk_nil()).unwrap_or_else(|| {
-            ccx.sess().span_err(sp, "entry symbol `main` defined multiple times");
             // FIXME: We should be smart and show a better diagnostic here.
-            ccx.sess().help("did you use #[no_mangle] on `fn main`? Use #[start] instead");
+            ccx.sess().struct_span_err(sp, "entry symbol `main` defined multiple times")
+                      .help("did you use #[no_mangle] on `fn main`? Use #[start] instead")
+                      .emit();
             ccx.sess().abort_if_errors();
             panic!();
         });
diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs
index 9012ecaa213..e6ad5ac4f06 100644
--- a/src/librustc_trans/trans/foreign.rs
+++ b/src/librustc_trans/trans/foreign.rs
@@ -460,12 +460,13 @@ fn gate_simd_ffi(tcx: &ty::ctxt, decl: &hir::FnDecl, ty: &ty::BareFnTy) {
     if !tcx.sess.features.borrow().simd_ffi {
         let check = |ast_ty: &hir::Ty, ty: ty::Ty| {
             if ty.is_simd() {
-                tcx.sess.span_err(ast_ty.span,
+                tcx.sess.struct_span_err(ast_ty.span,
                               &format!("use of SIMD type `{}` in FFI is highly experimental and \
                                         may result in invalid code",
-                                       pprust::ty_to_string(ast_ty)));
-                tcx.sess.fileline_help(ast_ty.span,
-                                   "add #![feature(simd_ffi)] to the crate attributes to enable");
+                                       pprust::ty_to_string(ast_ty)))
+                    .fileline_help(ast_ty.span,
+                                   "add #![feature(simd_ffi)] to the crate attributes to enable")
+                    .emit();
             }
         };
         let sig = &ty.sig.0;
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 95d85964044..751c18dfbc4 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -68,6 +68,7 @@ use util::nodemap::FnvHashSet;
 
 use syntax::{abi, ast};
 use syntax::codemap::{Span, Pos};
+use syntax::errors::DiagnosticBuilder;
 use syntax::feature_gate::{GateIssue, emit_feature_err};
 use syntax::parse::token;
 
@@ -195,7 +196,7 @@ pub fn ast_region_to_region(tcx: &ty::ctxt, lifetime: &hir::Lifetime)
 }
 
 fn report_elision_failure(
-    tcx: &ty::ctxt,
+    db: &mut DiagnosticBuilder,
     default_span: Span,
     params: Vec<ElisionFailureInfo>)
 {
@@ -233,26 +234,26 @@ fn report_elision_failure(
     }
 
     if len == 0 {
-        fileline_help!(tcx.sess, default_span,
+        fileline_help!(db, default_span,
                        "this function's return type contains a borrowed value, but \
                         there is no value for it to be borrowed from");
-        fileline_help!(tcx.sess, default_span,
+        fileline_help!(db, default_span,
                        "consider giving it a 'static lifetime");
     } else if !any_lifetimes {
-        fileline_help!(tcx.sess, default_span,
+        fileline_help!(db, default_span,
                        "this function's return type contains a borrowed value with \
                         an elided lifetime, but the lifetime cannot be derived from \
                         the arguments");
-        fileline_help!(tcx.sess, default_span,
+        fileline_help!(db, default_span,
                        "consider giving it an explicit bounded or 'static \
                         lifetime");
     } else if len == 1 {
-        fileline_help!(tcx.sess, default_span,
+        fileline_help!(db, default_span,
                        "this function's return type contains a borrowed value, but \
                         the signature does not say which {} it is borrowed from",
                        m);
     } else {
-        fileline_help!(tcx.sess, default_span,
+        fileline_help!(db, default_span,
                        "this function's return type contains a borrowed value, but \
                         the signature does not say whether it is borrowed from {}",
                        m);
@@ -273,11 +274,12 @@ pub fn opt_ast_region_to_region<'tcx>(
         None => match rscope.anon_regions(default_span, 1) {
             Ok(rs) => rs[0],
             Err(params) => {
-                span_err!(this.tcx().sess, default_span, E0106,
-                          "missing lifetime specifier");
+                let mut err = struct_span_err!(this.tcx().sess, default_span, E0106,
+                                               "missing lifetime specifier");
                 if let Some(params) = params {
-                    report_elision_failure(this.tcx(), default_span, params);
+                    report_elision_failure(&mut err, default_span, params);
                 }
+                err.emit();
                 ty::ReStatic
             }
         }
@@ -1044,9 +1046,9 @@ fn ast_ty_to_trait_ref<'tcx>(this: &AstConv<'tcx>,
             }
         }
         _ => {
-            span_err!(this.tcx().sess, ty.span, E0178,
-                      "expected a path on the left-hand side of `+`, not `{}`",
-                      pprust::ty_to_string(ty));
+            let mut err = struct_span_err!(this.tcx().sess, ty.span, E0178,
+                                           "expected a path on the left-hand side of `+`, not `{}`",
+                                           pprust::ty_to_string(ty));
             let hi = bounds.iter().map(|x| match *x {
                 hir::TraitTyParamBound(ref tr, _) => tr.span.hi,
                 hir::RegionTyParamBound(ref r) => r.span.hi,
@@ -1059,29 +1061,28 @@ fn ast_ty_to_trait_ref<'tcx>(this: &AstConv<'tcx>,
             match (&ty.node, full_span) {
                 (&hir::TyRptr(None, ref mut_ty), Some(full_span)) => {
                     let mutbl_str = if mut_ty.mutbl == hir::MutMutable { "mut " } else { "" };
-                    this.tcx().sess
-                        .span_suggestion(full_span, "try adding parentheses (per RFC 438):",
-                                         format!("&{}({} +{})",
-                                                 mutbl_str,
-                                                 pprust::ty_to_string(&*mut_ty.ty),
-                                                 pprust::bounds_to_string(bounds)));
+                    err.span_suggestion(full_span, "try adding parentheses (per RFC 438):",
+                                        format!("&{}({} +{})",
+                                                mutbl_str,
+                                                pprust::ty_to_string(&*mut_ty.ty),
+                                                pprust::bounds_to_string(bounds)));
                 }
                 (&hir::TyRptr(Some(ref lt), ref mut_ty), Some(full_span)) => {
                     let mutbl_str = if mut_ty.mutbl == hir::MutMutable { "mut " } else { "" };
-                    this.tcx().sess
-                        .span_suggestion(full_span, "try adding parentheses (per RFC 438):",
-                                         format!("&{} {}({} +{})",
-                                                 pprust::lifetime_to_string(lt),
-                                                 mutbl_str,
-                                                 pprust::ty_to_string(&*mut_ty.ty),
-                                                 pprust::bounds_to_string(bounds)));
+                    err.span_suggestion(full_span, "try adding parentheses (per RFC 438):",
+                                        format!("&{} {}({} +{})",
+                                                pprust::lifetime_to_string(lt),
+                                                mutbl_str,
+                                                pprust::ty_to_string(&*mut_ty.ty),
+                                                pprust::bounds_to_string(bounds)));
                 }
 
                 _ => {
-                    fileline_help!(this.tcx().sess, ty.span,
+                    fileline_help!(&mut err, ty.span,
                                "perhaps you forgot parentheses? (per RFC 438)");
                 }
             }
+            err.emit();
             Err(ErrorReported)
         }
     }
@@ -1134,7 +1135,8 @@ fn make_object_type<'tcx>(this: &AstConv<'tcx>,
         traits::astconv_object_safety_violations(tcx, principal.def_id());
     if !object_safety_violations.is_empty() {
         traits::report_object_safety_error(
-            tcx, span, principal.def_id(), object_safety_violations);
+            tcx, span, principal.def_id(), object_safety_violations)
+            .emit();
         return tcx.types.err;
     }
 
@@ -1235,17 +1237,18 @@ fn one_bound_for_assoc_type<'tcx>(tcx: &ty::ctxt<'tcx>,
     }
 
     if bounds.len() > 1 {
-        span_err!(tcx.sess, span, E0221,
-                  "ambiguous associated type `{}` in bounds of `{}`",
-                  assoc_name,
-                  ty_param_name);
+        let mut err = struct_span_err!(tcx.sess, span, E0221,
+                                       "ambiguous associated type `{}` in bounds of `{}`",
+                                       assoc_name,
+                                       ty_param_name);
 
         for bound in &bounds {
-            span_note!(tcx.sess, span,
+            span_note!(&mut err, span,
                        "associated type `{}` could derive from `{}`",
                        ty_param_name,
                        bound);
         }
+        err.emit();
     }
 
     Ok(bounds[0].clone())
@@ -1707,12 +1710,13 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
                     }
                 }
                 Err(ref r) => {
-                    span_err!(tcx.sess, r.span, E0250,
-                              "array length constant evaluation error: {}",
-                              r.description());
+                    let mut err = struct_span_err!(tcx.sess, r.span, E0250,
+                                                   "array length constant evaluation error: {}",
+                                                   r.description());
                     if !ast_ty.span.contains(r.span) {
-                        span_note!(tcx.sess, ast_ty.span, "for array length here")
+                        span_note!(&mut err, ast_ty.span, "for array length here")
                     }
+                    err.emit();
                     this.tcx().types.err
                 }
             }
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index efcc08c69f8..588dee57c52 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -700,7 +700,9 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
                 if !is_special_case {
                     return
                 } else {
-                    span_note!(tcx.sess, pat.span,
+                    // Boo! Too painful to attach this to the actual warning,
+                    // it should go away at some point though.
+                    tcx.sess.span_note_without_error(pat.span,
                         "this warning will become a HARD ERROR in a future release. \
                         See RFC 218 for details.");
                 }
@@ -786,12 +788,13 @@ pub fn check_struct_pat_fields<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
     for &Spanned { node: ref field, span } in fields {
         let field_ty = match used_fields.entry(field.name) {
             Occupied(occupied) => {
-                span_err!(tcx.sess, span, E0025,
-                    "field `{}` bound multiple times in the pattern",
-                    field.name);
-                span_note!(tcx.sess, *occupied.get(),
-                    "field `{}` previously bound here",
-                    field.name);
+                let mut err = struct_span_err!(tcx.sess, span, E0025,
+                                               "field `{}` bound multiple times in the pattern",
+                                               field.name);
+                span_note!(&mut err, *occupied.get(),
+                           "field `{}` previously bound here",
+                           field.name);
+                err.emit();
                 tcx.types.err
             }
             Vacant(vacant) => {
diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs
index 1e20cd39854..a1b378d84d0 100644
--- a/src/librustc_typeck/check/callee.rs
+++ b/src/librustc_typeck/check/callee.rs
@@ -61,11 +61,12 @@ pub fn check_legal_trait_for_method_call(ccx: &CrateCtxt, span: Span, trait_id:
             return // not a closure method, everything is OK.
         };
 
-        span_err!(tcx.sess, span, E0174,
-                  "explicit use of unboxed closure method `{}` is experimental",
-                  method);
-        fileline_help!(tcx.sess, span,
-                   "add `#![feature(unboxed_closures)]` to the crate attributes to enable");
+        struct_span_err!(tcx.sess, span, E0174,
+                         "explicit use of unboxed closure method `{}` is experimental",
+                         method)
+            .fileline_help(span, "add `#![feature(unboxed_closures)]` to the crate \
+                                  attributes to enable")
+            .emit();
     }
 }
 
@@ -228,7 +229,7 @@ fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
             sig
         }
         _ => {
-            fcx.type_error_message(call_expr.span, |actual| {
+            let mut err = fcx.type_error_struct(call_expr.span, |actual| {
                 format!("expected function, found `{}`", actual)
             }, callee_ty, None);
 
@@ -237,12 +238,14 @@ fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                 if let Some(pr) = tcx.def_map.borrow().get(&expr.id) {
                     if pr.depth == 0 && pr.base_def != def::DefErr {
                         if let Some(span) = tcx.map.span_if_local(pr.def_id()) {
-                            tcx.sess.span_note(span, "defined here")
+                            err.span_note(span, "defined here");
                         }
                     }
                 }
             }
 
+            err.emit();
+
             // This is the "default" function signature, used in case of error.
             // In that case, we check each argument against "error" in order to
             // set up all the node type bindings.
diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs
index 13e5e46ed27..90e67944ef9 100644
--- a/src/librustc_typeck/check/cast.rs
+++ b/src/librustc_typeck/check/cast.rs
@@ -127,23 +127,25 @@ impl<'tcx> CastCheck<'tcx> {
             CastError::NeedViaThinPtr |
             CastError::NeedViaInt |
             CastError::NeedViaUsize => {
-                fcx.type_error_message(self.span, |actual| {
+                fcx.type_error_struct(self.span, |actual| {
                     format!("casting `{}` as `{}` is invalid",
                             actual,
                             fcx.infcx().ty_to_string(self.cast_ty))
-                }, self.expr_ty, None);
-                fcx.ccx.tcx.sess.fileline_help(self.span,
-                    &format!("cast through {} first", match e {
-                        CastError::NeedViaPtr => "a raw pointer",
-                        CastError::NeedViaThinPtr => "a thin pointer",
-                        CastError::NeedViaInt => "an integer",
-                        CastError::NeedViaUsize => "a usize",
-                        _ => unreachable!()
-                }));
+                }, self.expr_ty, None)
+                    .fileline_help(self.span,
+                        &format!("cast through {} first", match e {
+                            CastError::NeedViaPtr => "a raw pointer",
+                            CastError::NeedViaThinPtr => "a thin pointer",
+                            CastError::NeedViaInt => "an integer",
+                            CastError::NeedViaUsize => "a usize",
+                            _ => unreachable!()
+                        }))
+                    .emit();
             }
             CastError::CastToBool => {
-                span_err!(fcx.tcx().sess, self.span, E0054, "cannot cast as `bool`");
-                fcx.ccx.tcx.sess.fileline_help(self.span, "compare with zero instead");
+                struct_span_err!(fcx.tcx().sess, self.span, E0054, "cannot cast as `bool`")
+                    .fileline_help(self.span, "compare with zero instead")
+                    .emit();
             }
             CastError::CastToChar => {
                 fcx.type_error_message(self.span, |actual| {
@@ -165,12 +167,13 @@ impl<'tcx> CastCheck<'tcx> {
                 }, self.expr_ty, None);
             }
             CastError::DifferingKinds => {
-                fcx.type_error_message(self.span, |actual| {
+                fcx.type_error_struct(self.span, |actual| {
                     format!("casting `{}` as `{}` is invalid",
                             actual,
                             fcx.infcx().ty_to_string(self.cast_ty))
-                }, self.expr_ty, None);
-                fcx.ccx.tcx.sess.fileline_note(self.span, "vtable kinds may not match");
+                }, self.expr_ty, None)
+                    .fileline_note(self.span, "vtable kinds may not match")
+                    .emit();
             }
         }
     }
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index 59025346ce3..0cf552b6efe 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -95,12 +95,13 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>(
 
     if let Err(_) = infer::mk_eqty(&infcx, true, infer::TypeOrigin::Misc(drop_impl_span),
                                    named_type, fresh_impl_self_ty) {
-        span_err!(tcx.sess, drop_impl_span, E0366,
-                  "Implementations of Drop cannot be specialized");
         let item_span = tcx.map.span(self_type_node_id);
-        tcx.sess.span_note(item_span,
-                           "Use same sequence of generic type and region \
-                            parameters that is on the struct/enum definition");
+        struct_span_err!(tcx.sess, drop_impl_span, E0366,
+                         "Implementations of Drop cannot be specialized")
+            .span_note(item_span,
+                       "Use same sequence of generic type and region \
+                        parameters that is on the struct/enum definition")
+            .emit();
         return Err(());
     }
 
@@ -197,11 +198,12 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
 
         if !assumptions_in_impl_context.contains(&predicate) {
             let item_span = tcx.map.span(self_type_node_id);
-            span_err!(tcx.sess, drop_impl_span, E0367,
-                      "The requirement `{}` is added only by the Drop impl.", predicate);
-            tcx.sess.span_note(item_span,
-                               "The same requirement must be part of \
-                                the struct/enum definition");
+            struct_span_err!(tcx.sess, drop_impl_span, E0367,
+                             "The requirement `{}` is added only by the Drop impl.", predicate)
+                .span_note(item_span,
+                           "The same requirement must be part of \
+                            the struct/enum definition")
+                .emit();
         }
     }
 
@@ -289,8 +291,8 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>
         Ok(()) => {}
         Err(Error::Overflow(ref ctxt, ref detected_on_typ)) => {
             let tcx = rcx.tcx();
-            span_err!(tcx.sess, span, E0320,
-                      "overflow while adding drop-check rules for {}", typ);
+            let mut err = struct_span_err!(tcx.sess, span, E0320,
+                                           "overflow while adding drop-check rules for {}", typ);
             match *ctxt {
                 TypeContext::Root => {
                     // no need for an additional note if the overflow
@@ -311,7 +313,7 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>
                         format!("`{}`", field)
                     };
                     span_note!(
-                        rcx.tcx().sess,
+                        &mut err,
                         span,
                         "overflowed on {} field {} type: {}",
                         variant_name,
@@ -319,6 +321,7 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>
                         detected_on_typ);
                 }
             }
+            err.emit();
         }
     }
 }
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 955bc92a8f3..65b0d589275 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -27,6 +27,7 @@ use util::nodemap::{FnvHashSet};
 
 use syntax::ast;
 use syntax::codemap::Span;
+use syntax::errors::DiagnosticBuilder;
 use rustc_front::print::pprust;
 use rustc_front::hir;
 
@@ -55,7 +56,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                            mode }) => {
             let cx = fcx.tcx();
 
-            fcx.type_error_message(
+            let mut err = fcx.type_error_struct(
                 span,
                 |actual| {
                     format!("no {} named `{}` found for type `{}` \
@@ -78,17 +79,21 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                         // snippet
                     };
 
-                    let span_stored_function = || {
-                        cx.sess.span_note(span,
+                    macro_rules! span_stored_function {
+                        () => {
+                            err.span_note(span,
                                           &format!("use `({0}.{1})(...)` if you meant to call \
                                                     the function stored in the `{1}` field",
                                                    expr_string, item_name));
-                    };
+                        }
+                    }
 
-                    let span_did_you_mean = || {
-                        cx.sess.span_note(span, &format!("did you mean to write `{0}.{1}`?",
+                    macro_rules! span_did_you_mean {
+                        () => {
+                            err.span_note(span, &format!("did you mean to write `{0}.{1}`?",
                                                          expr_string, item_name));
-                    };
+                        }
+                    }
 
                     // Determine if the field can be used as a function in some way
                     let field_ty = field.ty(cx, substs);
@@ -96,19 +101,22 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                     match field_ty.sty {
                         // Not all of these (e.g. unsafe fns) implement FnOnce
                         // so we look for these beforehand
-                        ty::TyClosure(..) | ty::TyBareFn(..) => span_stored_function(),
+                        ty::TyClosure(..) | ty::TyBareFn(..) => {
+                            span_stored_function!();
+                        }
                         // If it's not a simple function, look for things which implement FnOnce
                         _ => {
                             if let Ok(fn_once_trait_did) =
                                     cx.lang_items.require(FnOnceTraitLangItem) {
                                 let infcx = fcx.infcx();
                                 infcx.probe(|_| {
-                                    let fn_once_substs = Substs::new_trait(vec![
-                                                                            infcx.next_ty_var()],
-                                                                           Vec::new(),
-                                                                           field_ty);
-                                    let trait_ref = ty::TraitRef::new(fn_once_trait_did,
-                                                                      cx.mk_substs(fn_once_substs));
+                                    let fn_once_substs =
+                                        Substs::new_trait(vec![infcx.next_ty_var()],
+                                                          Vec::new(),
+                                                          field_ty);
+                                    let trait_ref =
+                                      ty::TraitRef::new(fn_once_trait_did,
+                                                        cx.mk_substs(fn_once_substs));
                                     let poly_trait_ref = trait_ref.to_poly_trait_ref();
                                     let obligation = Obligation::misc(span,
                                                                       fcx.body_id,
@@ -117,13 +125,13 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                     let mut selcx = SelectionContext::new(infcx);
 
                                     if selcx.evaluate_obligation(&obligation) {
-                                        span_stored_function();
+                                        span_stored_function!();
                                     } else {
-                                        span_did_you_mean();
+                                        span_did_you_mean!();
                                     }
                                 });
                             } else {
-                                span_did_you_mean()
+                                span_did_you_mean!();
                             }
                         }
                     }
@@ -131,11 +139,11 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
             }
 
             if !static_sources.is_empty() {
-                cx.sess.fileline_note(
+                err.fileline_note(
                     span,
                     "found defined static methods, maybe a `self` is missing?");
 
-                report_candidates(fcx, span, item_name, static_sources);
+                report_candidates(fcx, &mut err, span, item_name, static_sources);
             }
 
             if !unsatisfied_predicates.is_empty() {
@@ -145,7 +153,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                      p))
                     .collect::<Vec<_>>()
                     .join(", ");
-                cx.sess.fileline_note(
+                err.fileline_note(
                     span,
                     &format!("the method `{}` exists but the \
                              following trait bounds were not satisfied: {}",
@@ -153,15 +161,17 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                              bound_list));
             }
 
-            suggest_traits_to_import(fcx, span, rcvr_ty, item_name,
-                                     rcvr_expr, out_of_scope_traits)
+            suggest_traits_to_import(fcx, &mut err, span, rcvr_ty, item_name,
+                                     rcvr_expr, out_of_scope_traits);
+            err.emit();
         }
 
         MethodError::Ambiguity(sources) => {
-            span_err!(fcx.sess(), span, E0034,
-                      "multiple applicable items in scope");
+            let mut err = struct_span_err!(fcx.sess(), span, E0034,
+                                           "multiple applicable items in scope");
 
-            report_candidates(fcx, span, item_name, sources);
+            report_candidates(fcx, &mut err, span, item_name, sources);
+            err.emit();
         }
 
         MethodError::ClosureAmbiguity(trait_def_id) => {
@@ -181,6 +191,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     }
 
     fn report_candidates(fcx: &FnCtxt,
+                         err: &mut DiagnosticBuilder,
                          span: Span,
                          item_name: ast::Name,
                          mut sources: Vec<CandidateSource>) {
@@ -213,7 +224,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                         }
                     };
 
-                    span_note!(fcx.sess(), item_span,
+                    span_note!(err, item_span,
                                "candidate #{} is defined in an impl{} for the type `{}`",
                                idx + 1,
                                insertion,
@@ -222,7 +233,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                 CandidateSource::TraitSource(trait_did) => {
                     let item = trait_item(fcx.tcx(), trait_did, item_name).unwrap();
                     let item_span = fcx.tcx().map.def_id_span(item.def_id(), span);
-                    span_note!(fcx.sess(), item_span,
+                    span_note!(err, item_span,
                                "candidate #{} is defined in the trait `{}`",
                                idx + 1,
                                fcx.tcx().item_path_str(trait_did));
@@ -236,6 +247,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 pub type AllTraitsVec = Vec<TraitInfo>;
 
 fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                                      err: &mut DiagnosticBuilder,
                                       span: Span,
                                       rcvr_ty: Ty<'tcx>,
                                       item_name: ast::Name,
@@ -255,14 +267,13 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
             traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"},
             one_of_them = if candidates.len() == 1 {"it"} else {"one of them"});
 
-        fcx.sess().fileline_help(span, &msg[..]);
+        err.fileline_help(span, &msg[..]);
 
         for (i, trait_did) in candidates.iter().enumerate() {
-            fcx.sess().fileline_help(span,
-                                     &*format!("candidate #{}: use `{}`",
-                                               i + 1,
-                                               fcx.tcx().item_path_str(*trait_did)))
-
+            err.fileline_help(span,
+                              &*format!("candidate #{}: use `{}`",
+                                        i + 1,
+                                        fcx.tcx().item_path_str(*trait_did)));
         }
         return
     }
@@ -301,13 +312,13 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
             one_of_them = if candidates.len() == 1 {"it"} else {"one of them"},
             name = item_name);
 
-        fcx.sess().fileline_help(span, &msg[..]);
+        err.fileline_help(span, &msg[..]);
 
         for (i, trait_info) in candidates.iter().enumerate() {
-            fcx.sess().fileline_help(span,
-                                     &*format!("candidate #{}: `{}`",
-                                               i + 1,
-                                               fcx.tcx().item_path_str(trait_info.def_id)))
+            err.fileline_help(span,
+                              &*format!("candidate #{}: `{}`",
+                                        i + 1,
+                                        fcx.tcx().item_path_str(trait_info.def_id)));
         }
     }
 }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 72d0e5b5332..ca2db8c3def 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -119,6 +119,7 @@ use syntax::ast;
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
 use syntax::codemap::{self, Span, Spanned};
+use syntax::errors::DiagnosticBuilder;
 use syntax::parse::token::{self, InternedString};
 use syntax::ptr::P;
 use syntax::util::lev_distance::find_best_match_for_name;
@@ -702,11 +703,12 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) {
             for item in &m.items {
                 let pty = ccx.tcx.lookup_item_type(ccx.tcx.map.local_def_id(item.id));
                 if !pty.generics.types.is_empty() {
-                    span_err!(ccx.tcx.sess, item.span, E0044,
+                    let mut err = struct_span_err!(ccx.tcx.sess, item.span, E0044,
                         "foreign items may not have type parameters");
-                    span_help!(ccx.tcx.sess, item.span,
+                    span_help!(&mut err, item.span,
                         "consider using specialization instead of \
                         type parameters");
+                    err.emit();
                 }
 
                 if let hir::ForeignItemFn(ref fn_decl, _) = item.node {
@@ -1037,7 +1039,7 @@ fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                          t_expr: Ty<'tcx>,
                                          id: ast::NodeId) {
     let tstr = fcx.infcx().ty_to_string(t_cast);
-    fcx.type_error_message(span, |actual| {
+    let mut err = fcx.type_error_struct(span, |actual| {
         format!("cast to unsized type: `{}` as `{}`", actual, tstr)
     }, t_expr, None);
     match t_expr.sty {
@@ -1049,16 +1051,16 @@ fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
             if t_cast.is_trait() {
                 match fcx.tcx().sess.codemap().span_to_snippet(t_span) {
                     Ok(s) => {
-                        fcx.tcx().sess.span_suggestion(t_span,
-                                                       "try casting to a reference instead:",
-                                                       format!("&{}{}", mtstr, s));
+                        err.span_suggestion(t_span,
+                                            "try casting to a reference instead:",
+                                            format!("&{}{}", mtstr, s));
                     },
                     Err(_) =>
-                        span_help!(fcx.tcx().sess, t_span,
+                        span_help!(err, t_span,
                                    "did you mean `&{}{}`?", mtstr, tstr),
                 }
             } else {
-                span_help!(fcx.tcx().sess, span,
+                span_help!(err, span,
                            "consider using an implicit coercion to `&{}{}` instead",
                            mtstr, tstr);
             }
@@ -1066,19 +1068,20 @@ fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         ty::TyBox(..) => {
             match fcx.tcx().sess.codemap().span_to_snippet(t_span) {
                 Ok(s) => {
-                    fcx.tcx().sess.span_suggestion(t_span,
-                                                   "try casting to a `Box` instead:",
-                                                   format!("Box<{}>", s));
+                    err.span_suggestion(t_span,
+                                                          "try casting to a `Box` instead:",
+                                                           format!("Box<{}>", s));
                 },
                 Err(_) =>
-                    span_help!(fcx.tcx().sess, t_span, "did you mean `Box<{}>`?", tstr),
+                    span_help!(err, t_span, "did you mean `Box<{}>`?", tstr),
             }
         }
         _ => {
-            span_help!(fcx.tcx().sess, e_span,
+            span_help!(err, e_span,
                        "consider using a box or reference as appropriate");
         }
     }
+    err.emit();
     fcx.write_error(id);
 }
 
@@ -1443,10 +1446,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Some((adt, variant))
         } else if var_kind == ty::VariantKind::Unit {
             if !self.tcx().sess.features.borrow().braced_empty_structs {
-                self.tcx().sess.span_err(span, "empty structs and enum variants \
-                                                with braces are unstable");
-                fileline_help!(self.tcx().sess, span, "add #![feature(braced_empty_structs)] to \
-                                                       the crate features to enable");
+                let mut err = self.tcx().sess.struct_span_err(span,
+                                                              "empty structs and enum variants \
+                                                               with braces are unstable");
+                fileline_help!(&mut err, span, "add #![feature(braced_empty_structs)] to \
+                                                the crate features to enable");
+                err.emit();
             }
 
              Some((adt, variant))
@@ -1614,12 +1619,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                  sp: Span,
                                  mk_msg: M,
                                  actual_ty: Ty<'tcx>,
-                                 err: Option<&TypeError<'tcx>>) where
-        M: FnOnce(String) -> String,
+                                 err: Option<&TypeError<'tcx>>)
+        where M: FnOnce(String) -> String,
     {
         self.infcx().type_error_message(sp, mk_msg, actual_ty, err);
     }
 
+    pub fn type_error_struct<M>(&self,
+                                sp: Span,
+                                mk_msg: M,
+                                actual_ty: Ty<'tcx>,
+                                err: Option<&TypeError<'tcx>>)
+                                -> DiagnosticBuilder<'tcx>
+        where M: FnOnce(String) -> String,
+    {
+        self.infcx().type_error_struct(sp, mk_msg, actual_ty, err)
+    }
+
     pub fn report_mismatched_types(&self,
                                    sp: Span,
                                    e: Ty<'tcx>,
@@ -2913,7 +2929,6 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                             lvalue_pref: LvaluePreference,
                             base: &'tcx hir::Expr,
                             field: &Spanned<ast::Name>) {
-        let tcx = fcx.ccx.tcx;
         check_expr_with_lvalue_pref(fcx, base, lvalue_pref);
         let expr_t = structurally_resolved_type(fcx, expr.span,
                                                 fcx.expr_ty(base));
@@ -2945,19 +2960,18 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         }
 
         if method::exists(fcx, field.span, field.node, expr_t, expr.id) {
-            fcx.type_error_message(
-                field.span,
-                |actual| {
-                    format!("attempted to take value of method `{}` on type \
-                            `{}`", field.node, actual)
-                },
-                expr_t, None);
-
-            tcx.sess.fileline_help(field.span,
+            fcx.type_error_struct(field.span,
+                                  |actual| {
+                                       format!("attempted to take value of method `{}` on type \
+                                               `{}`", field.node, actual)
+                                   },
+                                   expr_t, None)
+                .fileline_help(field.span,
                                "maybe a `()` to call it is missing? \
-                               If not, try an anonymous function");
+                               If not, try an anonymous function")
+                .emit();
         } else {
-            fcx.type_error_message(
+            let mut err = fcx.type_error_struct(
                 expr.span,
                 |actual| {
                     format!("attempted access of field `{}` on \
@@ -2968,17 +2982,18 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                 },
                 expr_t, None);
             if let ty::TyStruct(def, _) = expr_t.sty {
-                suggest_field_names(def.struct_variant(), field, tcx, vec![]);
+                suggest_field_names(&mut err, def.struct_variant(), field, vec![]);
             }
+            err.emit();
         }
 
         fcx.write_error(expr.id);
     }
 
     // displays hints about the closest matches in field names
-    fn suggest_field_names<'tcx>(variant: ty::VariantDef<'tcx>,
+    fn suggest_field_names<'tcx>(err: &mut DiagnosticBuilder,
+                                 variant: ty::VariantDef<'tcx>,
                                  field: &Spanned<ast::Name>,
-                                 tcx: &ty::ctxt<'tcx>,
                                  skip : Vec<InternedString>) {
         let name = field.node.as_str();
         let names = variant.fields
@@ -2995,8 +3010,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
 
         // only find fits with at least one matching letter
         if let Some(name) = find_best_match_for_name(names, &name, Some(name.len())) {
-            tcx.sess.span_help(field.span,
-                &format!("did you mean `{}`?", name));
+            err.span_help(field.span,
+                          &format!("did you mean `{}`?", name));
         }
     }
 
@@ -3071,7 +3086,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                       variant: ty::VariantDef<'tcx>,
                                       field: &hir::Field,
                                       skip_fields: &[hir::Field]) {
-        fcx.type_error_message(
+        let mut err = fcx.type_error_struct(
             field.name.span,
             |actual| if let ty::TyEnum(..) = ty.sty {
                 format!("struct variant `{}::{}` has no field named `{}`",
@@ -3084,7 +3099,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
             None);
         // prevent all specified fields from being suggested
         let skip_fields = skip_fields.iter().map(|ref x| x.name.node.as_str());
-        suggest_field_names(variant, &field.name, fcx.tcx(), skip_fields.collect());
+        suggest_field_names(&mut err, variant, &field.name, skip_fields.collect());
+        err.emit();
     }
 
     fn check_expr_struct_fields<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
@@ -4146,9 +4162,9 @@ pub fn check_representable(tcx: &ty::ctxt,
     // caught by case 1.
     match rty.is_representable(tcx, sp) {
         Representability::SelfRecursive => {
-            span_err!(tcx.sess, sp, E0072, "invalid recursive {} type", designation);
-            tcx.sess.fileline_help(
-                sp, "wrap the inner value in a box to make it representable");
+            struct_span_err!(tcx.sess, sp, E0072, "invalid recursive {} type", designation)
+                .fileline_help(sp, "wrap the inner value in a box to make it representable")
+                .emit();
             return false
         }
         Representability::Representable | Representability::ContainsRecursive => (),
@@ -4245,11 +4261,12 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
             // Check for duplicate discriminant values
             match disr_vals.iter().position(|&x| x == current_disr_val) {
                 Some(i) => {
-                    span_err!(ccx.tcx.sess, v.span, E0081,
+                    let mut err = struct_span_err!(ccx.tcx.sess, v.span, E0081,
                         "discriminant value `{}` already exists", disr_vals[i]);
                     let variant_i_node_id = ccx.tcx.map.as_local_node_id(variants[i].did).unwrap();
-                    span_note!(ccx.tcx.sess, ccx.tcx.map.span(variant_i_node_id),
-                        "conflicting discriminant here")
+                    span_note!(&mut err, ccx.tcx.map.span(variant_i_node_id),
+                        "conflicting discriminant here");
+                    err.emit();
                 }
                 None => {}
             }
@@ -4258,10 +4275,11 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
                 attr::ReprAny | attr::ReprExtern => (),
                 attr::ReprInt(sp, ity) => {
                     if !disr_in_range(ccx, ity, current_disr_val) {
-                        span_err!(ccx.tcx.sess, v.span, E0082,
+                        let mut err = struct_span_err!(ccx.tcx.sess, v.span, E0082,
                             "discriminant value outside specified type");
-                        span_note!(ccx.tcx.sess, sp,
+                        span_note!(&mut err, sp,
                             "discriminant type specified here");
+                        err.emit();
                     }
                 }
                 attr::ReprSimd => {
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index 0c65f68f02e..c5a36fb4ada 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -187,10 +187,10 @@ fn check_overloaded_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                               hir_util::binop_to_string(op.node),
                               lhs_ty);
                 } else {
-                    span_err!(fcx.tcx().sess, lhs_expr.span, E0369,
-                              "binary operation `{}` cannot be applied to type `{}`",
-                              hir_util::binop_to_string(op.node),
-                              lhs_ty);
+                    let mut err = struct_span_err!(fcx.tcx().sess, lhs_expr.span, E0369,
+                        "binary operation `{}` cannot be applied to type `{}`",
+                        hir_util::binop_to_string(op.node),
+                        lhs_ty);
                     let missing_trait = match op.node {
                         hir::BiAdd    => Some("std::ops::Add"),
                         hir::BiSub    => Some("std::ops::Sub"),
@@ -208,10 +208,11 @@ fn check_overloaded_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                     };
 
                     if let Some(missing_trait) = missing_trait {
-                        span_note!(fcx.tcx().sess, lhs_expr.span,
+                        span_note!(&mut err, lhs_expr.span,
                                    "an implementation of `{}` might be missing for `{}`",
                                     missing_trait, lhs_ty);
                     }
+                    err.emit();
                 }
             }
             fcx.tcx().types.err
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index 230422b7044..d43efb17b2e 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -23,6 +23,7 @@ use std::cell::RefCell;
 use std::collections::HashSet;
 use syntax::ast;
 use syntax::codemap::{Span};
+use syntax::errors::DiagnosticBuilder;
 use syntax::parse::token::{special_idents};
 use rustc_front::intravisit::{self, Visitor};
 use rustc_front::hir;
@@ -496,12 +497,12 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
                          span: Span,
                          param_name: ast::Name)
     {
-        error_392(self.tcx(), span, param_name);
+        let mut err = error_392(self.tcx(), span, param_name);
 
         let suggested_marker_id = self.tcx().lang_items.phantom_data();
         match suggested_marker_id {
             Some(def_id) => {
-                self.tcx().sess.fileline_help(
+                err.fileline_help(
                     span,
                     &format!("consider removing `{}` or using a marker such as `{}`",
                              param_name,
@@ -511,6 +512,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
                 // no lang items, no help!
             }
         }
+        err.emit();
     }
 }
 
@@ -621,9 +623,10 @@ pub fn error_380<'ccx,'tcx>(ccx: &'ccx CrateCtxt<'ccx, 'tcx>, span: Span) {
                Trait for ..`) must have no methods or associated items")
 }
 
-pub fn error_392<'tcx>(tcx: &ty::ctxt<'tcx>, span: Span, param_name: ast::Name)  {
-    span_err!(tcx.sess, span, E0392,
-              "parameter `{}` is never used", param_name);
+pub fn error_392<'tcx>(tcx: &ty::ctxt<'tcx>, span: Span, param_name: ast::Name)
+                       -> DiagnosticBuilder<'tcx> {
+    struct_span_err!(tcx.sess, span, E0392,
+                     "parameter `{}` is never used", param_name)
 }
 
 pub fn error_194<'tcx>(tcx: &ty::ctxt<'tcx>, span: Span, name: ast::Name) {
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index c24a416a010..1536f13a1d5 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -129,13 +129,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
                             !self.fcx.expr_ty(lhs).references_error() &&
                             !self.fcx.expr_ty(rhs).references_error()
                         {
-                            tcx.sess.span_err(
-                                e.span,
-                                "overloaded augmented assignments are not stable");
-                            fileline_help!(
-                                tcx.sess, e.span,
-                                "add #![feature(augmented_assignments)] to the crate root \
-                                 to enable");
+                            tcx.sess.struct_span_err(e.span,
+                                                     "overloaded augmented assignments \
+                                                      are not stable")
+                                .fileline_help(e.span,
+                                               "add #![feature(augmented_assignments)] to the \
+                                                crate root to enable")
+                                .emit()
                         }
                     }
                 }
diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs
index 37bbfb4e967..02be74a5906 100644
--- a/src/librustc_typeck/coherence/mod.rs
+++ b/src/librustc_typeck/coherence/mod.rs
@@ -502,9 +502,14 @@ fn enforce_trait_manually_implementable(tcx: &ty::ctxt, sp: Span, trait_def_id:
     } else {
         return // everything OK
     };
-    span_err!(tcx.sess, sp, E0183, "manual implementations of `{}` are experimental", trait_name);
-    fileline_help!(tcx.sess, sp,
-               "add `#![feature(unboxed_closures)]` to the crate attributes to enable");
+    let mut err = struct_span_err!(tcx.sess,
+                                   sp,
+                                   E0183,
+                                   "manual implementations of `{}` are experimental",
+                                   trait_name);
+    fileline_help!(&mut err, sp,
+                   "add `#![feature(unboxed_closures)]` to the crate attributes to enable");
+    err.emit();
 }
 
 pub fn check_coherence(crate_context: &CrateCtxt) {
diff --git a/src/librustc_typeck/coherence/orphan.rs b/src/librustc_typeck/coherence/orphan.rs
index 411e1a4e480..76be04bb174 100644
--- a/src/librustc_typeck/coherence/orphan.rs
+++ b/src/librustc_typeck/coherence/orphan.rs
@@ -48,11 +48,11 @@ impl<'cx, 'tcx> OrphanChecker<'cx, 'tcx> {
         match lang_def_id {
             Some(lang_def_id) if lang_def_id == impl_def_id => { /* OK */ },
             _ => {
-                span_err!(self.tcx.sess, span, E0390,
+                struct_span_err!(self.tcx.sess, span, E0390,
                           "only a single inherent implementation marked with `#[lang = \"{}\"]` \
-                           is allowed for the `{}` primitive", lang, ty);
-                span_help!(self.tcx.sess, span,
-                           "consider using a trait to implement these methods");
+                           is allowed for the `{}` primitive", lang, ty)
+                    .span_help(span, "consider using a trait to implement these methods")
+                    .emit();
             }
         }
     }
diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs
index 7d1f71967a2..beb409f7f0f 100644
--- a/src/librustc_typeck/coherence/overlap.rs
+++ b/src/librustc_typeck/coherence/overlap.rs
@@ -150,18 +150,19 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> {
             }).unwrap_or(String::new())
         };
 
-        span_err!(self.tcx.sess, self.span_of_impl(impl1), E0119,
-                  "conflicting implementations of trait `{}`{}:",
-                  trait_ref,
-                  self_type);
+        let mut err = struct_span_err!(self.tcx.sess, self.span_of_impl(impl1), E0119,
+                                       "conflicting implementations of trait `{}`{}:",
+                                       trait_ref,
+                                       self_type);
 
         if impl2.is_local() {
-            span_note!(self.tcx.sess, self.span_of_impl(impl2),
+            span_note!(&mut err, self.span_of_impl(impl2),
                        "conflicting implementation is here:");
         } else {
             let cname = self.tcx.sess.cstore.crate_name(impl2.krate);
-            self.tcx.sess.note(&format!("conflicting implementation in crate `{}`", cname));
+            err.note(&format!("conflicting implementation in crate `{}`", cname));
         }
+        err.emit();
     }
 
     fn span_of_impl(&self, impl_did: DefId) -> Span {
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index b7ce91d3317..eaaa2c77379 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -225,24 +225,24 @@ impl<'a,'tcx> CrateCtxt<'a,'tcx> {
         assert!(!cycle.is_empty());
         let tcx = self.tcx;
 
-        span_err!(tcx.sess, span, E0391,
+        let mut err = struct_span_err!(tcx.sess, span, E0391,
             "unsupported cyclic reference between types/traits detected");
 
         match cycle[0] {
             AstConvRequest::GetItemTypeScheme(def_id) |
             AstConvRequest::GetTraitDef(def_id) => {
-                tcx.sess.note(
+                err.note(
                     &format!("the cycle begins when processing `{}`...",
                              tcx.item_path_str(def_id)));
             }
             AstConvRequest::EnsureSuperPredicates(def_id) => {
-                tcx.sess.note(
+                err.note(
                     &format!("the cycle begins when computing the supertraits of `{}`...",
                              tcx.item_path_str(def_id)));
             }
             AstConvRequest::GetTypeParameterBounds(id) => {
                 let def = tcx.type_parameter_def(id);
-                tcx.sess.note(
+                err.note(
                     &format!("the cycle begins when computing the bounds \
                               for type parameter `{}`...",
                              def.name));
@@ -253,18 +253,18 @@ impl<'a,'tcx> CrateCtxt<'a,'tcx> {
             match *request {
                 AstConvRequest::GetItemTypeScheme(def_id) |
                 AstConvRequest::GetTraitDef(def_id) => {
-                    tcx.sess.note(
+                    err.note(
                         &format!("...which then requires processing `{}`...",
                                  tcx.item_path_str(def_id)));
                 }
                 AstConvRequest::EnsureSuperPredicates(def_id) => {
-                    tcx.sess.note(
+                    err.note(
                         &format!("...which then requires computing the supertraits of `{}`...",
                                  tcx.item_path_str(def_id)));
                 }
                 AstConvRequest::GetTypeParameterBounds(id) => {
                     let def = tcx.type_parameter_def(id);
-                    tcx.sess.note(
+                    err.note(
                         &format!("...which then requires computing the bounds \
                                   for type parameter `{}`...",
                                  def.name));
@@ -275,24 +275,25 @@ impl<'a,'tcx> CrateCtxt<'a,'tcx> {
         match cycle[0] {
             AstConvRequest::GetItemTypeScheme(def_id) |
             AstConvRequest::GetTraitDef(def_id) => {
-                tcx.sess.note(
+                err.note(
                     &format!("...which then again requires processing `{}`, completing the cycle.",
                              tcx.item_path_str(def_id)));
             }
             AstConvRequest::EnsureSuperPredicates(def_id) => {
-                tcx.sess.note(
+                err.note(
                     &format!("...which then again requires computing the supertraits of `{}`, \
                               completing the cycle.",
                              tcx.item_path_str(def_id)));
             }
             AstConvRequest::GetTypeParameterBounds(id) => {
                 let def = tcx.type_parameter_def(id);
-                tcx.sess.note(
+                err.note(
                     &format!("...which then again requires computing the bounds \
                               for type parameter `{}`, completing the cycle.",
                              def.name));
             }
         }
+        err.emit();
     }
 
     /// Loads the trait def for a given trait, returning ErrorReported if a cycle arises.
@@ -1012,10 +1013,11 @@ fn convert_struct_variant<'tcx>(tcx: &ty::ctxt<'tcx>,
             hir::NamedField(name, vis) => {
                 let dup_span = seen_fields.get(&name).cloned();
                 if let Some(prev_span) = dup_span {
-                    span_err!(tcx.sess, f.span, E0124,
-                              "field `{}` is already declared",
-                              name);
-                    span_note!(tcx.sess, prev_span, "previously declared here");
+                    let mut err = struct_span_err!(tcx.sess, f.span, E0124,
+                                                   "field `{}` is already declared",
+                                                   name);
+                    span_note!(&mut err, prev_span, "previously declared here");
+                    err.emit();
                 } else {
                     seen_fields.insert(name, f.span);
                 }
@@ -1080,12 +1082,13 @@ fn convert_enum_def<'tcx>(tcx: &ty::ctxt<'tcx>,
                 None
             },
             Err(err) => {
-                span_err!(tcx.sess, err.span, E0080,
-                          "constant evaluation error: {}",
-                          err.description());
+                let mut diag = struct_span_err!(tcx.sess, err.span, E0080,
+                                                "constant evaluation error: {}",
+                                                err.description());
                 if !e.span.contains(err.span) {
-                    tcx.sess.span_note(e.span, "for enum discriminant here");
+                    diag.span_note(e.span, "for enum discriminant here");
                 }
+                diag.emit();
                 None
             }
         }
@@ -1254,13 +1257,14 @@ fn trait_def_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
 
     let paren_sugar = tcx.has_attr(def_id, "rustc_paren_sugar");
     if paren_sugar && !ccx.tcx.sess.features.borrow().unboxed_closures {
-        ccx.tcx.sess.span_err(
+        let mut err = ccx.tcx.sess.struct_span_err(
             it.span,
             "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling \
              which traits can use parenthetical notation");
-        fileline_help!(ccx.tcx.sess, it.span,
+        fileline_help!(&mut err, it.span,
                    "add `#![feature(unboxed_closures)]` to \
                     the crate attributes to use it");
+        err.emit();
     }
 
     let substs = ccx.tcx.mk_substs(mk_trait_substs(ccx, generics));
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index 70c6d6f05c6..bf890f3e507 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -203,8 +203,9 @@ fn require_same_types<'a, 'tcx, M>(tcx: &ty::ctxt<'tcx>,
     match result {
         Ok(_) => true,
         Err(ref terr) => {
-            span_err!(tcx.sess, span, E0211, "{}: {}", msg(), terr);
-            tcx.note_and_explain_type_err(terr, span);
+            let mut err = struct_span_err!(tcx.sess, span, E0211, "{}: {}", msg(), terr);
+            tcx.note_and_explain_type_err(&mut err, terr, span);
+            err.emit();
             false
         }
     }
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index 26662605ba8..96d0052cf18 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -305,8 +305,10 @@ pub fn find_export_name_attr(diag: &Handler, attrs: &[Attribute]) -> Option<Inte
             if let s@Some(_) = attr.value_str() {
                 s
             } else {
-                diag.span_err(attr.span, "export_name attribute has invalid format");
-                diag.help("use #[export_name=\"*\"]");
+                diag.struct_span_err(attr.span,
+                                     "export_name attribute has invalid format")
+                    .help("use #[export_name=\"*\"]")
+                    .emit();
                 None
             }
         } else {
diff --git a/src/libsyntax/diagnostics/macros.rs b/src/libsyntax/diagnostics/macros.rs
index 3c8347f8a8e..95a74d87554 100644
--- a/src/libsyntax/diagnostics/macros.rs
+++ b/src/libsyntax/diagnostics/macros.rs
@@ -31,6 +31,14 @@ macro_rules! span_err {
 }
 
 #[macro_export]
+macro_rules! span_warn {
+    ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
+        __diagnostic_used!($code);
+        $session.span_warn_with_code($span, &format!($($message)*), stringify!($code))
+    })
+}
+
+#[macro_export]
 macro_rules! span_err_or_warn {
     ($is_warning:expr, $session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
         __diagnostic_used!($code);
@@ -43,31 +51,59 @@ macro_rules! span_err_or_warn {
 }
 
 #[macro_export]
-macro_rules! span_warn {
+macro_rules! struct_span_fatal {
     ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
         __diagnostic_used!($code);
-        $session.span_warn_with_code($span, &format!($($message)*), stringify!($code))
+        $session.struct_span_fatal_with_code($span, &format!($($message)*), stringify!($code))
+    })
+}
+
+#[macro_export]
+macro_rules! struct_span_err {
+    ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
+        __diagnostic_used!($code);
+        $session.struct_span_err_with_code($span, &format!($($message)*), stringify!($code))
+    })
+}
+
+#[macro_export]
+macro_rules! struct_span_warn {
+    ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
+        __diagnostic_used!($code);
+        $session.struct_span_warn_with_code($span, &format!($($message)*), stringify!($code))
+    })
+}
+
+#[macro_export]
+macro_rules! struct_span_err_or_warn {
+    ($is_warning:expr, $session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
+        __diagnostic_used!($code);
+        if $is_warning {
+            $session.struct_span_warn_with_code($span, &format!($($message)*), stringify!($code))
+        } else {
+            $session.struct_span_err_with_code($span, &format!($($message)*), stringify!($code))
+        }
     })
 }
 
 #[macro_export]
 macro_rules! span_note {
-    ($session:expr, $span:expr, $($message:tt)*) => ({
-        ($session).span_note($span, &format!($($message)*))
+    ($err:expr, $span:expr, $($message:tt)*) => ({
+        ($err).span_note($span, &format!($($message)*));
     })
 }
 
 #[macro_export]
 macro_rules! span_help {
-    ($session:expr, $span:expr, $($message:tt)*) => ({
-        ($session).span_help($span, &format!($($message)*))
+    ($err:expr, $span:expr, $($message:tt)*) => ({
+        ($err).span_help($span, &format!($($message)*));
     })
 }
 
 #[macro_export]
 macro_rules! fileline_help {
-    ($session:expr, $span:expr, $($message:tt)*) => ({
-        ($session).fileline_help($span, &format!($($message)*))
+    ($err:expr, $span:expr, $($message:tt)*) => ({
+        ($err).fileline_help($span, &format!($($message)*));
     })
 }
 
diff --git a/src/libsyntax/diagnostics/plugin.rs b/src/libsyntax/diagnostics/plugin.rs
index be0d5729c70..d17ca3892dc 100644
--- a/src/libsyntax/diagnostics/plugin.rs
+++ b/src/libsyntax/diagnostics/plugin.rs
@@ -62,10 +62,10 @@ pub fn expand_diagnostic_used<'cx>(ecx: &'cx mut ExtCtxt,
         match diagnostics.get_mut(&code.name) {
             // Previously used errors.
             Some(&mut ErrorInfo { description: _, use_site: Some(previous_span) }) => {
-                ecx.span_warn(span, &format!(
+                ecx.struct_span_warn(span, &format!(
                     "diagnostic code {} already used", code
-                ));
-                ecx.span_note(previous_span, "previous invocation");
+                )).span_note(previous_span, "previous invocation")
+                  .emit();
             }
             // Newly used errors.
             Some(ref mut info) => {
diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs
index 7fef85a833e..a7bfdedf718 100644
--- a/src/libsyntax/errors/emitter.rs
+++ b/src/libsyntax/errors/emitter.rs
@@ -13,7 +13,7 @@ use self::Destination::*;
 use codemap::{self, COMMAND_LINE_SP, COMMAND_LINE_EXPN, Pos, Span};
 use diagnostics;
 
-use errors::{Level, RenderSpan};
+use errors::{Level, RenderSpan, DiagnosticBuilder};
 use errors::RenderSpan::*;
 use errors::Level::*;
 
@@ -27,6 +27,17 @@ use term;
 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);
+
+    /// Emit a structured diagnostic.
+    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 {
+            match child.render_span {
+                Some(ref sp) => self.custom_emit(sp.clone(), &child.message, child.level),
+                None => self.emit(child.span, &child.message, None, child.level),
+            }
+        }
+    }
 }
 
 /// maximum number of lines we will print for each error; arbitrary.
@@ -49,8 +60,8 @@ impl ColorConfig {
     }
 }
 
-// A basic emitter for when we don't have access to a codemap or registry. Used
-// for reporting very early errors, etc.
+/// 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,
 }
@@ -111,9 +122,8 @@ impl Emitter for EmitterWriter {
                    sp: RenderSpan,
                    msg: &str,
                    lvl: Level) {
-        match self.emit_(sp, msg, None, lvl) {
-            Ok(()) => {}
-            Err(e) => panic!("failed to print diagnostics: {:?}", e),
+        if let Err(e) = self.emit_(sp, msg, None, lvl) {
+            panic!("failed to print diagnostics: {:?}", e);
         }
     }
 }
diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs
index f2e61090ba2..a2fae975148 100644
--- a/src/libsyntax/errors/mod.rs
+++ b/src/libsyntax/errors/mod.rs
@@ -98,6 +98,171 @@ impl error::Error for ExplicitBug {
     }
 }
 
+/// Used for emitting structured error messages and other diagnostic information.
+#[must_use]
+pub struct DiagnosticBuilder<'a> {
+    emitter: &'a RefCell<Box<Emitter>>,
+    level: Level,
+    message: String,
+    code: Option<String>,
+    span: Option<Span>,
+    children: Vec<SubDiagnostic>,
+}
+
+/// For example a note attached to an error.
+struct SubDiagnostic {
+    level: Level,
+    message: String,
+    span: Option<Span>,
+    render_span: Option<RenderSpan>,
+}
+
+impl<'a> DiagnosticBuilder<'a> {
+    /// Emit the diagnostic.
+    pub fn emit(&mut self) {
+        if self.cancelled() {
+            return;
+        }
+
+        self.emitter.borrow_mut().emit_struct(&self);
+        self.cancel();
+
+        // if self.is_fatal() {
+        //     panic!(FatalError);
+        // }
+    }
+
+    /// Cancel the diagnostic (a structured diagnostic must either be emitted or
+    /// cancelled or it will panic when dropped).
+    /// BEWARE: if this DiagnosticBuilder is an error, then creating it will
+    /// bump the error count on the Handler and cancelling it won't undo that.
+    /// If you want to decrement the error count you should use `Handler::cancel`.
+    pub fn cancel(&mut self) {
+        self.level = Level::Cancelled;
+    }
+
+    pub fn cancelled(&self) -> bool {
+        self.level == Level::Cancelled
+    }
+
+    pub fn is_fatal(&self) -> bool {
+        self.level == Level::Fatal
+    }
+
+    pub fn note(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Note, msg, None, None);
+        self
+    }
+    pub fn span_note(&mut self ,
+                     sp: Span,
+                     msg: &str)
+                     -> &mut DiagnosticBuilder<'a> {
+        self.sub(Level::Note, msg, Some(sp), None);
+        self
+    }
+    pub fn help(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Help, msg, None, None);
+        self
+    }
+    pub fn span_help(&mut self ,
+                     sp: Span,
+                     msg: &str)
+                     -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Help, msg, Some(sp), None);
+        self
+    }
+    /// Prints out a message with a suggested edit of the code.
+    ///
+    /// See `diagnostic::RenderSpan::Suggestion` for more information.
+    pub fn span_suggestion(&mut self ,
+                           sp: Span,
+                           msg: &str,
+                           suggestion: String)
+                           -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Help, msg, Some(sp), Some(Suggestion(sp, suggestion)));
+        self
+    }
+    pub fn span_end_note(&mut self ,
+                         sp: Span,
+                         msg: &str)
+                         -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Note, msg, Some(sp), Some(EndSpan(sp)));
+        self
+    }
+    pub fn fileline_note(&mut self ,
+                         sp: Span,
+                         msg: &str)
+                         -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Note, msg, Some(sp), Some(FileLine(sp)));
+        self
+    }
+    pub fn fileline_help(&mut self ,
+                         sp: Span,
+                         msg: &str)
+                         -> &mut DiagnosticBuilder<'a>  {
+        self.sub(Level::Help, msg, Some(sp), Some(FileLine(sp)));
+        self
+    }
+
+    pub fn span(&mut self, sp: Span) -> &mut Self {
+        self.span = Some(sp);
+        self
+    }
+
+    pub fn code(&mut self, s: String) -> &mut Self {
+        self.code = Some(s);
+        self
+    }
+
+    /// Convenience function for internal use, clients should use one of the
+    /// struct_* methods on Handler.
+    fn new(emitter: &'a RefCell<Box<Emitter>>,
+           level: Level,
+           message: &str) -> DiagnosticBuilder<'a>  {
+        DiagnosticBuilder {
+            emitter: emitter,
+            level: level,
+            message: message.to_owned(),
+            code: None,
+            span: None,
+            children: vec![],
+        }
+    }
+
+    /// Convenience function for internal use, clients should use one of the
+    /// public methods above.
+    fn sub(&mut self,
+           level: Level,
+           message: &str,
+           span: Option<Span>,
+           render_span: Option<RenderSpan>) {
+        let sub = SubDiagnostic {
+            level: level,
+            message: message.to_owned(),
+            span: span,
+            render_span: render_span,
+        };
+        self.children.push(sub);
+    }
+}
+
+impl<'a> fmt::Debug for DiagnosticBuilder<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.message.fmt(f)
+    }
+}
+
+/// Destructor bomb - a DiagnosticBuilder must be either emitted or cancelled or
+/// we emit a bug.
+impl<'a> Drop for DiagnosticBuilder<'a> {
+    fn drop(&mut self) {
+        if !self.cancelled() {
+            self.emitter.borrow_mut().emit(None, "Error constructed but not emitted", None, Bug);
+            panic!();
+        }
+    }
+}
+
 /// A handler deals with errors; certain errors
 /// (fatal, bug, unimpl) may cause immediate exit,
 /// others log errors for later reporting.
@@ -132,11 +297,104 @@ impl Handler {
         }
     }
 
+    pub fn struct_dummy<'a>(&'a self) -> DiagnosticBuilder<'a> {
+        DiagnosticBuilder::new(&self.emit, Level::Cancelled, "")
+    }
+
+    pub fn struct_span_warn<'a>(&'a self,
+                                sp: Span,
+                                msg: &str)
+                                -> DiagnosticBuilder<'a> {
+        let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
+        result.span(sp);
+        if !self.can_emit_warnings {
+            result.cancel();
+        }
+        result
+    }
+    pub fn struct_span_warn_with_code<'a>(&'a self,
+                                          sp: Span,
+                                          msg: &str,
+                                          code: &str)
+                                          -> DiagnosticBuilder<'a> {
+        let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
+        result.span(sp);
+        result.code(code.to_owned());
+        if !self.can_emit_warnings {
+            result.cancel();
+        }
+        result
+    }
+    pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
+        let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg);
+        if !self.can_emit_warnings {
+            result.cancel();
+        }
+        result
+    }
+    pub fn struct_span_err<'a>(&'a self,
+                               sp: Span,
+                               msg: &str)
+                               -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg);
+        result.span(sp);
+        result
+    }
+    pub fn struct_span_err_with_code<'a>(&'a self,
+                                         sp: Span,
+                                         msg: &str,
+                                         code: &str)
+                                         -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg);
+        result.span(sp);
+        result.code(code.to_owned());
+        result
+    }
+    pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        DiagnosticBuilder::new(&self.emit, Level::Error, msg)
+    }
+    pub fn struct_span_fatal<'a>(&'a self,
+                                 sp: Span,
+                                 msg: &str)
+                                 -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg);
+        result.span(sp);
+        result
+    }
+    pub fn struct_span_fatal_with_code<'a>(&'a self,
+                                           sp: Span,
+                                           msg: &str,
+                                           code: &str)
+                                           -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg);
+        result.span(sp);
+        result.code(code.to_owned());
+        result
+    }
+    pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> {
+        self.bump_err_count();
+        DiagnosticBuilder::new(&self.emit, Level::Fatal, msg)
+    }
+
+    pub fn cancel(&mut self, err: &mut DiagnosticBuilder) {
+        if err.level == Level::Error || err.level == Level::Fatal {
+            assert!(self.has_errors());
+            self.err_count.set(self.err_count.get() + 1);
+        }
+        err.cancel();
+    }
+
     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);
+        self.bump_err_count();
         return FatalError;
     }
     pub fn span_fatal_with_code(&self, sp: Span, msg: &str, code: &str) -> FatalError {
@@ -144,6 +402,7 @@ impl Handler {
             self.span_bug(sp, msg);
         }
         self.emit_with_code(Some(sp), msg, code, Fatal);
+        self.bump_err_count();
         return FatalError;
     }
     pub fn span_err(&self, sp: Span, msg: &str) {
@@ -166,27 +425,6 @@ impl Handler {
     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);
@@ -199,6 +437,9 @@ impl Handler {
         self.emit(Some(sp), msg, Bug);
         self.bump_err_count();
     }
+    pub fn span_note_without_error(&self, sp: Span, msg: &str) {
+        self.emit.borrow_mut().emit(Some(sp), msg, None, Note);
+    }
     pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! {
         self.span_bug(sp, &format!("unimplemented {}", msg));
     }
@@ -207,6 +448,7 @@ impl Handler {
             self.bug(msg);
         }
         self.emit.borrow_mut().emit(None, msg, None, Fatal);
+        self.bump_err_count();
         FatalError
     }
     pub fn err(&self, msg: &str) {
@@ -219,12 +461,9 @@ impl Handler {
     pub fn warn(&self, msg: &str) {
         self.emit.borrow_mut().emit(None, msg, None, Warning);
     }
-    pub fn note(&self, msg: &str) {
+    pub fn note_without_error(&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);
@@ -266,7 +505,7 @@ impl Handler {
             }
         }
 
-        panic!(self.fatal(&s[..]));
+        panic!(self.fatal(&s));
     }
 
     pub fn emit(&self,
@@ -301,6 +540,7 @@ pub enum Level {
     Warning,
     Note,
     Help,
+    Cancelled,
 }
 
 impl fmt::Display for Level {
@@ -313,6 +553,7 @@ impl fmt::Display for Level {
             Warning => "warning".fmt(f),
             Note => "note".fmt(f),
             Help => "help".fmt(f),
+            Cancelled => unreachable!(),
         }
     }
 }
@@ -324,6 +565,7 @@ impl Level {
             Warning => term::color::BRIGHT_YELLOW,
             Note => term::color::BRIGHT_GREEN,
             Help => term::color::BRIGHT_CYAN,
+            Cancelled => unreachable!(),
         }
     }
 }
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index f616420218e..fc0f1925207 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -14,6 +14,7 @@ use ast;
 use ast::Name;
 use codemap;
 use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION};
+use errors::DiagnosticBuilder;
 use ext;
 use ext::expand;
 use ext::tt::macro_rules;
@@ -678,6 +679,25 @@ impl<'a> ExtCtxt<'a> {
         }
     }
 
+    pub fn struct_span_warn(&self,
+                            sp: Span,
+                            msg: &str)
+                            -> DiagnosticBuilder<'a> {
+        self.parse_sess.span_diagnostic.struct_span_warn(sp, msg)
+    }
+    pub fn struct_span_err(&self,
+                           sp: Span,
+                           msg: &str)
+                           -> DiagnosticBuilder<'a> {
+        self.parse_sess.span_diagnostic.struct_span_err(sp, msg)
+    }
+    pub fn struct_span_fatal(&self,
+                             sp: Span,
+                             msg: &str)
+                             -> DiagnosticBuilder<'a> {
+        self.parse_sess.span_diagnostic.struct_span_fatal(sp, msg)
+    }
+
     /// Emit `msg` attached to `sp`, and stop compilation immediately.
     ///
     /// `span_err` should be strongly preferred where-ever possible:
@@ -710,15 +730,6 @@ impl<'a> ExtCtxt<'a> {
     pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
         self.parse_sess.span_diagnostic.span_bug(sp, msg);
     }
-    pub fn span_note(&self, sp: Span, msg: &str) {
-        self.parse_sess.span_diagnostic.span_note(sp, msg);
-    }
-    pub fn span_help(&self, sp: Span, msg: &str) {
-        self.parse_sess.span_diagnostic.span_help(sp, msg);
-    }
-    pub fn fileline_help(&self, sp: Span, msg: &str) {
-        self.parse_sess.span_diagnostic.fileline_help(sp, msg);
-    }
     pub fn bug(&self, msg: &str) -> ! {
         self.parse_sess.span_diagnostic.bug(msg);
     }
@@ -743,10 +754,13 @@ impl<'a> ExtCtxt<'a> {
         token::intern(st)
     }
 
-    pub fn suggest_macro_name(&mut self, name: &str, span: Span) {
+    pub fn suggest_macro_name(&mut self,
+                              name: &str,
+                              span: Span,
+                              err: &mut DiagnosticBuilder<'a>) {
         let names = &self.syntax_env.names;
         if let Some(suggestion) = find_best_match_for_name(names.iter(), name, None) {
-            self.fileline_help(span, &format!("did you mean `{}!`?", suggestion));
+            err.fileline_help(span, &format!("did you mean `{}!`?", suggestion));
         }
     }
 }
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 743bcda18de..5f27bdfc98a 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -201,11 +201,12 @@ fn expand_mac_invoc<T, F, G>(mac: ast::Mac,
     let extname = pth.segments[0].identifier.name;
     match fld.cx.syntax_env.find(extname) {
         None => {
-            fld.cx.span_err(
+            let mut err = fld.cx.struct_span_err(
                 pth.span,
                 &format!("macro undefined: '{}!'",
                         &extname));
-            fld.cx.suggest_macro_name(&extname.as_str(), pth.span);
+            fld.cx.suggest_macro_name(&extname.as_str(), pth.span, &mut err);
+            err.emit();
 
             // let compilation continue
             None
@@ -334,11 +335,15 @@ fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool
     for attr in attrs {
         let mut is_use = attr.check_name("macro_use");
         if attr.check_name("macro_escape") {
-            fld.cx.span_warn(attr.span, "macro_escape is a deprecated synonym for macro_use");
+            let mut err =
+                fld.cx.struct_span_warn(attr.span,
+                                        "macro_escape is a deprecated synonym for macro_use");
             is_use = true;
             if let ast::AttrStyle::Inner = attr.node.style {
-                fld.cx.fileline_help(attr.span, "consider an outer attribute, \
-                                             #[macro_use] mod ...");
+                err.fileline_help(attr.span, "consider an outer attribute, \
+                                              #[macro_use] mod ...").emit();
+            } else {
+                err.emit();
             }
         };
 
diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs
index 0c3a8b05fba..7a1a207a562 100644
--- a/src/libsyntax/ext/quote.rs
+++ b/src/libsyntax/ext/quote.rs
@@ -802,7 +802,7 @@ fn parse_arguments_to_quote(cx: &ExtCtxt, tts: &[TokenTree])
 
     let cx_expr = panictry!(p.parse_expr());
     if !panictry!(p.eat(&token::Comma)) {
-        panic!(p.fatal("expected token `,`"));
+        let _ = p.diagnostic().fatal("expected token `,`");
     }
 
     let tts = panictry!(p.parse_all_token_trees());
diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs
index 7899e170ecf..f00224bacdd 100644
--- a/src/libsyntax/ext/source_util.rs
+++ b/src/libsyntax/ext/source_util.rs
@@ -117,11 +117,9 @@ pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree
             while self.p.token != token::Eof {
                 match panictry!(self.p.parse_item()) {
                     Some(item) => ret.push(item),
-                    None => panic!(self.p.span_fatal(
-                        self.p.span,
-                        &format!("expected item, found `{}`",
-                                 self.p.this_token_to_string())
-                    ))
+                    None => panic!(self.p.diagnostic().span_fatal(self.p.span,
+                                                           &format!("expected item, found `{}`",
+                                                                    self.p.this_token_to_string())))
                 }
             }
             Some(ret)
diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs
index fb09118035c..166d32a8cc6 100644
--- a/src/libsyntax/ext/tt/macro_parser.rs
+++ b/src/libsyntax/ext/tt/macro_parser.rs
@@ -82,6 +82,7 @@ use ast;
 use ast::{TokenTree, Name, Ident};
 use codemap::{BytePos, mk_sp, Span, Spanned};
 use codemap;
+use errors::FatalError;
 use parse::lexer::*; //resolve bug?
 use parse::ParseSess;
 use parse::parser::{LifetimeAndTypesWithoutColons, Parser};
@@ -499,11 +500,12 @@ pub fn parse(sess: &ParseSess,
     }
 }
 
-pub fn parse_nt(p: &mut Parser, sp: Span, name: &str) -> Nonterminal {
+pub fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal {
     match name {
         "tt" => {
             p.quote_depth += 1; //but in theory, non-quoted tts might be useful
-            let res = token::NtTT(P(panictry!(p.parse_token_tree())));
+            let res: ::parse::PResult<'a, _> = p.parse_token_tree();
+            let res = token::NtTT(P(panictry!(res)));
             p.quote_depth -= 1;
             return res;
         }
@@ -514,12 +516,18 @@ pub fn parse_nt(p: &mut Parser, sp: Span, name: &str) -> Nonterminal {
     match name {
         "item" => match panictry!(p.parse_item()) {
             Some(i) => token::NtItem(i),
-            None => panic!(p.fatal("expected an item keyword"))
+            None => {
+                p.fatal("expected an item keyword").emit();
+                panic!(FatalError);
+            }
         },
         "block" => token::NtBlock(panictry!(p.parse_block())),
         "stmt" => match panictry!(p.parse_stmt()) {
             Some(s) => token::NtStmt(s),
-            None => panic!(p.fatal("expected a statement"))
+            None => {
+                p.fatal("expected a statement").emit();
+                panic!(FatalError);
+            }
         },
         "pat" => token::NtPat(panictry!(p.parse_pat())),
         "expr" => token::NtExpr(panictry!(p.parse_expr())),
@@ -532,8 +540,9 @@ pub fn parse_nt(p: &mut Parser, sp: Span, name: &str) -> Nonterminal {
             }
             _ => {
                 let token_str = pprust::token_to_string(&p.token);
-                panic!(p.fatal(&format!("expected ident, found {}",
-                                 &token_str[..])))
+                p.fatal(&format!("expected ident, found {}",
+                                 &token_str[..])).emit();
+                panic!(FatalError)
             }
         },
         "path" => {
@@ -541,11 +550,12 @@ pub fn parse_nt(p: &mut Parser, sp: Span, name: &str) -> Nonterminal {
         },
         "meta" => token::NtMeta(panictry!(p.parse_meta_item())),
         _ => {
-            panic!(p.span_fatal_help(sp,
-                            &format!("invalid fragment specifier `{}`", name),
-                            "valid fragment specifiers are `ident`, `block`, \
-                             `stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \
-                             and `item`"))
+            p.span_fatal_help(sp,
+                              &format!("invalid fragment specifier `{}`", name),
+                              "valid fragment specifiers are `ident`, `block`, \
+                               `stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \
+                               and `item`").emit();
+            panic!(FatalError);
         }
     }
 }
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index fae60d28032..fd0bbf7a072 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -55,12 +55,12 @@ impl<'a> ParserAnyMacro<'a> {
                                following",
                               token_str);
             let span = parser.span;
-            parser.span_err(span, &msg[..]);
-
+            let mut err = parser.diagnostic().struct_span_err(span, &msg[..]);
             let msg = format!("caused by the macro expansion here; the usage \
                                of `{}!` is likely invalid in {} context",
                                self.macro_ident, context);
-            parser.span_note(self.site_span, &msg[..]);
+            err.span_note(self.site_span, &msg[..])
+               .emit();
         }
     }
 }
@@ -111,7 +111,10 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
                         Some(stmt) => ret.push(stmt),
                         None => (),
                     },
-                    Err(_) => break,
+                    Err(mut e) => {
+                        e.emit();
+                        break;
+                    }
                 }
             }
         }
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 4ea0fd76fea..a23dc3b8871 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -725,17 +725,21 @@ pub fn emit_feature_err(diag: &Handler, feature: &str, span: Span, issue: GateIs
         GateIssue::Library(lib) => lib,
     };
 
-    if let Some(n) = issue {
-        diag.span_err(span, &format!("{} (see issue #{})", explain, n));
+    let mut err = if let Some(n) = issue {
+        diag.struct_span_err(span, &format!("{} (see issue #{})", explain, n))
     } else {
-        diag.span_err(span, explain);
-    }
+        diag.struct_span_err(span, explain)
+    };
 
     // #23973: do not suggest `#![feature(...)]` if we are in beta/stable
-    if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some() { return; }
-    diag.fileline_help(span, &format!("add #![feature({})] to the \
-                                   crate attributes to enable",
-                                  feature));
+    if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some() {
+        err.emit();
+        return;
+    }
+    err.fileline_help(span, &format!("add #![feature({})] to the \
+                                      crate attributes to enable",
+                                     feature));
+    err.emit();
 }
 
 pub const EXPLAIN_ASM: &'static str =
@@ -942,11 +946,13 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
                 self.gate_feature("braced_empty_structs", span,
                                   "empty structs and enum variants with braces are unstable");
             } else if s.is_tuple() {
-                self.context.span_handler.span_err(span, "empty tuple structs and enum variants \
-                                                          are not allowed, use unit structs and \
-                                                          enum variants instead");
-                self.context.span_handler.span_help(span, "remove trailing `()` to make a unit \
-                                                           struct or unit enum variant");
+                self.context.span_handler.struct_span_err(span, "empty tuple structs and enum \
+                                                                 variants are not allowed, use \
+                                                                 unit structs and enum variants \
+                                                                 instead")
+                                         .span_help(span, "remove trailing `()` to make a unit \
+                                                           struct or unit enum variant")
+                                         .emit();
             }
         }
         visit::walk_struct_def(self, s)
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index c09e35f1077..795f4044f6e 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -40,17 +40,22 @@ extern crate libc;
 
 extern crate serialize as rustc_serialize; // used by deriving
 
-// A variant of 'try!' that panics on Err(FatalError). This is used as a
-// crutch on the way towards a non-panic!-prone parser. It should be used
-// for fatal parsing errors; eventually we plan to convert all code using
-// panictry to just use normal try
+// A variant of 'try!' that panics on an Err. This is used as a crutch on the
+// way towards a non-panic!-prone parser. It should be used for fatal parsing
+// errors; eventually we plan to convert all code using panictry to just use
+// normal try.
+// Exported for syntax_ext, not meant for general use.
+#[macro_export]
 macro_rules! panictry {
     ($e:expr) => ({
         use std::result::Result::{Ok, Err};
-        use errors::FatalError;
+        use $crate::errors::FatalError;
         match $e {
             Ok(e) => e,
-            Err(FatalError) => panic!(FatalError)
+            Err(mut e) => {
+                e.emit();
+                panic!(FatalError);
+            }
         }
     })
 }
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index 5df2478d487..35ec3dcb0fd 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -19,7 +19,7 @@ use ptr::P;
 
 impl<'a> Parser<'a> {
     /// Parse attributes that appear before an item
-    pub fn parse_outer_attributes(&mut self) -> PResult<Vec<ast::Attribute>> {
+    pub fn parse_outer_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
         let mut attrs: Vec<ast::Attribute> = Vec::new();
         loop {
             debug!("parse_outer_attributes: self.token={:?}",
@@ -51,7 +51,7 @@ impl<'a> Parser<'a> {
     ///
     /// If permit_inner is true, then a leading `!` indicates an inner
     /// attribute
-    pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<ast::Attribute> {
+    pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> {
         debug!("parse_attributes: permit_inner={:?} self.token={:?}",
                permit_inner, self.token);
         let (span, value, mut style) = match self.token {
@@ -64,11 +64,13 @@ impl<'a> Parser<'a> {
                     try!(self.bump());
                     if !permit_inner {
                         let span = self.span;
-                        self.span_err(span,
-                                      "an inner attribute is not permitted in \
-                                       this context");
-                        self.fileline_help(span,
-                                       "place inner attribute at the top of the module or block");
+                        self.diagnostic().struct_span_err(span,
+                                                          "an inner attribute is not permitted in \
+                                                           this context")
+                                         .fileline_help(span,
+                                                        "place inner attribute at the top of \
+                                                         the module or block")
+                                         .emit()
                     }
                     ast::AttrStyle::Inner
                 } else {
@@ -111,7 +113,7 @@ impl<'a> Parser<'a> {
     /// terminated by a semicolon.
 
     /// matches inner_attrs*
-    pub fn parse_inner_attributes(&mut self) -> PResult<Vec<ast::Attribute>> {
+    pub fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> {
         let mut attrs: Vec<ast::Attribute> = vec![];
         loop {
             match self.token {
@@ -146,7 +148,7 @@ impl<'a> Parser<'a> {
     /// matches meta_item = IDENT
     /// | IDENT = lit
     /// | IDENT meta_seq
-    pub fn parse_meta_item(&mut self) -> PResult<P<ast::MetaItem>> {
+    pub fn parse_meta_item(&mut self) -> PResult<'a, P<ast::MetaItem>> {
         let nt_meta = match self.token {
             token::Interpolated(token::NtMeta(ref e)) => {
                 Some(e.clone())
@@ -195,10 +197,10 @@ impl<'a> Parser<'a> {
     }
 
     /// matches meta_seq = ( COMMASEP(meta_item) )
-    fn parse_meta_seq(&mut self) -> PResult<Vec<P<ast::MetaItem>>> {
+    fn parse_meta_seq(&mut self) -> PResult<'a, Vec<P<ast::MetaItem>>> {
         self.parse_unspanned_seq(&token::OpenDelim(token::Paren),
                                  &token::CloseDelim(token::Paren),
                                  seq_sep_trailing_allowed(token::Comma),
-                                 |p| p.parse_meta_item())
+                                 |p: &mut Parser<'a>| p.parse_meta_item())
     }
 }
diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs
index 4619410ada7..3d8f3bcd526 100644
--- a/src/libsyntax/parse/lexer/mod.rs
+++ b/src/libsyntax/parse/lexer/mod.rs
@@ -11,7 +11,7 @@
 use ast;
 use codemap::{BytePos, CharPos, CodeMap, Pos, Span};
 use codemap;
-use errors::{FatalError, Handler};
+use errors::{FatalError, Handler, DiagnosticBuilder};
 use ext::tt::transcribe::tt_next_token;
 use parse::token::str_to_ident;
 use parse::token;
@@ -173,10 +173,6 @@ impl<'a> StringReader<'a> {
         self.span_diagnostic.span_err(sp, m)
     }
 
-    /// Suggest some help with a given span.
-    pub fn help_span(&self, sp: Span, m: &str) {
-        self.span_diagnostic.span_help(sp, m)
-    }
 
     /// Report a fatal error spanning [`from_pos`, `to_pos`).
     fn fatal_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) -> FatalError {
@@ -188,11 +184,6 @@ impl<'a> StringReader<'a> {
         self.err_span(codemap::mk_sp(from_pos, to_pos), m)
     }
 
-    /// Suggest some help spanning [`from_pos`, `to_pos`).
-    fn help_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) {
-        self.help_span(codemap::mk_sp(from_pos, to_pos), m)
-    }
-
     /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an
     /// escaped character to the error message
     fn fatal_span_char(&self, from_pos: BytePos, to_pos: BytePos, m: &str, c: char) -> FatalError {
@@ -201,6 +192,17 @@ impl<'a> StringReader<'a> {
         for c in c.escape_default() { m.push(c) }
         self.fatal_span_(from_pos, to_pos, &m[..])
     }
+    fn struct_fatal_span_char(&self,
+                              from_pos: BytePos,
+                              to_pos: BytePos,
+                              m: &str,
+                              c: char)
+                              -> DiagnosticBuilder<'a>  {
+        let mut m = m.to_string();
+        m.push_str(": ");
+        for c in c.escape_default() { m.push(c) }
+        self.span_diagnostic.struct_span_fatal(codemap::mk_sp(from_pos, to_pos), &m[..])
+    }
 
     /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an
     /// escaped character to the error message
@@ -210,6 +212,17 @@ impl<'a> StringReader<'a> {
         for c in c.escape_default() { m.push(c) }
         self.err_span_(from_pos, to_pos, &m[..]);
     }
+    fn struct_err_span_char(&self,
+                            from_pos: BytePos,
+                            to_pos: BytePos,
+                            m: &str,
+                            c: char)
+                            -> DiagnosticBuilder<'a>  {
+        let mut m = m.to_string();
+        m.push_str(": ");
+        for c in c.escape_default() { m.push(c) }
+        self.span_diagnostic.struct_span_err(codemap::mk_sp(from_pos, to_pos), &m[..])
+    }
 
     /// Report a lexical error spanning [`from_pos`, `to_pos`), appending the
     /// offending string to the error message
@@ -746,10 +759,12 @@ impl<'a> StringReader<'a> {
                                 let valid = if self.curr_is('{') {
                                     self.scan_unicode_escape(delim) && !ascii_only
                                 } else {
-                                    self.err_span_(start, self.last_pos,
-                                        "incorrect unicode escape sequence");
-                                    self.help_span_(start, self.last_pos,
-                                        "format of unicode escape sequences is `\\u{…}`");
+                                    let span = codemap::mk_sp(start, self.last_pos);
+                                    self.span_diagnostic.struct_span_err(span,
+                                        "incorrect unicode escape sequence")
+                                        .span_help(span,
+                                        "format of unicode escape sequences is `\\u{…}`")
+                                        .emit();
                                     false
                                 };
                                 if ascii_only {
@@ -771,21 +786,22 @@ impl<'a> StringReader<'a> {
                             }
                             c => {
                                 let last_pos = self.last_pos;
-                                self.err_span_char(
+                                let mut err = self.struct_err_span_char(
                                     escaped_pos, last_pos,
                                     if ascii_only { "unknown byte escape" }
                                     else { "unknown character escape" },
                                     c);
                                 if e == '\r' {
-                                    self.help_span_(escaped_pos, last_pos,
+                                    err.span_help(codemap::mk_sp(escaped_pos, last_pos),
                                         "this is an isolated carriage return; consider checking \
-                                         your editor and version control settings")
+                                         your editor and version control settings");
                                 }
                                 if (e == '{' || e == '}') && !ascii_only {
-                                    self.help_span_(escaped_pos, last_pos,
+                                    err.span_help(codemap::mk_sp(escaped_pos, last_pos),
                                         "if used in a formatting string, \
-                                        curly braces are escaped with `{{` and `}}`")
+                                        curly braces are escaped with `{{` and `}}`");
                                 }
+                                err.emit();
                                 false
                             }
                         }
@@ -1224,8 +1240,13 @@ impl<'a> StringReader<'a> {
           c => {
               let last_bpos = self.last_pos;
               let bpos = self.pos;
-              unicode_chars::check_for_substitution(&self, c);
-              panic!(self.fatal_span_char(last_bpos, bpos, "unknown start of token", c))
+              let mut err = self.struct_fatal_span_char(last_bpos,
+                                                        bpos,
+                                                        "unknown start of token",
+                                                        c);
+              unicode_chars::check_for_substitution(&self, c, &mut err);
+              err.emit();
+              panic!(FatalError);
           }
         }
     }
diff --git a/src/libsyntax/parse/lexer/unicode_chars.rs b/src/libsyntax/parse/lexer/unicode_chars.rs
index a2f37426210..1d32dd49731 100644
--- a/src/libsyntax/parse/lexer/unicode_chars.rs
+++ b/src/libsyntax/parse/lexer/unicode_chars.rs
@@ -12,6 +12,7 @@
 // http://www.unicode.org/Public/security/revision-06/confusables.txt
 
 use codemap::mk_sp as make_span;
+use errors::DiagnosticBuilder;
 use super::StringReader;
 
 const UNICODE_ARRAY: &'static [(char, &'static str, char)] = &[
@@ -179,7 +180,9 @@ const ASCII_ARRAY: &'static [(char, &'static str)] = &[
     ('=', "Equals Sign"),
     ('>', "Greater-Than Sign"), ];
 
-pub fn check_for_substitution(reader: &StringReader, ch: char) {
+pub fn check_for_substitution<'a>(reader: &StringReader<'a>,
+                                  ch: char,
+                                  err: &mut DiagnosticBuilder<'a>) {
     UNICODE_ARRAY
     .iter()
     .find(|&&(c, _, _)| c == ch)
@@ -190,7 +193,7 @@ pub fn check_for_substitution(reader: &StringReader, ch: char) {
                 let msg =
                     format!("unicode character '{}' ({}) looks much like '{}' ({}), but it's not",
                             ch, u_name, ascii_char, ascii_name);
-                reader.help_span(span, &msg);
+                err.span_help(span, &msg);
             },
             None => {
                 reader
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index 6951d6319ac..efbde0f85a6 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 errors::{Handler, ColorConfig, FatalError};
+use errors::{Handler, ColorConfig, DiagnosticBuilder};
 use parse::parser::Parser;
 use parse::token::InternedString;
 use ptr::P;
@@ -25,7 +25,7 @@ use std::path::{Path, PathBuf};
 use std::rc::Rc;
 use std::str;
 
-pub type PResult<T> = Result<T, FatalError>;
+pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;
 
 #[macro_use]
 pub mod parser;
@@ -76,8 +76,8 @@ pub fn parse_crate_from_file(
     cfg: ast::CrateConfig,
     sess: &ParseSess
 ) -> ast::Crate {
-    panictry!(new_parser_from_file(sess, cfg, input).parse_crate_mod())
-    // why is there no p.abort_if_errors here?
+    let mut parser = new_parser_from_file(sess, cfg, input);
+    abort_if_errors(parser.parse_crate_mod(), &parser)
 }
 
 pub fn parse_crate_attrs_from_file(
@@ -85,8 +85,8 @@ pub fn parse_crate_attrs_from_file(
     cfg: ast::CrateConfig,
     sess: &ParseSess
 ) -> Vec<ast::Attribute> {
-    // FIXME: maybe_aborted?
-    panictry!(new_parser_from_file(sess, cfg, input).parse_inner_attributes())
+    let mut parser = new_parser_from_file(sess, cfg, input);
+    abort_if_errors(parser.parse_inner_attributes(), &parser)
 }
 
 pub fn parse_crate_from_source_str(name: String,
@@ -271,6 +271,20 @@ pub fn maybe_aborted<T>(result: T, p: Parser) -> T {
     result
 }
 
+fn abort_if_errors<'a, T>(result: PResult<'a, T>, p: &Parser) -> T {
+    match result {
+        Ok(c) => {
+            p.abort_if_errors();
+            c
+        }
+        Err(mut e) => {
+            e.emit();
+            p.abort_if_errors();
+            unreachable!();
+        }
+    }
+}
+
 /// Parse a string representing a character literal into its final form.
 /// Rather than just accepting/rejecting a given literal, unescapes it as
 /// well. Can take any slice prefixed by a character escape. Returns the
@@ -449,11 +463,13 @@ fn filtered_float_lit(data: token::InternedString, suffix: Option<&str>,
         Some(suf) => {
             if suf.len() >= 2 && looks_like_width_suffix(&['f'], suf) {
                 // if it looks like a width, lets try to be helpful.
-                sd.span_err(sp, &format!("invalid width `{}` for float literal", &suf[1..]));
-                sd.fileline_help(sp, "valid widths are 32 and 64");
+                sd.struct_span_err(sp, &format!("invalid width `{}` for float literal", &suf[1..]))
+                 .fileline_help(sp, "valid widths are 32 and 64")
+                 .emit();
             } else {
-                sd.span_err(sp, &format!("invalid suffix `{}` for float literal", suf));
-                sd.fileline_help(sp, "valid suffixes are `f32` and `f64`");
+                sd.struct_span_err(sp, &format!("invalid suffix `{}` for float literal", suf))
+                  .fileline_help(sp, "valid suffixes are `f32` and `f64`")
+                  .emit();
             }
 
             ast::LitFloatUnsuffixed(data)
@@ -622,13 +638,15 @@ pub fn integer_lit(s: &str,
                 // i<digits> and u<digits> look like widths, so lets
                 // give an error message along those lines
                 if looks_like_width_suffix(&['i', 'u'], suf) {
-                    sd.span_err(sp, &format!("invalid width `{}` for integer literal",
-                                             &suf[1..]));
-                    sd.fileline_help(sp, "valid widths are 8, 16, 32 and 64");
+                    sd.struct_span_err(sp, &format!("invalid width `{}` for integer literal",
+                                             &suf[1..]))
+                      .fileline_help(sp, "valid widths are 8, 16, 32 and 64")
+                      .emit();
                 } else {
-                    sd.span_err(sp, &format!("invalid suffix `{}` for numeric literal", suf));
-                    sd.fileline_help(sp, "the suffix must be one of the integral types \
-                                      (`u32`, `isize`, etc)");
+                    sd.struct_span_err(sp, &format!("invalid suffix `{}` for numeric literal", suf))
+                      .fileline_help(sp, "the suffix must be one of the integral types \
+                                      (`u32`, `isize`, etc)")
+                      .emit();
                 }
 
                 ty
diff --git a/src/libsyntax/parse/obsolete.rs b/src/libsyntax/parse/obsolete.rs
index 5dba1e189ab..75f1ac49c9a 100644
--- a/src/libsyntax/parse/obsolete.rs
+++ b/src/libsyntax/parse/obsolete.rs
@@ -59,18 +59,17 @@ impl<'a> ParserObsoleteMethods for parser::Parser<'a> {
               kind_str: &str,
               desc: &str,
               error: bool) {
-        if error {
-            self.span_err(sp, &format!("obsolete syntax: {}", kind_str));
+        let mut err = if error {
+            self.diagnostic().struct_span_err(sp, &format!("obsolete syntax: {}", kind_str))
         } else {
-            self.span_warn(sp, &format!("obsolete syntax: {}", kind_str));
-        }
+            self.diagnostic().struct_span_warn(sp, &format!("obsolete syntax: {}", kind_str))
+        };
 
         if !self.obsolete_set.contains(&kind) &&
             (error || self.sess.span_diagnostic.can_emit_warnings) {
-            self.sess
-                .span_diagnostic
-                .note(&format!("{}", desc));
+            err.note(&format!("{}", desc));
             self.obsolete_set.insert(kind);
         }
+        err.emit();
     }
 }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index dbd34b49f7d..efd351a632d 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 errors::{self, FatalError};
+use errors::{self, DiagnosticBuilder};
 use ext::tt::macro_parser;
 use parse;
 use parse::classify;
@@ -392,14 +392,14 @@ impl<'a> Parser<'a> {
         Parser::token_to_string(&self.token)
     }
 
-    pub fn unexpected_last(&self, t: &token::Token) -> FatalError {
+    pub fn unexpected_last(&self, t: &token::Token) -> DiagnosticBuilder<'a> {
         let token_str = Parser::token_to_string(t);
         let last_span = self.last_span;
         self.span_fatal(last_span, &format!("unexpected token: `{}`",
                                                 token_str))
     }
 
-    pub fn unexpected(&mut self) -> FatalError {
+    pub fn unexpected(&mut self) -> DiagnosticBuilder<'a> {
         match self.expect_one_of(&[], &[]) {
             Err(e) => e,
             Ok(_) => unreachable!()
@@ -408,7 +408,7 @@ impl<'a> Parser<'a> {
 
     /// Expect and consume the token t. Signal an error if
     /// the next token is not t.
-    pub fn expect(&mut self, t: &token::Token) -> PResult<()> {
+    pub fn expect(&mut self, t: &token::Token) -> PResult<'a,  ()> {
         if self.expected_tokens.is_empty() {
             if self.token == *t {
                 self.bump()
@@ -429,7 +429,7 @@ impl<'a> Parser<'a> {
     /// anything.  Signal a fatal error if next token is unexpected.
     pub fn expect_one_of(&mut self,
                          edible: &[token::Token],
-                         inedible: &[token::Token]) -> PResult<()>{
+                         inedible: &[token::Token]) -> PResult<'a,  ()>{
         fn tokens_to_string(tokens: &[TokenType]) -> String {
             let mut i = tokens.iter();
             // This might be a sign we need a connect method on Iterator.
@@ -484,7 +484,7 @@ impl<'a> Parser<'a> {
     /// true if and only if input was consumed for recovery.
     pub fn check_for_erroneous_unit_struct_expecting(&mut self,
                                                      expected: &[token::Token])
-                                                     -> PResult<bool> {
+                                                     -> PResult<'a, bool> {
         if self.token == token::OpenDelim(token::Brace)
             && expected.iter().all(|t| *t != token::OpenDelim(token::Brace))
             && self.look_ahead(1, |t| *t == token::CloseDelim(token::Brace)) {
@@ -504,7 +504,7 @@ impl<'a> Parser<'a> {
     /// followed by some token from the set edible + inedible.  Recover
     /// from anticipated input errors, discarding erroneous characters.
     pub fn commit_expr(&mut self, e: &Expr, edible: &[token::Token],
-                       inedible: &[token::Token]) -> PResult<()> {
+                       inedible: &[token::Token]) -> PResult<'a, ()> {
         debug!("commit_expr {:?}", e);
         if let ExprPath(..) = e.node {
             // might be unit-struct construction; check for recoverableinput error.
@@ -517,7 +517,7 @@ impl<'a> Parser<'a> {
         self.expect_one_of(edible, inedible)
     }
 
-    pub fn commit_expr_expecting(&mut self, e: &Expr, edible: token::Token) -> PResult<()> {
+    pub fn commit_expr_expecting(&mut self, e: &Expr, edible: token::Token) -> PResult<'a, ()> {
         self.commit_expr(e, &[edible], &[])
     }
 
@@ -525,7 +525,7 @@ impl<'a> Parser<'a> {
     /// followed by some token from the set edible + inedible.  Check
     /// for recoverable input errors, discarding erroneous characters.
     pub fn commit_stmt(&mut self, edible: &[token::Token],
-                       inedible: &[token::Token]) -> PResult<()> {
+                       inedible: &[token::Token]) -> PResult<'a, ()> {
         if self.last_token
                .as_ref()
                .map_or(false, |t| t.is_ident() || t.is_path()) {
@@ -538,11 +538,11 @@ impl<'a> Parser<'a> {
         self.expect_one_of(edible, inedible)
     }
 
-    pub fn commit_stmt_expecting(&mut self, edible: token::Token) -> PResult<()> {
+    pub fn commit_stmt_expecting(&mut self, edible: token::Token) -> PResult<'a, ()> {
         self.commit_stmt(&[edible], &[])
     }
 
-    pub fn parse_ident(&mut self) -> PResult<ast::Ident> {
+    pub fn parse_ident(&mut self) -> PResult<'a, ast::Ident> {
         self.check_strict_keywords();
         try!(self.check_reserved_keywords());
         match self.token {
@@ -561,7 +561,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn parse_ident_or_self_type(&mut self) -> PResult<ast::Ident> {
+    pub fn parse_ident_or_self_type(&mut self) -> PResult<'a, ast::Ident> {
         if self.is_self_type_ident() {
             self.expect_self_type_ident()
         } else {
@@ -569,7 +569,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn parse_path_list_item(&mut self) -> PResult<ast::PathListItem> {
+    pub fn parse_path_list_item(&mut self) -> PResult<'a, ast::PathListItem> {
         let lo = self.span.lo;
         let node = if try!(self.eat_keyword(keywords::SelfValue)) {
             let rename = try!(self.parse_rename());
@@ -595,7 +595,7 @@ impl<'a> Parser<'a> {
 
     /// Consume token 'tok' if it exists. Returns true if the given
     /// token was present, false otherwise.
-    pub fn eat(&mut self, tok: &token::Token) -> PResult<bool> {
+    pub fn eat(&mut self, tok: &token::Token) -> PResult<'a, bool> {
         let is_present = self.check(tok);
         if is_present { try!(self.bump())}
         Ok(is_present)
@@ -608,7 +608,7 @@ impl<'a> Parser<'a> {
 
     /// If the next token is the given keyword, eat it and return
     /// true. Otherwise, return false.
-    pub fn eat_keyword(&mut self, kw: keywords::Keyword) -> PResult<bool> {
+    pub fn eat_keyword(&mut self, kw: keywords::Keyword) -> PResult<'a, bool> {
         if self.check_keyword(kw) {
             try!(self.bump());
             Ok(true)
@@ -617,7 +617,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn eat_keyword_noexpect(&mut self, kw: keywords::Keyword) -> PResult<bool> {
+    pub fn eat_keyword_noexpect(&mut self, kw: keywords::Keyword) -> PResult<'a, bool> {
         if self.token.is_keyword(kw) {
             try!(self.bump());
             Ok(true)
@@ -629,7 +629,7 @@ impl<'a> Parser<'a> {
     /// If the given word is not a keyword, signal an error.
     /// If the next token is not the given word, signal an error.
     /// Otherwise, eat it.
-    pub fn expect_keyword(&mut self, kw: keywords::Keyword) -> PResult<()> {
+    pub fn expect_keyword(&mut self, kw: keywords::Keyword) -> PResult<'a, ()> {
         if !try!(self.eat_keyword(kw) ){
             self.expect_one_of(&[], &[])
         } else {
@@ -649,11 +649,10 @@ impl<'a> Parser<'a> {
     }
 
     /// Signal an error if the current token is a reserved keyword
-    pub fn check_reserved_keywords(&mut self) -> PResult<()>{
+    pub fn check_reserved_keywords(&mut self) -> PResult<'a, ()>{
         if self.token.is_reserved_keyword() {
             let token_str = self.this_token_to_string();
-            Err(self.fatal(&format!("`{}` is a reserved keyword",
-                               token_str)))
+            Err(self.fatal(&format!("`{}` is a reserved keyword", token_str)))
         } else {
             Ok(())
         }
@@ -661,7 +660,7 @@ impl<'a> Parser<'a> {
 
     /// Expect and consume an `&`. If `&&` is seen, replace it with a single
     /// `&` and continue. If an `&` is not seen, signal an error.
-    fn expect_and(&mut self) -> PResult<()> {
+    fn expect_and(&mut self) -> PResult<'a, ()> {
         self.expected_tokens.push(TokenType::Token(token::BinOp(token::And)));
         match self.token {
             token::BinOp(token::And) => self.bump(),
@@ -693,7 +692,7 @@ impl<'a> Parser<'a> {
     ///
     /// This is meant to be used when parsing generics on a path to get the
     /// starting token.
-    fn eat_lt(&mut self) -> PResult<bool> {
+    fn eat_lt(&mut self) -> PResult<'a, bool> {
         self.expected_tokens.push(TokenType::Token(token::Lt));
         match self.token {
             token::Lt => { try!(self.bump()); Ok(true)}
@@ -707,7 +706,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn expect_lt(&mut self) -> PResult<()> {
+    fn expect_lt(&mut self) -> PResult<'a, ()> {
         if !try!(self.eat_lt()) {
             self.expect_one_of(&[], &[])
         } else {
@@ -718,7 +717,7 @@ impl<'a> Parser<'a> {
     /// Expect and consume a GT. if a >> is seen, replace it
     /// with a single > and continue. If a GT is not seen,
     /// signal an error.
-    pub fn expect_gt(&mut self) -> PResult<()> {
+    pub fn expect_gt(&mut self) -> PResult<'a, ()> {
         self.expected_tokens.push(TokenType::Token(token::Gt));
         match self.token {
             token::Gt => self.bump(),
@@ -750,8 +749,8 @@ impl<'a> Parser<'a> {
     pub fn parse_seq_to_before_gt_or_return<T, F>(&mut self,
                                                   sep: Option<token::Token>,
                                                   mut f: F)
-                                                  -> PResult<(P<[T]>, bool)> where
-        F: FnMut(&mut Parser) -> PResult<Option<T>>,
+                                                  -> PResult<'a, (P<[T]>, bool)>
+        where F: FnMut(&mut Parser<'a>) -> PResult<'a, Option<T>>,
     {
         let mut v = Vec::new();
         // This loop works by alternating back and forth between parsing types
@@ -788,8 +787,8 @@ impl<'a> Parser<'a> {
     pub fn parse_seq_to_before_gt<T, F>(&mut self,
                                         sep: Option<token::Token>,
                                         mut f: F)
-                                        -> PResult<P<[T]>> where
-        F: FnMut(&mut Parser) -> PResult<T>,
+                                        -> PResult<'a, P<[T]>> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
     {
         let (result, returned) = try!(self.parse_seq_to_before_gt_or_return(sep,
                                                     |p| Ok(Some(try!(f(p))))));
@@ -800,8 +799,8 @@ impl<'a> Parser<'a> {
     pub fn parse_seq_to_gt<T, F>(&mut self,
                                  sep: Option<token::Token>,
                                  f: F)
-                                 -> PResult<P<[T]>> where
-        F: FnMut(&mut Parser) -> PResult<T>,
+                                 -> PResult<'a, P<[T]>> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
     {
         let v = try!(self.parse_seq_to_before_gt(sep, f));
         try!(self.expect_gt());
@@ -811,8 +810,8 @@ impl<'a> Parser<'a> {
     pub fn parse_seq_to_gt_or_return<T, F>(&mut self,
                                            sep: Option<token::Token>,
                                            f: F)
-                                           -> PResult<(P<[T]>, bool)> where
-        F: FnMut(&mut Parser) -> PResult<Option<T>>,
+                                           -> PResult<'a, (P<[T]>, bool)> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a, Option<T>>,
     {
         let (v, returned) = try!(self.parse_seq_to_before_gt_or_return(sep, f));
         if !returned {
@@ -828,8 +827,8 @@ impl<'a> Parser<'a> {
                                   ket: &token::Token,
                                   sep: SeqSep,
                                   f: F)
-                                  -> PResult<Vec<T>> where
-        F: FnMut(&mut Parser) -> PResult<T>,
+                                  -> PResult<'a, Vec<T>> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
     {
         let val = try!(self.parse_seq_to_before_end(ket, sep, f));
         try!(self.bump());
@@ -843,8 +842,8 @@ impl<'a> Parser<'a> {
                                          ket: &token::Token,
                                          sep: SeqSep,
                                          mut f: F)
-                                         -> PResult<Vec<T>> where
-        F: FnMut(&mut Parser) -> PResult<T>,
+                                         -> PResult<'a, Vec<T>> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
     {
         let mut first: bool = true;
         let mut v = vec!();
@@ -870,8 +869,8 @@ impl<'a> Parser<'a> {
                                      ket: &token::Token,
                                      sep: SeqSep,
                                      f: F)
-                                     -> PResult<Vec<T>> where
-        F: FnMut(&mut Parser) -> PResult<T>,
+                                     -> PResult<'a, Vec<T>> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
     {
         try!(self.expect(bra));
         let result = try!(self.parse_seq_to_before_end(ket, sep, f));
@@ -886,8 +885,8 @@ impl<'a> Parser<'a> {
                                         ket: &token::Token,
                                         sep: SeqSep,
                                         f: F)
-                                        -> PResult<Vec<T>> where
-        F: FnMut(&mut Parser) -> PResult<T>,
+                                        -> PResult<'a, Vec<T>> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
     {
         let result = try!(self.parse_unspanned_seq(bra, ket, sep, f));
         if result.is_empty() {
@@ -905,8 +904,8 @@ impl<'a> Parser<'a> {
                            ket: &token::Token,
                            sep: SeqSep,
                            f: F)
-                           -> PResult<Spanned<Vec<T>>> where
-        F: FnMut(&mut Parser) -> PResult<T>,
+                           -> PResult<'a, Spanned<Vec<T>>> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a,  T>,
     {
         let lo = self.span.lo;
         try!(self.expect(bra));
@@ -917,7 +916,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Advance the parser by one token
-    pub fn bump(&mut self) -> PResult<()> {
+    pub fn bump(&mut self) -> PResult<'a,  ()> {
         self.last_span = self.span;
         // Stash token for error recovery (sometimes; clone is not necessarily cheap).
         self.last_token = if self.token.is_ident() ||
@@ -950,7 +949,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Advance the parser by one token and return the bumped token.
-    pub fn bump_and_get(&mut self) -> PResult<token::Token> {
+    pub fn bump_and_get(&mut self) -> PResult<'a, token::Token> {
         let old_token = mem::replace(&mut self.token, token::Underscore);
         try!(self.bump());
         Ok(old_token)
@@ -981,28 +980,16 @@ impl<'a> Parser<'a> {
         }
         f(&self.buffer[((self.buffer_start + dist - 1) & 3) as usize].tok)
     }
-    pub fn fatal(&self, m: &str) -> errors::FatalError {
-        self.sess.span_diagnostic.span_fatal(self.span, m)
+    pub fn fatal(&self, m: &str) -> DiagnosticBuilder<'a> {
+        self.sess.span_diagnostic.struct_span_fatal(self.span, m)
     }
-    pub fn span_fatal(&self, sp: Span, m: &str) -> errors::FatalError {
-        self.sess.span_diagnostic.span_fatal(sp, m)
+    pub fn span_fatal(&self, sp: Span, m: &str) -> DiagnosticBuilder<'a> {
+        self.sess.span_diagnostic.struct_span_fatal(sp, m)
     }
-    pub fn span_fatal_help(&self, sp: Span, m: &str, help: &str) -> errors::FatalError {
-        self.span_err(sp, m);
-        self.fileline_help(sp, help);
-        errors::FatalError
-    }
-    pub fn span_note(&self, sp: Span, m: &str) {
-        self.sess.span_diagnostic.span_note(sp, m)
-    }
-    pub fn span_help(&self, sp: Span, m: &str) {
-        self.sess.span_diagnostic.span_help(sp, m)
-    }
-    pub fn span_suggestion(&self, sp: Span, m: &str, n: String) {
-        self.sess.span_diagnostic.span_suggestion(sp, m, n)
-    }
-    pub fn fileline_help(&self, sp: Span, m: &str) {
-        self.sess.span_diagnostic.fileline_help(sp, m)
+    pub fn span_fatal_help(&self, sp: Span, m: &str, help: &str) -> DiagnosticBuilder<'a> {
+        let mut err = self.sess.span_diagnostic.struct_span_fatal(sp, m);
+        err.fileline_help(sp, help);
+        err
     }
     pub fn bug(&self, m: &str) -> ! {
         self.sess.span_diagnostic.span_bug(self.span, m)
@@ -1023,6 +1010,10 @@ impl<'a> Parser<'a> {
         self.sess.span_diagnostic.abort_if_errors();
     }
 
+    pub fn diagnostic(&self) -> &'a errors::Handler {
+        &self.sess.span_diagnostic
+    }
+
     pub fn id_to_interned_str(&mut self, id: Ident) -> InternedString {
         id.name.as_str()
     }
@@ -1042,7 +1033,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn parse_for_in_type(&mut self) -> PResult<Ty_> {
+    pub fn parse_for_in_type(&mut self) -> PResult<'a, Ty_> {
         /*
         Parses whatever can come after a `for` keyword in a type.
         The `for` has already been consumed.
@@ -1085,12 +1076,12 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn parse_ty_path(&mut self) -> PResult<Ty_> {
+    pub fn parse_ty_path(&mut self) -> PResult<'a, Ty_> {
         Ok(TyPath(None, try!(self.parse_path(LifetimeAndTypesWithoutColons))))
     }
 
     /// parse a TyBareFn type:
-    pub fn parse_ty_bare_fn(&mut self, lifetime_defs: Vec<ast::LifetimeDef>) -> PResult<Ty_> {
+    pub fn parse_ty_bare_fn(&mut self, lifetime_defs: Vec<ast::LifetimeDef>) -> PResult<'a, Ty_> {
         /*
 
         [unsafe] [extern "ABI"] fn <'lt> (S) -> T
@@ -1127,7 +1118,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parses an obsolete closure kind (`&:`, `&mut:`, or `:`).
-    pub fn parse_obsolete_closure_kind(&mut self) -> PResult<()> {
+    pub fn parse_obsolete_closure_kind(&mut self) -> PResult<'a, ()> {
          let lo = self.span.lo;
         if
             self.check(&token::BinOp(token::And)) &&
@@ -1156,7 +1147,7 @@ impl<'a> Parser<'a> {
         Ok(())
     }
 
-    pub fn parse_unsafety(&mut self) -> PResult<Unsafety> {
+    pub fn parse_unsafety(&mut self) -> PResult<'a, Unsafety> {
         if try!(self.eat_keyword(keywords::Unsafe)) {
             return Ok(Unsafety::Unsafe);
         } else {
@@ -1165,12 +1156,12 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse the items in a trait declaration
-    pub fn parse_trait_items(&mut self) -> PResult<Vec<P<TraitItem>>> {
+    pub fn parse_trait_items(&mut self) -> PResult<'a,  Vec<P<TraitItem>>> {
         self.parse_unspanned_seq(
             &token::OpenDelim(token::Brace),
             &token::CloseDelim(token::Brace),
             seq_sep_none(),
-            |p| -> PResult<P<TraitItem>> {
+            |p| -> PResult<'a, P<TraitItem>> {
             maybe_whole!(no_clone p, NtTraitItem);
             let mut attrs = try!(p.parse_outer_attributes());
             let lo = p.span.lo;
@@ -1200,7 +1191,7 @@ impl<'a> Parser<'a> {
                 let ident = try!(p.parse_ident());
                 let mut generics = try!(p.parse_generics());
 
-                let (explicit_self, d) = try!(p.parse_fn_decl_with_self(|p|{
+                let (explicit_self, d) = try!(p.parse_fn_decl_with_self(|p: &mut Parser<'a>|{
                     // This is somewhat dubious; We don't want to allow
                     // argument names to be left off if there is a
                     // definition...
@@ -1251,14 +1242,14 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a possibly mutable type
-    pub fn parse_mt(&mut self) -> PResult<MutTy> {
+    pub fn parse_mt(&mut self) -> PResult<'a, MutTy> {
         let mutbl = try!(self.parse_mutability());
         let t = try!(self.parse_ty());
         Ok(MutTy { ty: t, mutbl: mutbl })
     }
 
     /// Parse optional return type [ -> TY ] in function decl
-    pub fn parse_ret_ty(&mut self) -> PResult<FunctionRetTy> {
+    pub fn parse_ret_ty(&mut self) -> PResult<'a, FunctionRetTy> {
         if try!(self.eat(&token::RArrow) ){
             if try!(self.eat(&token::Not) ){
                 Ok(NoReturn(self.last_span))
@@ -1272,7 +1263,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a type in a context where `T1+T2` is allowed.
-    pub fn parse_ty_sum(&mut self) -> PResult<P<Ty>> {
+    pub fn parse_ty_sum(&mut self) -> PResult<'a, P<Ty>> {
         let lo = self.span.lo;
         let lhs = try!(self.parse_ty());
 
@@ -1297,7 +1288,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a type.
-    pub fn parse_ty(&mut self) -> PResult<P<Ty>> {
+    pub fn parse_ty(&mut self) -> PResult<'a, P<Ty>> {
         maybe_whole!(no_clone self, NtTy);
 
         let lo = self.span.lo;
@@ -1397,7 +1388,7 @@ impl<'a> Parser<'a> {
         Ok(P(Ty {id: ast::DUMMY_NODE_ID, node: t, span: sp}))
     }
 
-    pub fn parse_borrowed_pointee(&mut self) -> PResult<Ty_> {
+    pub fn parse_borrowed_pointee(&mut self) -> PResult<'a, Ty_> {
         // look for `&'lt` or `&'foo ` and interpret `foo` as the region name:
         let opt_lifetime = try!(self.parse_opt_lifetime());
 
@@ -1405,7 +1396,7 @@ impl<'a> Parser<'a> {
         return Ok(TyRptr(opt_lifetime, mt));
     }
 
-    pub fn parse_ptr(&mut self) -> PResult<MutTy> {
+    pub fn parse_ptr(&mut self) -> PResult<'a, MutTy> {
         let mutbl = if try!(self.eat_keyword(keywords::Mut) ){
             MutMutable
         } else if try!(self.eat_keyword(keywords::Const) ){
@@ -1443,7 +1434,7 @@ impl<'a> Parser<'a> {
 
     /// This version of parse arg doesn't necessarily require
     /// identifier names.
-    pub fn parse_arg_general(&mut self, require_name: bool) -> PResult<Arg> {
+    pub fn parse_arg_general(&mut self, require_name: bool) -> PResult<'a, Arg> {
         maybe_whole!(no_clone self, NtArg);
 
         let pat = if require_name || self.is_named_argument() {
@@ -1470,12 +1461,12 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a single function argument
-    pub fn parse_arg(&mut self) -> PResult<Arg> {
+    pub fn parse_arg(&mut self) -> PResult<'a, Arg> {
         self.parse_arg_general(true)
     }
 
     /// Parse an argument in a lambda header e.g. |arg, arg|
-    pub fn parse_fn_block_arg(&mut self) -> PResult<Arg> {
+    pub fn parse_fn_block_arg(&mut self) -> PResult<'a, Arg> {
         let pat = try!(self.parse_pat());
         let t = if try!(self.eat(&token::Colon) ){
             try!(self.parse_ty_sum())
@@ -1493,7 +1484,7 @@ impl<'a> Parser<'a> {
         })
     }
 
-    pub fn maybe_parse_fixed_length_of_vec(&mut self) -> PResult<Option<P<ast::Expr>>> {
+    pub fn maybe_parse_fixed_length_of_vec(&mut self) -> PResult<'a, Option<P<ast::Expr>>> {
         if self.check(&token::Semi) {
             try!(self.bump());
             Ok(Some(try!(self.parse_expr())))
@@ -1503,7 +1494,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Matches token_lit = LIT_INTEGER | ...
-    pub fn lit_from_token(&self, tok: &token::Token) -> PResult<Lit_> {
+    pub fn lit_from_token(&self, tok: &token::Token) -> PResult<'a, Lit_> {
         match *tok {
             token::Interpolated(token::NtExpr(ref v)) => {
                 match v.node {
@@ -1562,7 +1553,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Matches lit = true | false | token_lit
-    pub fn parse_lit(&mut self) -> PResult<Lit> {
+    pub fn parse_lit(&mut self) -> PResult<'a, Lit> {
         let lo = self.span.lo;
         let lit = if try!(self.eat_keyword(keywords::True) ){
             LitBool(true)
@@ -1577,7 +1568,7 @@ impl<'a> Parser<'a> {
     }
 
     /// matches '-' lit | lit
-    pub fn parse_pat_literal_maybe_minus(&mut self) -> PResult<P<Expr>> {
+    pub fn parse_pat_literal_maybe_minus(&mut self) -> PResult<'a, P<Expr>> {
         let minus_lo = self.span.lo;
         let minus_present = try!(self.eat(&token::BinOp(token::Minus)));
         let lo = self.span.lo;
@@ -1610,7 +1601,7 @@ impl<'a> Parser<'a> {
     /// `<T as U>::a`
     /// `<T as U>::F::a::<S>`
     pub fn parse_qualified_path(&mut self, mode: PathParsingMode)
-                                -> PResult<(QSelf, ast::Path)> {
+                                -> PResult<'a, (QSelf, ast::Path)> {
         let span = self.last_span;
         let self_type = try!(self.parse_ty_sum());
         let mut path = if try!(self.eat_keyword(keywords::As)) {
@@ -1653,7 +1644,7 @@ impl<'a> Parser<'a> {
     /// mode. The `mode` parameter determines whether lifetimes, types, and/or
     /// bounds are permitted and whether `::` must precede type parameter
     /// groups.
-    pub fn parse_path(&mut self, mode: PathParsingMode) -> PResult<ast::Path> {
+    pub fn parse_path(&mut self, mode: PathParsingMode) -> PResult<'a, ast::Path> {
         // Check for a whole path...
         let found = match self.token {
             token::Interpolated(token::NtPath(_)) => Some(try!(self.bump_and_get())),
@@ -1696,7 +1687,7 @@ impl<'a> Parser<'a> {
     /// - `a::b<T,U>::c<V,W>`
     /// - `a::b<T,U>::c(V) -> W`
     /// - `a::b<T,U>::c(V)`
-    pub fn parse_path_segments_without_colons(&mut self) -> PResult<Vec<ast::PathSegment>> {
+    pub fn parse_path_segments_without_colons(&mut self) -> PResult<'a, Vec<ast::PathSegment>> {
         let mut segments = Vec::new();
         loop {
             // First, parse an identifier.
@@ -1749,7 +1740,7 @@ impl<'a> Parser<'a> {
 
     /// Examples:
     /// - `a::b::<T,U>::c`
-    pub fn parse_path_segments_with_colons(&mut self) -> PResult<Vec<ast::PathSegment>> {
+    pub fn parse_path_segments_with_colons(&mut self) -> PResult<'a, Vec<ast::PathSegment>> {
         let mut segments = Vec::new();
         loop {
             // First, parse an identifier.
@@ -1794,7 +1785,7 @@ impl<'a> Parser<'a> {
 
     /// Examples:
     /// - `a::b::c`
-    pub fn parse_path_segments_without_types(&mut self) -> PResult<Vec<ast::PathSegment>> {
+    pub fn parse_path_segments_without_types(&mut self) -> PResult<'a, Vec<ast::PathSegment>> {
         let mut segments = Vec::new();
         loop {
             // First, parse an identifier.
@@ -1814,7 +1805,7 @@ impl<'a> Parser<'a> {
     }
 
     /// parses 0 or 1 lifetime
-    pub fn parse_opt_lifetime(&mut self) -> PResult<Option<ast::Lifetime>> {
+    pub fn parse_opt_lifetime(&mut self) -> PResult<'a, Option<ast::Lifetime>> {
         match self.token {
             token::Lifetime(..) => {
                 Ok(Some(try!(self.parse_lifetime())))
@@ -1827,7 +1818,7 @@ impl<'a> Parser<'a> {
 
     /// Parses a single lifetime
     /// Matches lifetime = LIFETIME
-    pub fn parse_lifetime(&mut self) -> PResult<ast::Lifetime> {
+    pub fn parse_lifetime(&mut self) -> PResult<'a, ast::Lifetime> {
         match self.token {
             token::Lifetime(i) => {
                 let span = self.span;
@@ -1846,7 +1837,7 @@ impl<'a> Parser<'a> {
 
     /// Parses `lifetime_defs = [ lifetime_defs { ',' lifetime_defs } ]` where `lifetime_def  =
     /// lifetime [':' lifetimes]`
-    pub fn parse_lifetime_defs(&mut self) -> PResult<Vec<ast::LifetimeDef>> {
+    pub fn parse_lifetime_defs(&mut self) -> PResult<'a, Vec<ast::LifetimeDef>> {
 
         let mut res = Vec::new();
         loop {
@@ -1889,7 +1880,7 @@ impl<'a> Parser<'a> {
     /// Parses zero or more comma separated lifetimes. Expects each lifetime to be followed by
     /// either a comma or `>`.  Used when parsing type parameter lists, where we expect something
     /// like `<'a, 'b, T>`.
-    pub fn parse_lifetimes(&mut self, sep: token::Token) -> PResult<Vec<ast::Lifetime>> {
+    pub fn parse_lifetimes(&mut self, sep: token::Token) -> PResult<'a, Vec<ast::Lifetime>> {
 
         let mut res = Vec::new();
         loop {
@@ -1911,7 +1902,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse mutability declaration (mut/const/imm)
-    pub fn parse_mutability(&mut self) -> PResult<Mutability> {
+    pub fn parse_mutability(&mut self) -> PResult<'a, Mutability> {
         if try!(self.eat_keyword(keywords::Mut) ){
             Ok(MutMutable)
         } else {
@@ -1920,7 +1911,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse ident COLON expr
-    pub fn parse_field(&mut self) -> PResult<Field> {
+    pub fn parse_field(&mut self) -> PResult<'a, Field> {
         let lo = self.span.lo;
         let i = try!(self.parse_ident());
         let hi = self.last_span.hi;
@@ -2012,7 +2003,7 @@ impl<'a> Parser<'a> {
         })
     }
 
-    fn expect_open_delim(&mut self) -> PResult<token::DelimToken> {
+    fn expect_open_delim(&mut self) -> PResult<'a, token::DelimToken> {
         self.expected_tokens.push(TokenType::Token(token::Gt));
         match self.token {
             token::OpenDelim(delim) => {
@@ -2030,7 +2021,7 @@ impl<'a> Parser<'a> {
     /// NB: This does not parse outer attributes,
     ///     and is private because it only works
     ///     correctly if called from parse_dot_or_call_expr().
-    fn parse_bottom_expr(&mut self) -> PResult<P<Expr>> {
+    fn parse_bottom_expr(&mut self) -> PResult<'a, P<Expr>> {
         maybe_whole_expr!(self);
 
         // Outer attributes are already parsed and will be
@@ -2294,7 +2285,7 @@ impl<'a> Parser<'a> {
 
     fn parse_or_use_outer_attributes(&mut self,
                                      already_parsed_attrs: Option<ThinAttributes>)
-                                     -> PResult<ThinAttributes> {
+                                     -> PResult<'a, ThinAttributes> {
         if let Some(attrs) = already_parsed_attrs {
             Ok(attrs)
         } else {
@@ -2305,7 +2296,7 @@ impl<'a> Parser<'a> {
     /// Parse a block or unsafe block
     pub fn parse_block_expr(&mut self, lo: BytePos, blk_mode: BlockCheckMode,
                             attrs: ThinAttributes)
-                            -> PResult<P<Expr>> {
+                            -> PResult<'a, P<Expr>> {
 
         let outer_attrs = attrs;
         try!(self.expect(&token::OpenDelim(token::Brace)));
@@ -2320,7 +2311,7 @@ impl<'a> Parser<'a> {
     /// parse a.b or a(13) or a[4] or just a
     pub fn parse_dot_or_call_expr(&mut self,
                                   already_parsed_attrs: Option<ThinAttributes>)
-                                  -> PResult<P<Expr>> {
+                                  -> PResult<'a, P<Expr>> {
         let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs));
 
         let b = try!(self.parse_bottom_expr());
@@ -2330,7 +2321,7 @@ impl<'a> Parser<'a> {
     pub fn parse_dot_or_call_expr_with(&mut self,
                                        e0: P<Expr>,
                                        attrs: ThinAttributes)
-                                       -> PResult<P<Expr>> {
+                                       -> PResult<'a, P<Expr>> {
         // Stitch the list of outer attributes onto the return value.
         // A little bit ugly, but the best way given the current code
         // structure
@@ -2356,7 +2347,7 @@ impl<'a> Parser<'a> {
         )
     }
 
-    fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>) -> PResult<P<Expr>> {
+    fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>) -> PResult<'a, P<Expr>> {
         let mut e = e0;
         let lo = e.span.lo;
         let mut hi;
@@ -2437,18 +2428,19 @@ impl<'a> Parser<'a> {
                     try!(self.bump());
                     let last_span = self.last_span;
                     let fstr = n.as_str();
-                    self.span_err(last_span,
-                                  &format!("unexpected token: `{}`", n.as_str()));
+                    let mut err = self.diagnostic().struct_span_err(last_span,
+                        &format!("unexpected token: `{}`", n.as_str()));
                     if fstr.chars().all(|x| "0123456789.".contains(x)) {
                         let float = match fstr.parse::<f64>().ok() {
                             Some(f) => f,
                             None => continue,
                         };
-                        self.fileline_help(last_span,
+                        err.fileline_help(last_span,
                             &format!("try parenthesizing the first index; e.g., `(foo.{}){}`",
                                     float.trunc() as usize,
                                     format!(".{}", fstr.splitn(2, ".").last().unwrap())));
                     }
+                    err.emit();
                     self.abort_if_errors();
 
                   }
@@ -2489,7 +2481,7 @@ impl<'a> Parser<'a> {
     }
 
     // Parse unquoted tokens after a `$` in a token tree
-    fn parse_unquoted(&mut self) -> PResult<TokenTree> {
+    fn parse_unquoted(&mut self) -> PResult<'a, TokenTree> {
         let mut sp = self.span;
         let (name, namep) = match self.token {
             token::Dollar => {
@@ -2541,7 +2533,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn check_unknown_macro_variable(&mut self) -> PResult<()> {
+    pub fn check_unknown_macro_variable(&mut self) -> PResult<'a, ()> {
         if self.quote_depth == 0 {
             match self.token {
                 token::SubstNt(name, _) =>
@@ -2555,8 +2547,9 @@ impl<'a> Parser<'a> {
 
     /// Parse an optional separator followed by a Kleene-style
     /// repetition token (+ or *).
-    pub fn parse_sep_and_kleene_op(&mut self) -> PResult<(Option<token::Token>, ast::KleeneOp)> {
-        fn parse_kleene_op(parser: &mut Parser) -> PResult<Option<ast::KleeneOp>> {
+    pub fn parse_sep_and_kleene_op(&mut self)
+                                   -> PResult<'a, (Option<token::Token>, ast::KleeneOp)> {
+        fn parse_kleene_op<'a>(parser: &mut Parser<'a>) -> PResult<'a,  Option<ast::KleeneOp>> {
             match parser.token {
                 token::BinOp(token::Star) => {
                     try!(parser.bump());
@@ -2583,7 +2576,7 @@ impl<'a> Parser<'a> {
     }
 
     /// parse a single token tree from the input.
-    pub fn parse_token_tree(&mut self) -> PResult<TokenTree> {
+    pub fn parse_token_tree(&mut self) -> PResult<'a, TokenTree> {
         // FIXME #6994: currently, this is too eager. It
         // parses token trees but also identifies TokenType::Sequence's
         // and token::SubstNt's; it's too early to know yet
@@ -2596,20 +2589,20 @@ impl<'a> Parser<'a> {
         // not an EOF, and not the desired right-delimiter (if
         // it were, parse_seq_to_before_end would have prevented
         // reaching this point.
-        fn parse_non_delim_tt_tok(p: &mut Parser) -> PResult<TokenTree> {
+        fn parse_non_delim_tt_tok<'b>(p: &mut Parser<'b>) -> PResult<'b,  TokenTree> {
             maybe_whole!(deref p, NtTT);
             match p.token {
                 token::CloseDelim(_) => {
+                    let token_str = p.this_token_to_string();
+                    let mut err = p.fatal(
+                        &format!("incorrect close delimiter: `{}`", token_str));
                     // This is a conservative error: only report the last unclosed delimiter. The
                     // previous unclosed delimiters could actually be closed! The parser just hasn't
                     // gotten to them yet.
-                    match p.open_braces.last() {
-                        None => {}
-                        Some(&sp) => p.span_note(sp, "unclosed delimiter"),
+                    if let Some(&sp) = p.open_braces.last() {
+                        err.span_note(sp, "unclosed delimiter");
                     };
-                    let token_str = p.this_token_to_string();
-                    Err(p.fatal(&format!("incorrect close delimiter: `{}`",
-                                    token_str)))
+                    Err(err)
                 },
                 /* we ought to allow different depths of unquotation */
                 token::Dollar | token::SubstNt(..) if p.quote_depth > 0 => {
@@ -2624,12 +2617,12 @@ impl<'a> Parser<'a> {
         match self.token {
             token::Eof => {
                 let open_braces = self.open_braces.clone();
+                let mut err: DiagnosticBuilder<'a> =
+                    self.fatal("this file contains an un-closed delimiter");
                 for sp in &open_braces {
-                    self.span_help(*sp, "did you mean to close this delimiter?");
+                    err.span_help(*sp, "did you mean to close this delimiter?");
                 }
-                // There shouldn't really be a span, but it's easier for the test runner
-                // if we give it one
-                return Err(self.fatal("this file contains an un-closed delimiter "));
+                return Err(err);
             },
             token::OpenDelim(delim) => {
                 // The span for beginning of the delimited section
@@ -2668,7 +2661,7 @@ impl<'a> Parser<'a> {
 
     // parse a stream of tokens into a list of TokenTree's,
     // up to EOF.
-    pub fn parse_all_token_trees(&mut self) -> PResult<Vec<TokenTree>> {
+    pub fn parse_all_token_trees(&mut self) -> PResult<'a, Vec<TokenTree>> {
         let mut tts = Vec::new();
         while self.token != token::Eof {
             tts.push(try!(self.parse_token_tree()));
@@ -2679,7 +2672,7 @@ impl<'a> Parser<'a> {
     /// Parse a prefix-unary-operator expr
     pub fn parse_prefix_expr(&mut self,
                              already_parsed_attrs: Option<ThinAttributes>)
-                             -> PResult<P<Expr>> {
+                             -> PResult<'a, P<Expr>> {
         let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs));
         let lo = self.span.lo;
         let hi;
@@ -2740,7 +2733,7 @@ impl<'a> Parser<'a> {
     /// the expression.
     pub fn parse_assoc_expr(&mut self,
                             already_parsed_attrs: Option<ThinAttributes>)
-                            -> PResult<P<Expr>> {
+                            -> PResult<'a, P<Expr>> {
         self.parse_assoc_expr_with(0, already_parsed_attrs.into())
     }
 
@@ -2748,7 +2741,7 @@ impl<'a> Parser<'a> {
     pub fn parse_assoc_expr_with(&mut self,
                                  min_prec: usize,
                                  lhs: LhsExpr)
-                                 -> PResult<P<Expr>> {
+                                 -> PResult<'a, P<Expr>> {
         let mut lhs = if let LhsExpr::AlreadyParsed(expr) = lhs {
             expr
         } else {
@@ -2879,12 +2872,13 @@ impl<'a> Parser<'a> {
             ExprBinary(op, _, _) if op.node.is_comparison() => {
                 // respan to include both operators
                 let op_span = mk_sp(op.span.lo, self.span.hi);
-                self.span_err(op_span,
+                let mut err = self.diagnostic().struct_span_err(op_span,
                     "chained comparison operators require parentheses");
                 if op.node == BiLt && *outer_op == AssocOp::Greater {
-                    self.fileline_help(op_span,
+                    err.fileline_help(op_span,
                         "use `::<...>` instead of `<...>` if you meant to specify type arguments");
                 }
+                err.emit();
             }
             _ => {}
         }
@@ -2893,7 +2887,7 @@ impl<'a> Parser<'a> {
     /// Parse prefix-forms of range notation: `..expr` and `..`
     fn parse_prefix_range_expr(&mut self,
                                already_parsed_attrs: Option<ThinAttributes>)
-                               -> PResult<P<Expr>> {
+                               -> PResult<'a, P<Expr>> {
         debug_assert!(self.token == token::DotDot);
         let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs));
         let lo = self.span.lo;
@@ -2928,7 +2922,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse an 'if' or 'if let' expression ('if' token already eaten)
-    pub fn parse_if_expr(&mut self, attrs: ThinAttributes) -> PResult<P<Expr>> {
+    pub fn parse_if_expr(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
         if self.check_keyword(keywords::Let) {
             return self.parse_if_let_expr(attrs);
         }
@@ -2947,7 +2941,7 @@ impl<'a> Parser<'a> {
 
     /// Parse an 'if let' expression ('if' token already eaten)
     pub fn parse_if_let_expr(&mut self, attrs: ThinAttributes)
-                             -> PResult<P<Expr>> {
+                             -> PResult<'a, P<Expr>> {
         let lo = self.last_span.lo;
         try!(self.expect_keyword(keywords::Let));
         let pat = try!(self.parse_pat());
@@ -2967,7 +2961,7 @@ impl<'a> Parser<'a> {
     pub fn parse_lambda_expr(&mut self, lo: BytePos,
                              capture_clause: CaptureClause,
                              attrs: ThinAttributes)
-                             -> PResult<P<Expr>>
+                             -> PResult<'a, P<Expr>>
     {
         let decl = try!(self.parse_fn_block_decl());
         let body = match decl.output {
@@ -2997,7 +2991,7 @@ impl<'a> Parser<'a> {
     }
 
     // `else` token already eaten
-    pub fn parse_else_expr(&mut self) -> PResult<P<Expr>> {
+    pub fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> {
         if try!(self.eat_keyword(keywords::If) ){
             return self.parse_if_expr(None);
         } else {
@@ -3009,7 +3003,7 @@ impl<'a> Parser<'a> {
     /// Parse a 'for' .. 'in' expression ('for' token already eaten)
     pub fn parse_for_expr(&mut self, opt_ident: Option<ast::Ident>,
                           span_lo: BytePos,
-                          attrs: ThinAttributes) -> PResult<P<Expr>> {
+                          attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
         // Parse: `for <src_pat> in <src_expr> <src_loop_block>`
 
         let pat = try!(self.parse_pat());
@@ -3028,7 +3022,7 @@ impl<'a> Parser<'a> {
     /// Parse a 'while' or 'while let' expression ('while' token already eaten)
     pub fn parse_while_expr(&mut self, opt_ident: Option<ast::Ident>,
                             span_lo: BytePos,
-                            attrs: ThinAttributes) -> PResult<P<Expr>> {
+                            attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
         if self.token.is_keyword(keywords::Let) {
             return self.parse_while_let_expr(opt_ident, span_lo, attrs);
         }
@@ -3043,7 +3037,7 @@ impl<'a> Parser<'a> {
     /// Parse a 'while let' expression ('while' token already eaten)
     pub fn parse_while_let_expr(&mut self, opt_ident: Option<ast::Ident>,
                                 span_lo: BytePos,
-                                attrs: ThinAttributes) -> PResult<P<Expr>> {
+                                attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
         try!(self.expect_keyword(keywords::Let));
         let pat = try!(self.parse_pat());
         try!(self.expect(&token::Eq));
@@ -3057,7 +3051,7 @@ impl<'a> Parser<'a> {
     // parse `loop {...}`, `loop` token already eaten
     pub fn parse_loop_expr(&mut self, opt_ident: Option<ast::Ident>,
                            span_lo: BytePos,
-                           attrs: ThinAttributes) -> PResult<P<Expr>> {
+                           attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
         let (iattrs, body) = try!(self.parse_inner_attrs_and_block());
         let attrs = attrs.append(iattrs.into_thin_attrs());
         let hi = body.span.hi;
@@ -3065,14 +3059,15 @@ impl<'a> Parser<'a> {
     }
 
     // `match` token already eaten
-    fn parse_match_expr(&mut self, attrs: ThinAttributes) -> PResult<P<Expr>> {
+    fn parse_match_expr(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Expr>> {
         let match_span = self.last_span;
         let lo = self.last_span.lo;
         let discriminant = try!(self.parse_expr_res(
             Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None));
-        if let Err(e) = self.commit_expr_expecting(&*discriminant, token::OpenDelim(token::Brace)) {
+        if let Err(mut e) = self.commit_expr_expecting(&*discriminant,
+                                                       token::OpenDelim(token::Brace)) {
             if self.token == token::Token::Semi {
-                self.span_note(match_span, "did you mean to remove this `match` keyword?");
+                e.span_note(match_span, "did you mean to remove this `match` keyword?");
             }
             return Err(e)
         }
@@ -3087,7 +3082,7 @@ impl<'a> Parser<'a> {
         return Ok(self.mk_expr(lo, hi, ExprMatch(discriminant, arms), attrs));
     }
 
-    pub fn parse_arm(&mut self) -> PResult<Arm> {
+    pub fn parse_arm(&mut self) -> PResult<'a, Arm> {
         maybe_whole!(no_clone self, NtArm);
 
         let attrs = try!(self.parse_outer_attributes());
@@ -3118,15 +3113,16 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse an expression
-    pub fn parse_expr(&mut self) -> PResult<P<Expr>> {
+    pub fn parse_expr(&mut self) -> PResult<'a, P<Expr>> {
         self.parse_expr_res(Restrictions::empty(), None)
     }
 
     /// Evaluate the closure with restrictions in place.
     ///
     /// After the closure is evaluated, restrictions are reset.
-    pub fn with_res<F>(&mut self, r: Restrictions, f: F) -> PResult<P<Expr>>
-    where F: FnOnce(&mut Self) -> PResult<P<Expr>> {
+    pub fn with_res<F>(&mut self, r: Restrictions, f: F) -> PResult<'a, P<Expr>>
+        where F: FnOnce(&mut Self) -> PResult<'a,  P<Expr>>
+    {
         let old = self.restrictions;
         self.restrictions = r;
         let r = f(self);
@@ -3138,12 +3134,12 @@ impl<'a> Parser<'a> {
     /// Parse an expression, subject to the given restrictions
     pub fn parse_expr_res(&mut self, r: Restrictions,
                           already_parsed_attrs: Option<ThinAttributes>)
-                          -> PResult<P<Expr>> {
+                          -> PResult<'a, P<Expr>> {
         self.with_res(r, |this| this.parse_assoc_expr(already_parsed_attrs))
     }
 
     /// Parse the RHS of a local variable declaration (e.g. '= 14;')
-    fn parse_initializer(&mut self) -> PResult<Option<P<Expr>>> {
+    fn parse_initializer(&mut self) -> PResult<'a, Option<P<Expr>>> {
         if self.check(&token::Eq) {
             try!(self.bump());
             Ok(Some(try!(self.parse_expr())))
@@ -3153,7 +3149,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse patterns, separated by '|' s
-    fn parse_pats(&mut self) -> PResult<Vec<P<Pat>>> {
+    fn parse_pats(&mut self) -> PResult<'a, Vec<P<Pat>>> {
         let mut pats = Vec::new();
         loop {
             pats.push(try!(self.parse_pat()));
@@ -3162,7 +3158,7 @@ impl<'a> Parser<'a> {
         };
     }
 
-    fn parse_pat_tuple_elements(&mut self) -> PResult<Vec<P<Pat>>> {
+    fn parse_pat_tuple_elements(&mut self) -> PResult<'a, Vec<P<Pat>>> {
         let mut fields = vec![];
         if !self.check(&token::CloseDelim(token::Paren)) {
             fields.push(try!(self.parse_pat()));
@@ -3181,7 +3177,7 @@ impl<'a> Parser<'a> {
 
     fn parse_pat_vec_elements(
         &mut self,
-    ) -> PResult<(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>)> {
+    ) -> PResult<'a, (Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>)> {
         let mut before = Vec::new();
         let mut slice = None;
         let mut after = Vec::new();
@@ -3233,7 +3229,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse the fields of a struct-like pattern
-    fn parse_pat_fields(&mut self) -> PResult<(Vec<codemap::Spanned<ast::FieldPat>> , bool)> {
+    fn parse_pat_fields(&mut self) -> PResult<'a, (Vec<codemap::Spanned<ast::FieldPat>> , bool)> {
         let mut fields = Vec::new();
         let mut etc = false;
         let mut first = true;
@@ -3310,7 +3306,7 @@ impl<'a> Parser<'a> {
         return Ok((fields, etc));
     }
 
-    fn parse_pat_range_end(&mut self) -> PResult<P<Expr>> {
+    fn parse_pat_range_end(&mut self) -> PResult<'a, P<Expr>> {
         if self.is_path_start() {
             let lo = self.span.lo;
             let (qself, path) = if try!(self.eat_lt()) {
@@ -3336,7 +3332,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a pattern.
-    pub fn parse_pat(&mut self) -> PResult<P<Pat>> {
+    pub fn parse_pat(&mut self) -> PResult<'a, P<Pat>> {
         maybe_whole!(self, NtPat);
 
         let lo = self.span.lo;
@@ -3496,7 +3492,7 @@ impl<'a> Parser<'a> {
     /// error message when parsing mistakes like ref foo(a,b)
     fn parse_pat_ident(&mut self,
                        binding_mode: ast::BindingMode)
-                       -> PResult<ast::Pat_> {
+                       -> PResult<'a, ast::Pat_> {
         if !self.token.is_plain_ident() {
             let span = self.span;
             let tok_str = self.this_token_to_string();
@@ -3529,7 +3525,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a local variable declaration
-    fn parse_local(&mut self, attrs: ThinAttributes) -> PResult<P<Local>> {
+    fn parse_local(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Local>> {
         let lo = self.span.lo;
         let pat = try!(self.parse_pat());
 
@@ -3549,7 +3545,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a "let" stmt
-    fn parse_let(&mut self, attrs: ThinAttributes) -> PResult<P<Decl>> {
+    fn parse_let(&mut self, attrs: ThinAttributes) -> PResult<'a, P<Decl>> {
         let lo = self.span.lo;
         let local = try!(self.parse_local(attrs));
         Ok(P(spanned(lo, self.last_span.hi, DeclLocal(local))))
@@ -3557,7 +3553,7 @@ impl<'a> Parser<'a> {
 
     /// Parse a structure field
     fn parse_name_and_ty(&mut self, pr: Visibility,
-                         attrs: Vec<Attribute> ) -> PResult<StructField> {
+                         attrs: Vec<Attribute> ) -> PResult<'a, StructField> {
         let lo = match pr {
             Inherited => self.span.lo,
             Public => self.last_span.lo,
@@ -3589,11 +3585,11 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a statement. may include decl.
-    pub fn parse_stmt(&mut self) -> PResult<Option<P<Stmt>>> {
+    pub fn parse_stmt(&mut self) -> PResult<'a, Option<P<Stmt>>> {
         Ok(try!(self.parse_stmt_()).map(P))
     }
 
-    fn parse_stmt_(&mut self) -> PResult<Option<Stmt>> {
+    fn parse_stmt_(&mut self) -> PResult<'a, Option<Stmt>> {
         maybe_whole!(Some deref self, NtStmt);
 
         let attrs = try!(self.parse_outer_attributes());
@@ -3729,7 +3725,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a block. No inner attrs are allowed.
-    pub fn parse_block(&mut self) -> PResult<P<Block>> {
+    pub fn parse_block(&mut self) -> PResult<'a, P<Block>> {
         maybe_whole!(no_clone self, NtBlock);
 
         let lo = self.span.lo;
@@ -3746,7 +3742,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a block. Inner attrs are allowed.
-    fn parse_inner_attrs_and_block(&mut self) -> PResult<(Vec<Attribute>, P<Block>)> {
+    fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (Vec<Attribute>, P<Block>)> {
         maybe_whole!(pair_empty self, NtBlock);
 
         let lo = self.span.lo;
@@ -3757,7 +3753,7 @@ impl<'a> Parser<'a> {
 
     /// Parse the rest of a block expression or function body
     /// Precondition: already parsed the '{'.
-    fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult<P<Block>> {
+    fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult<'a, P<Block>> {
         let mut stmts = vec![];
         let mut expr = None;
 
@@ -3851,7 +3847,7 @@ impl<'a> Parser<'a> {
             e: P<Expr>,
             span: Span,
             stmts: &mut Vec<P<Stmt>>,
-            last_block_expr: &mut Option<P<Expr>>) -> PResult<()> {
+            last_block_expr: &mut Option<P<Expr>>) -> PResult<'a, ()> {
         // expression without semicolon
         if classify::expr_requires_semi_to_be_stmt(&*e) {
             // Just check for errors and recover; do not eat semicolon yet.
@@ -3887,7 +3883,7 @@ impl<'a> Parser<'a> {
     // otherwise returns empty list.
     fn parse_colon_then_ty_param_bounds(&mut self,
                                         mode: BoundParsingMode)
-                                        -> PResult<TyParamBounds>
+                                        -> PResult<'a, TyParamBounds>
     {
         if !try!(self.eat(&token::Colon) ){
             Ok(P::empty())
@@ -3902,7 +3898,7 @@ impl<'a> Parser<'a> {
     // and     bound     = 'region | trait_ref
     fn parse_ty_param_bounds(&mut self,
                              mode: BoundParsingMode)
-                             -> PResult<TyParamBounds>
+                             -> PResult<'a, TyParamBounds>
     {
         let mut result = vec!();
         loop {
@@ -3948,7 +3944,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Matches typaram = IDENT (`?` unbound)? optbounds ( EQ ty )?
-    fn parse_ty_param(&mut self) -> PResult<TyParam> {
+    fn parse_ty_param(&mut self) -> PResult<'a, TyParam> {
         let span = self.span;
         let ident = try!(self.parse_ident());
 
@@ -3977,7 +3973,7 @@ impl<'a> Parser<'a> {
     /// matches generics = ( ) | ( < > ) | ( < typaramseq ( , )? > ) | ( < lifetimes ( , )? > )
     ///                  | ( < lifetimes , typaramseq ( , )? > )
     /// where   typaramseq = ( typaram ) | ( typaram , typaramseq )
-    pub fn parse_generics(&mut self) -> PResult<ast::Generics> {
+    pub fn parse_generics(&mut self) -> PResult<'a, ast::Generics> {
         maybe_whole!(self, NtGenerics);
 
         if try!(self.eat(&token::Lt) ){
@@ -4008,7 +4004,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn parse_generic_values_after_lt(&mut self) -> PResult<(Vec<ast::Lifetime>,
+    fn parse_generic_values_after_lt(&mut self) -> PResult<'a, (Vec<ast::Lifetime>,
                                                             Vec<P<Ty>>,
                                                             Vec<P<TypeBinding>>)> {
         let span_lo = self.span.lo;
@@ -4025,7 +4021,7 @@ impl<'a> Parser<'a> {
             let msg = format!("expected `,` or `>` after lifetime \
                               name, found `{}`",
                               self.this_token_to_string());
-            self.span_err(self.span, &msg);
+            let mut err = self.diagnostic().struct_span_err(self.span, &msg);
 
             let span_hi = self.span.hi;
             let span_hi = if self.parse_ty().is_ok() {
@@ -4037,7 +4033,8 @@ impl<'a> Parser<'a> {
             let msg = format!("did you mean a single argument type &'a Type, \
                               or did you mean the comma-separated arguments \
                               'a, Type?");
-            self.span_note(mk_sp(span_lo, span_hi), &msg);
+            err.span_note(mk_sp(span_lo, span_hi), &msg);
+            err.emit();
 
             self.abort_if_errors()
         }
@@ -4085,7 +4082,7 @@ impl<'a> Parser<'a> {
         Ok((lifetimes, types.into_vec(), bindings.into_vec()))
     }
 
-    fn forbid_lifetime(&mut self) -> PResult<()> {
+    fn forbid_lifetime(&mut self) -> PResult<'a, ()> {
         if self.token.is_lifetime() {
             let span = self.span;
             return Err(self.span_fatal(span, "lifetime parameters must be declared \
@@ -4099,7 +4096,7 @@ impl<'a> Parser<'a> {
     /// ```ignore
     /// where T : Trait<U, V> + 'b, 'a : 'b
     /// ```
-    pub fn parse_where_clause(&mut self) -> PResult<ast::WhereClause> {
+    pub fn parse_where_clause(&mut self) -> PResult<'a, ast::WhereClause> {
         maybe_whole!(self, NtWhereClause);
 
         let mut where_clause = WhereClause {
@@ -4215,7 +4212,7 @@ impl<'a> Parser<'a> {
     }
 
     fn parse_fn_args(&mut self, named_args: bool, allow_variadic: bool)
-                     -> PResult<(Vec<Arg> , bool)> {
+                     -> PResult<'a, (Vec<Arg> , bool)> {
         let sp = self.span;
         let mut args: Vec<Option<Arg>> =
             try!(self.parse_unspanned_seq(
@@ -4264,7 +4261,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse the argument list and result type of a function declaration
-    pub fn parse_fn_decl(&mut self, allow_variadic: bool) -> PResult<P<FnDecl>> {
+    pub fn parse_fn_decl(&mut self, allow_variadic: bool) -> PResult<'a, P<FnDecl>> {
 
         let (args, variadic) = try!(self.parse_fn_args(true, allow_variadic));
         let ret_ty = try!(self.parse_ret_ty());
@@ -4283,7 +4280,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn expect_self_ident(&mut self) -> PResult<ast::Ident> {
+    fn expect_self_ident(&mut self) -> PResult<'a, ast::Ident> {
         match self.token {
             token::Ident(id, token::Plain) if id.name == special_idents::self_.name => {
                 try!(self.bump());
@@ -4304,7 +4301,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn expect_self_type_ident(&mut self) -> PResult<ast::Ident> {
+    fn expect_self_type_ident(&mut self) -> PResult<'a, ast::Ident> {
         match self.token {
             token::Ident(id, token::Plain) if id.name == special_idents::type_self.name => {
                 try!(self.bump());
@@ -4321,11 +4318,11 @@ impl<'a> Parser<'a> {
     /// Parse the argument list and result type of a function
     /// that may have a self type.
     fn parse_fn_decl_with_self<F>(&mut self,
-                                  parse_arg_fn: F) -> PResult<(ExplicitSelf, P<FnDecl>)> where
-        F: FnMut(&mut Parser) -> PResult<Arg>,
+                                  parse_arg_fn: F) -> PResult<'a, (ExplicitSelf, P<FnDecl>)> where
+        F: FnMut(&mut Parser<'a>) -> PResult<'a,  Arg>,
     {
-        fn maybe_parse_borrowed_explicit_self(this: &mut Parser)
-                                              -> PResult<ast::ExplicitSelf_> {
+        fn maybe_parse_borrowed_explicit_self<'b>(this: &mut Parser<'b>)
+                                                  -> PResult<'b,  ast::ExplicitSelf_> {
             // The following things are possible to see here:
             //
             //     fn(&mut self)
@@ -4483,7 +4480,7 @@ impl<'a> Parser<'a> {
     }
 
     // parse the |arg, arg| header on a lambda
-    fn parse_fn_block_decl(&mut self) -> PResult<P<FnDecl>> {
+    fn parse_fn_block_decl(&mut self) -> PResult<'a, P<FnDecl>> {
         let inputs_captures = {
             if try!(self.eat(&token::OrOr) ){
                 Vec::new()
@@ -4509,7 +4506,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse the name and optional generic types of a function header.
-    fn parse_fn_header(&mut self) -> PResult<(Ident, ast::Generics)> {
+    fn parse_fn_header(&mut self) -> PResult<'a, (Ident, ast::Generics)> {
         let id = try!(self.parse_ident());
         let generics = try!(self.parse_generics());
         Ok((id, generics))
@@ -4533,7 +4530,7 @@ impl<'a> Parser<'a> {
                      unsafety: Unsafety,
                      constness: Constness,
                      abi: abi::Abi)
-                     -> PResult<ItemInfo> {
+                     -> PResult<'a, ItemInfo> {
         let (ident, mut generics) = try!(self.parse_fn_header());
         let decl = try!(self.parse_fn_decl(false));
         generics.where_clause = try!(self.parse_where_clause());
@@ -4556,7 +4553,8 @@ impl<'a> Parser<'a> {
     /// - `const unsafe fn`
     /// - `extern fn`
     /// - etc
-    pub fn parse_fn_front_matter(&mut self) -> PResult<(ast::Constness, ast::Unsafety, abi::Abi)> {
+    pub fn parse_fn_front_matter(&mut self)
+                                 -> PResult<'a, (ast::Constness, ast::Unsafety, abi::Abi)> {
         let is_const_fn = try!(self.eat_keyword(keywords::Const));
         let unsafety = try!(self.parse_unsafety());
         let (constness, unsafety, abi) = if is_const_fn {
@@ -4574,7 +4572,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse an impl item.
-    pub fn parse_impl_item(&mut self) -> PResult<P<ImplItem>> {
+    pub fn parse_impl_item(&mut self) -> PResult<'a, P<ImplItem>> {
         maybe_whole!(no_clone self, NtImplItem);
 
         let mut attrs = try!(self.parse_outer_attributes());
@@ -4614,9 +4612,10 @@ impl<'a> Parser<'a> {
     fn complain_if_pub_macro(&mut self, visa: Visibility, span: Span) {
         match visa {
             Public => {
-                self.span_err(span, "can't qualify macro invocation with `pub`");
-                self.fileline_help(span, "try adjusting the macro to put `pub` inside \
-                                      the invocation");
+                self.diagnostic().struct_span_err(span, "can't qualify macro invocation with `pub`")
+                                 .fileline_help(span, "try adjusting the macro to put `pub` inside \
+                                                       the invocation")
+                                 .emit();
             }
             Inherited => (),
         }
@@ -4624,7 +4623,7 @@ impl<'a> Parser<'a> {
 
     /// Parse a method or a macro invocation in a trait impl.
     fn parse_impl_method(&mut self, vis: Visibility)
-                         -> PResult<(Ident, Vec<ast::Attribute>, ast::ImplItemKind)> {
+                         -> PResult<'a, (Ident, Vec<ast::Attribute>, ast::ImplItemKind)> {
         // code copied from parse_macro_use_or_failure... abstraction!
         if !self.token.is_any_keyword()
             && self.look_ahead(1, |t| *t == token::Not)
@@ -4673,7 +4672,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse trait Foo { ... }
-    fn parse_item_trait(&mut self, unsafety: Unsafety) -> PResult<ItemInfo> {
+    fn parse_item_trait(&mut self, unsafety: Unsafety) -> PResult<'a, ItemInfo> {
 
         let ident = try!(self.parse_ident());
         let mut tps = try!(self.parse_generics());
@@ -4691,7 +4690,7 @@ impl<'a> Parser<'a> {
     ///    impl<T> Foo { ... }
     ///    impl<T> ToString for &'static T { ... }
     ///    impl Send for .. {}
-    fn parse_item_impl(&mut self, unsafety: ast::Unsafety) -> PResult<ItemInfo> {
+    fn parse_item_impl(&mut self, unsafety: ast::Unsafety) -> PResult<'a, ItemInfo> {
         let impl_span = self.span;
 
         // First, parse type parameters if necessary.
@@ -4769,14 +4768,14 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a::B<String,i32>
-    fn parse_trait_ref(&mut self) -> PResult<TraitRef> {
+    fn parse_trait_ref(&mut self) -> PResult<'a, TraitRef> {
         Ok(ast::TraitRef {
             path: try!(self.parse_path(LifetimeAndTypesWithoutColons)),
             ref_id: ast::DUMMY_NODE_ID,
         })
     }
 
-    fn parse_late_bound_lifetime_defs(&mut self) -> PResult<Vec<ast::LifetimeDef>> {
+    fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec<ast::LifetimeDef>> {
         if try!(self.eat_keyword(keywords::For) ){
             try!(self.expect(&token::Lt));
             let lifetime_defs = try!(self.parse_lifetime_defs());
@@ -4788,7 +4787,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse for<'l> a::B<String,i32>
-    fn parse_poly_trait_ref(&mut self) -> PResult<PolyTraitRef> {
+    fn parse_poly_trait_ref(&mut self) -> PResult<'a, PolyTraitRef> {
         let lo = self.span.lo;
         let lifetime_defs = try!(self.parse_late_bound_lifetime_defs());
 
@@ -4800,7 +4799,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse struct Foo { ... }
-    fn parse_item_struct(&mut self) -> PResult<ItemInfo> {
+    fn parse_item_struct(&mut self) -> PResult<'a, ItemInfo> {
         let class_name = try!(self.parse_ident());
         let mut generics = try!(self.parse_generics());
 
@@ -4851,7 +4850,9 @@ impl<'a> Parser<'a> {
         Ok((class_name, ItemStruct(vdata, generics), None))
     }
 
-    pub fn parse_record_struct_body(&mut self, parse_pub: ParsePub) -> PResult<Vec<StructField>> {
+    pub fn parse_record_struct_body(&mut self,
+                                    parse_pub: ParsePub)
+                                    -> PResult<'a, Vec<StructField>> {
         let mut fields = Vec::new();
         if try!(self.eat(&token::OpenDelim(token::Brace)) ){
             while self.token != token::CloseDelim(token::Brace) {
@@ -4869,7 +4870,9 @@ impl<'a> Parser<'a> {
         Ok(fields)
     }
 
-    pub fn parse_tuple_struct_body(&mut self, parse_pub: ParsePub) -> PResult<Vec<StructField>> {
+    pub fn parse_tuple_struct_body(&mut self,
+                                   parse_pub: ParsePub)
+                                   -> PResult<'a, Vec<StructField>> {
         // This is the case where we find `struct Foo<T>(T) where T: Copy;`
         // Unit like structs are handled in parse_item_struct function
         let fields = try!(self.parse_unspanned_seq(
@@ -4901,7 +4904,7 @@ impl<'a> Parser<'a> {
     pub fn parse_single_struct_field(&mut self,
                                      vis: Visibility,
                                      attrs: Vec<Attribute> )
-                                     -> PResult<StructField> {
+                                     -> PResult<'a, StructField> {
         let a_var = try!(self.parse_name_and_ty(vis, attrs));
         match self.token {
             token::Comma => {
@@ -4921,7 +4924,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse an element of a struct definition
-    fn parse_struct_decl_field(&mut self, parse_pub: ParsePub) -> PResult<StructField> {
+    fn parse_struct_decl_field(&mut self, parse_pub: ParsePub) -> PResult<'a, StructField> {
 
         let attrs = try!(self.parse_outer_attributes());
 
@@ -4937,13 +4940,13 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse visibility: PUB or nothing
-    fn parse_visibility(&mut self) -> PResult<Visibility> {
+    fn parse_visibility(&mut self) -> PResult<'a, Visibility> {
         if try!(self.eat_keyword(keywords::Pub)) { Ok(Public) }
         else { Ok(Inherited) }
     }
 
     /// Given a termination token, parse all of the items in a module
-    fn parse_mod_items(&mut self, term: &token::Token, inner_lo: BytePos) -> PResult<Mod> {
+    fn parse_mod_items(&mut self, term: &token::Token, inner_lo: BytePos) -> PResult<'a, Mod> {
         let mut items = vec![];
         while let Some(item) = try!(self.parse_item()) {
             items.push(item);
@@ -4966,7 +4969,7 @@ impl<'a> Parser<'a> {
         })
     }
 
-    fn parse_item_const(&mut self, m: Option<Mutability>) -> PResult<ItemInfo> {
+    fn parse_item_const(&mut self, m: Option<Mutability>) -> PResult<'a, ItemInfo> {
         let id = try!(self.parse_ident());
         try!(self.expect(&token::Colon));
         let ty = try!(self.parse_ty_sum());
@@ -4981,7 +4984,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a `mod <foo> { ... }` or `mod <foo>;` item
-    fn parse_item_mod(&mut self, outer_attrs: &[Attribute]) -> PResult<ItemInfo> {
+    fn parse_item_mod(&mut self, outer_attrs: &[Attribute]) -> PResult<'a, ItemInfo> {
         let id_span = self.span;
         let id = try!(self.parse_ident());
         if self.check(&token::Semi) {
@@ -5060,7 +5063,7 @@ impl<'a> Parser<'a> {
     fn submod_path(&mut self,
                    id: ast::Ident,
                    outer_attrs: &[ast::Attribute],
-                   id_sp: Span) -> PResult<ModulePathSuccess> {
+                   id_sp: Span) -> PResult<'a, ModulePathSuccess> {
         let mut prefix = PathBuf::from(&self.sess.codemap().span_to_filename(self.span));
         prefix.pop();
         let mut dir_path = prefix;
@@ -5075,21 +5078,23 @@ impl<'a> Parser<'a> {
         let paths = Parser::default_submod_path(id, &dir_path, self.sess.codemap());
 
         if !self.owns_directory {
-            self.span_err(id_sp, "cannot declare a new module at this location");
+            let mut err = self.diagnostic().struct_span_err(id_sp,
+                "cannot declare a new module at this location");
             let this_module = match self.mod_path_stack.last() {
                 Some(name) => name.to_string(),
                 None => self.root_module_name.as_ref().unwrap().clone(),
             };
-            self.span_note(id_sp,
-                           &format!("maybe move this module `{0}` to its own directory \
+            err.span_note(id_sp,
+                          &format!("maybe move this module `{0}` to its own directory \
                                      via `{0}/mod.rs`",
                                     this_module));
             if paths.path_exists {
-                self.span_note(id_sp,
-                               &format!("... or maybe `use` the module `{}` instead \
-                                         of possibly redeclaring it",
-                                        paths.name));
+                err.span_note(id_sp,
+                              &format!("... or maybe `use` the module `{}` instead \
+                                        of possibly redeclaring it",
+                                       paths.name));
             }
+            err.emit();
             self.abort_if_errors();
         }
 
@@ -5104,7 +5109,7 @@ impl<'a> Parser<'a> {
                     id: ast::Ident,
                     outer_attrs: &[ast::Attribute],
                     id_sp: Span)
-                    -> PResult<(ast::Item_, Vec<ast::Attribute> )> {
+                    -> PResult<'a, (ast::Item_, Vec<ast::Attribute> )> {
         let ModulePathSuccess { path, owns_directory } = try!(self.submod_path(id,
                                                                                outer_attrs,
                                                                                id_sp));
@@ -5119,7 +5124,7 @@ impl<'a> Parser<'a> {
                               path: PathBuf,
                               owns_directory: bool,
                               name: String,
-                              id_sp: Span) -> PResult<(ast::Item_, Vec<ast::Attribute> )> {
+                              id_sp: Span) -> PResult<'a, (ast::Item_, Vec<ast::Attribute> )> {
         let mut included_mod_stack = self.sess.included_mod_stack.borrow_mut();
         match included_mod_stack.iter().position(|p| *p == path) {
             Some(i) => {
@@ -5152,7 +5157,7 @@ impl<'a> Parser<'a> {
 
     /// Parse a function declaration from a foreign module
     fn parse_item_foreign_fn(&mut self, vis: ast::Visibility, lo: BytePos,
-                             attrs: Vec<Attribute>) -> PResult<P<ForeignItem>> {
+                             attrs: Vec<Attribute>) -> PResult<'a, P<ForeignItem>> {
         try!(self.expect_keyword(keywords::Fn));
 
         let (ident, mut generics) = try!(self.parse_fn_header());
@@ -5172,7 +5177,7 @@ impl<'a> Parser<'a> {
 
     /// Parse a static item from a foreign module
     fn parse_item_foreign_static(&mut self, vis: ast::Visibility, lo: BytePos,
-                                 attrs: Vec<Attribute>) -> PResult<P<ForeignItem>> {
+                                 attrs: Vec<Attribute>) -> PResult<'a, P<ForeignItem>> {
         try!(self.expect_keyword(keywords::Static));
         let mutbl = try!(self.eat_keyword(keywords::Mut));
 
@@ -5201,7 +5206,7 @@ impl<'a> Parser<'a> {
                                lo: BytePos,
                                visibility: Visibility,
                                attrs: Vec<Attribute>)
-                                -> PResult<P<Item>> {
+                                -> PResult<'a, P<Item>> {
 
         let crate_name = try!(self.parse_ident());
         let (maybe_path, ident) = if let Some(ident) = try!(self.parse_rename()) {
@@ -5242,7 +5247,7 @@ impl<'a> Parser<'a> {
                               opt_abi: Option<abi::Abi>,
                               visibility: Visibility,
                               mut attrs: Vec<Attribute>)
-                              -> PResult<P<Item>> {
+                              -> PResult<'a, P<Item>> {
         try!(self.expect(&token::OpenDelim(token::Brace)));
 
         let abi = opt_abi.unwrap_or(abi::C);
@@ -5269,7 +5274,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse type Foo = Bar;
-    fn parse_item_type(&mut self) -> PResult<ItemInfo> {
+    fn parse_item_type(&mut self) -> PResult<'a, ItemInfo> {
         let ident = try!(self.parse_ident());
         let mut tps = try!(self.parse_generics());
         tps.where_clause = try!(self.parse_where_clause());
@@ -5280,7 +5285,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse the part of an "enum" decl following the '{'
-    fn parse_enum_def(&mut self, _generics: &ast::Generics) -> PResult<EnumDef> {
+    fn parse_enum_def(&mut self, _generics: &ast::Generics) -> PResult<'a, EnumDef> {
         let mut variants = Vec::new();
         let mut all_nullary = true;
         let mut any_disr = None;
@@ -5330,7 +5335,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse an "enum" declaration
-    fn parse_item_enum(&mut self) -> PResult<ItemInfo> {
+    fn parse_item_enum(&mut self) -> PResult<'a, ItemInfo> {
         let id = try!(self.parse_ident());
         let mut generics = try!(self.parse_generics());
         generics.where_clause = try!(self.parse_where_clause());
@@ -5342,7 +5347,7 @@ impl<'a> Parser<'a> {
 
     /// Parses a string as an ABI spec on an extern type or module. Consumes
     /// the `extern` keyword, if one is found.
-    fn parse_opt_abi(&mut self) -> PResult<Option<abi::Abi>> {
+    fn parse_opt_abi(&mut self) -> PResult<'a, Option<abi::Abi>> {
         match self.token {
             token::Literal(token::Str_(s), suf) | token::Literal(token::StrRaw(s, _), suf) => {
                 let sp = self.span;
@@ -5371,7 +5376,7 @@ impl<'a> Parser<'a> {
     /// NB: this function no longer parses the items inside an
     /// extern crate.
     fn parse_item_(&mut self, attrs: Vec<Attribute>,
-                   macros_allowed: bool, attributes_allowed: bool) -> PResult<Option<P<Item>>> {
+                   macros_allowed: bool, attributes_allowed: bool) -> PResult<'a, Option<P<Item>>> {
         let nt_item = match self.token {
             token::Interpolated(token::NtItem(ref item)) => {
                 Some((**item).clone())
@@ -5474,8 +5479,9 @@ impl<'a> Parser<'a> {
             // CONST ITEM
             if try!(self.eat_keyword(keywords::Mut) ){
                 let last_span = self.last_span;
-                self.span_err(last_span, "const globals cannot be mutable");
-                self.fileline_help(last_span, "did you mean to declare a static?");
+                self.diagnostic().struct_span_err(last_span, "const globals cannot be mutable")
+                                 .fileline_help(last_span, "did you mean to declare a static?")
+                                 .emit();
             }
             let (ident, item_, extra_attrs) = try!(self.parse_item_const(None));
             let last_span = self.last_span;
@@ -5633,7 +5639,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a foreign item.
-    fn parse_foreign_item(&mut self) -> PResult<Option<P<ForeignItem>>> {
+    fn parse_foreign_item(&mut self) -> PResult<'a, Option<P<ForeignItem>>> {
         let attrs = try!(self.parse_outer_attributes());
         let lo = self.span.lo;
         let visibility = try!(self.parse_visibility());
@@ -5664,7 +5670,7 @@ impl<'a> Parser<'a> {
         attributes_allowed: bool,
         lo: BytePos,
         visibility: Visibility
-    ) -> PResult<Option<P<Item>>> {
+    ) -> PResult<'a, Option<P<Item>>> {
         if macros_allowed && !self.token.is_any_keyword()
                 && self.look_ahead(1, |t| *t == token::Not)
                 && (self.look_ahead(2, |t| t.is_plain_ident())
@@ -5736,7 +5742,7 @@ impl<'a> Parser<'a> {
         Ok(None)
     }
 
-    pub fn parse_item(&mut self) -> PResult<Option<P<Item>>> {
+    pub fn parse_item(&mut self) -> PResult<'a, Option<P<Item>>> {
         let attrs = try!(self.parse_outer_attributes());
         self.parse_item_(attrs, true, false)
     }
@@ -5747,7 +5753,7 @@ impl<'a> Parser<'a> {
     /// | MOD? non_global_path MOD_SEP LBRACE ident_seq RBRACE
     /// | MOD? non_global_path MOD_SEP STAR
     /// | MOD? non_global_path
-    fn parse_view_path(&mut self) -> PResult<P<ViewPath>> {
+    fn parse_view_path(&mut self) -> PResult<'a, P<ViewPath>> {
         let lo = self.span.lo;
 
         // Allow a leading :: because the paths are absolute either way.
@@ -5843,7 +5849,7 @@ impl<'a> Parser<'a> {
         Ok(P(spanned(lo, self.last_span.hi, ViewPathSimple(rename_to, path))))
     }
 
-    fn parse_rename(&mut self) -> PResult<Option<Ident>> {
+    fn parse_rename(&mut self) -> PResult<'a, Option<Ident>> {
         if try!(self.eat_keyword(keywords::As)) {
             self.parse_ident().map(Some)
         } else {
@@ -5853,7 +5859,7 @@ impl<'a> Parser<'a> {
 
     /// Parses a source module as a crate. This is the main
     /// entry point for the parser.
-    pub fn parse_crate_mod(&mut self) -> PResult<Crate> {
+    pub fn parse_crate_mod(&mut self) -> PResult<'a, Crate> {
         let lo = self.span.lo;
         Ok(ast::Crate {
             attrs: try!(self.parse_inner_attributes()),
@@ -5865,7 +5871,7 @@ impl<'a> Parser<'a> {
     }
 
     pub fn parse_optional_str(&mut self)
-                              -> PResult<Option<(InternedString,
+                              -> PResult<'a, Option<(InternedString,
                                                  ast::StrStyle,
                                                  Option<ast::Name>)>> {
         let ret = match self.token {
@@ -5881,7 +5887,7 @@ impl<'a> Parser<'a> {
         Ok(Some(ret))
     }
 
-    pub fn parse_str(&mut self) -> PResult<(InternedString, StrStyle)> {
+    pub fn parse_str(&mut self) -> PResult<'a, (InternedString, StrStyle)> {
         match try!(self.parse_optional_str()) {
             Some((s, style, suf)) => {
                 let sp = self.last_span;
diff --git a/src/libsyntax/show_span.rs b/src/libsyntax/show_span.rs
index 014c7b2a68f..5e3cd0773aa 100644
--- a/src/libsyntax/show_span.rs
+++ b/src/libsyntax/show_span.rs
@@ -47,21 +47,21 @@ struct ShowSpanVisitor<'a> {
 impl<'a, 'v> Visitor<'v> for ShowSpanVisitor<'a> {
     fn visit_expr(&mut self, e: &ast::Expr) {
         if let Mode::Expression = self.mode {
-            self.span_diagnostic.span_note(e.span, "expression");
+            self.span_diagnostic.span_warn(e.span, "expression");
         }
         visit::walk_expr(self, e);
     }
 
     fn visit_pat(&mut self, p: &ast::Pat) {
         if let Mode::Pattern = self.mode {
-            self.span_diagnostic.span_note(p.span, "pattern");
+            self.span_diagnostic.span_warn(p.span, "pattern");
         }
         visit::walk_pat(self, p);
     }
 
     fn visit_ty(&mut self, t: &ast::Ty) {
         if let Mode::Type = self.mode {
-            self.span_diagnostic.span_note(t.span, "type");
+            self.span_diagnostic.span_warn(t.span, "type");
         }
         visit::walk_ty(self, t);
     }
diff --git a/src/libsyntax/util/parser_testing.rs b/src/libsyntax/util/parser_testing.rs
index a462dbeb6e4..454b925a494 100644
--- a/src/libsyntax/util/parser_testing.rs
+++ b/src/libsyntax/util/parser_testing.rs
@@ -30,10 +30,9 @@ pub fn string_to_parser<'a>(ps: &'a ParseSess, source_str: String) -> Parser<'a>
                                source_str)
 }
 
-fn with_error_checking_parse<T, F>(s: String, f: F) -> T where
-    F: FnOnce(&mut Parser) -> PResult<T>,
+fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> T where
+    F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
 {
-    let ps = ParseSess::new();
     let mut p = string_to_parser(&ps, s);
     let x = panictry!(f(&mut p));
     p.abort_if_errors();
@@ -42,28 +41,32 @@ fn with_error_checking_parse<T, F>(s: String, f: F) -> T where
 
 /// Parse a string, return a crate.
 pub fn string_to_crate (source_str : String) -> ast::Crate {
-    with_error_checking_parse(source_str, |p| {
+    let ps = ParseSess::new();
+    with_error_checking_parse(source_str, &ps, |p| {
         p.parse_crate_mod()
     })
 }
 
 /// Parse a string, return an expr
 pub fn string_to_expr (source_str : String) -> P<ast::Expr> {
-    with_error_checking_parse(source_str, |p| {
+    let ps = ParseSess::new();
+    with_error_checking_parse(source_str, &ps, |p| {
         p.parse_expr()
     })
 }
 
 /// Parse a string, return an item
 pub fn string_to_item (source_str : String) -> Option<P<ast::Item>> {
-    with_error_checking_parse(source_str, |p| {
+    let ps = ParseSess::new();
+    with_error_checking_parse(source_str, &ps, |p| {
         p.parse_item()
     })
 }
 
 /// Parse a string, return a stmt
 pub fn string_to_stmt(source_str : String) -> Option<P<ast::Stmt>> {
-    with_error_checking_parse(source_str, |p| {
+    let ps = ParseSess::new();
+    with_error_checking_parse(source_str, &ps, |p| {
         p.parse_stmt()
     })
 }
@@ -71,7 +74,8 @@ pub fn string_to_stmt(source_str : String) -> Option<P<ast::Stmt>> {
 /// Parse a string, return a pat. Uses "irrefutable"... which doesn't
 /// (currently) affect parsing.
 pub fn string_to_pat(source_str: String) -> P<ast::Pat> {
-    with_error_checking_parse(source_str, |p| {
+    let ps = ParseSess::new();
+    with_error_checking_parse(source_str, &ps, |p| {
         p.parse_pat()
     })
 }
diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs
index 24094f797e6..78e7f7462f3 100644
--- a/src/libsyntax_ext/format.rs
+++ b/src/libsyntax_ext/format.rs
@@ -130,10 +130,11 @@ fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
             match names.get(name) {
                 None => {}
                 Some(prev) => {
-                    ecx.span_err(e.span,
-                                 &format!("duplicate argument named `{}`",
-                                         name));
-                    ecx.parse_sess.span_diagnostic.span_note(prev.span, "previously here");
+                    ecx.struct_span_err(e.span,
+                                        &format!("duplicate argument named `{}`",
+                                                 name))
+                       .span_note(prev.span, "previously here")
+                       .emit();
                     continue
                 }
             }
diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs
index a032666595e..0f049fa9792 100644
--- a/src/libsyntax_ext/lib.rs
+++ b/src/libsyntax_ext/lib.rs
@@ -24,26 +24,13 @@
 #![feature(str_char)]
 
 extern crate fmt_macros;
+#[macro_use]
 extern crate syntax;
 
 use syntax::ext::base::{MacroExpanderFn, NormalTT};
 use syntax::ext::base::{SyntaxEnv, SyntaxExtension};
 use syntax::parse::token::intern;
 
-// A variant of 'try!' that panics on Err(FatalError). This is used as a
-// crutch on the way towards a non-panic!-prone parser. It should be used
-// for fatal parsing errors; eventually we plan to convert all code using
-// panictry to just use normal try
-macro_rules! panictry {
-    ($e:expr) => ({
-        use std::result::Result::{Ok, Err};
-        use syntax::errors::FatalError;
-        match $e {
-            Ok(e) => e,
-            Err(FatalError) => panic!(FatalError)
-        }
-    })
-}
 
 mod asm;
 mod cfg;
diff --git a/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs b/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs
index 7c1a45d020b..b64e5778d90 100644
--- a/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs
+++ b/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs
@@ -37,34 +37,36 @@ pub fn string_to_parser<'a>(ps: &'a ParseSess, source_str: String) -> Parser<'a>
                                source_str)
 }
 
-fn with_error_checking_parse<T, F>(s: String, f: F) -> PResult<T> where
-    F: FnOnce(&mut Parser) -> PResult<T>,
+fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> PResult<'a, T> where
+    F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
 {
-    let ps = ParseSess::new();
     let mut p = string_to_parser(&ps, s);
     let x = f(&mut p);
 
     if ps.span_diagnostic.has_errors() || p.token != token::Eof {
+        if let Err(mut e) = x {
+            e.cancel();
+        }
         return Err(p.fatal("parse error"));
     }
 
     x
 }
 
-fn expr(s: &str) -> PResult<P<ast::Expr>> {
-    with_error_checking_parse(s.to_string(), |p| {
+fn expr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, P<ast::Expr>> {
+    with_error_checking_parse(s.to_string(), ps, |p| {
         p.parse_expr()
     })
 }
 
-fn stmt(s: &str) -> PResult<P<ast::Stmt>> {
-    with_error_checking_parse(s.to_string(), |p| {
+fn stmt<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, P<ast::Stmt>> {
+    with_error_checking_parse(s.to_string(), ps, |p| {
         p.parse_stmt().map(|s| s.unwrap())
     })
 }
 
-fn attr(s: &str) -> PResult<ast::Attribute> {
-    with_error_checking_parse(s.to_string(), |p| {
+fn attr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, ast::Attribute> {
+    with_error_checking_parse(s.to_string(), ps, |p| {
         p.parse_attribute(true)
     })
 }
@@ -79,29 +81,39 @@ fn str_compare<T, F: Fn(&T) -> String>(e: &str, expected: &[T], actual: &[T], f:
 }
 
 fn check_expr_attrs(es: &str, expected: &[&str]) {
-    let e = expr(es).expect("parse error");
+    let ps = ParseSess::new();
+    let e = expr(es, &ps).expect("parse error");
     let actual = &e.attrs;
     str_compare(es,
-                &expected.iter().map(|r| attr(r).unwrap()).collect::<Vec<_>>(),
+                &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::<Vec<_>>(),
                 actual.as_attr_slice(),
                 pprust::attribute_to_string);
 }
 
 fn check_stmt_attrs(es: &str, expected: &[&str]) {
-    let e = stmt(es).expect("parse error");
+    let ps = ParseSess::new();
+    let e = stmt(es, &ps).expect("parse error");
     let actual = e.node.attrs();
     str_compare(es,
-                &expected.iter().map(|r| attr(r).unwrap()).collect::<Vec<_>>(),
+                &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::<Vec<_>>(),
                 actual,
                 pprust::attribute_to_string);
 }
 
 fn reject_expr_parse(es: &str) {
-    assert!(expr(es).is_err(), "parser did not reject `{}`", es);
+    let ps = ParseSess::new();
+    match expr(es, &ps) {
+        Ok(_) => panic!("parser did not reject `{}`", es),
+        Err(mut e) => e.cancel(),
+    };
 }
 
 fn reject_stmt_parse(es: &str) {
-    assert!(stmt(es).is_err(), "parser did not reject `{}`", es);
+    let ps = ParseSess::new();
+    match stmt(es, &ps) {
+        Ok(_) => panic!("parser did not reject `{}`", es),
+        Err(mut e) => e.cancel(),
+    };
 }
 
 fn main() {