about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/dep_graph/dep_node.rs1
-rw-r--r--src/librustc/ich/impls_ty.rs2
-rw-r--r--src/librustc/lint/context.rs771
-rw-r--r--src/librustc/lint/levels.rs343
-rw-r--r--src/librustc/lint/mod.rs236
-rw-r--r--src/librustc/lint/table.rs71
-rw-r--r--src/librustc/middle/dead.rs52
-rw-r--r--src/librustc/middle/effect.rs10
-rw-r--r--src/librustc/middle/liveness.rs18
-rw-r--r--src/librustc/middle/stability.rs14
-rw-r--r--src/librustc/session/mod.rs29
-rw-r--r--src/librustc/traits/error_reporting.rs27
-rw-r--r--src/librustc/traits/select.rs10
-rw-r--r--src/librustc/ty/context.rs61
-rw-r--r--src/librustc/ty/maps.rs13
-rw-r--r--src/librustc_const_eval/check_match.rs26
-rw-r--r--src/librustc_driver/driver.rs22
-rw-r--r--src/librustc_lint/builtin.rs15
-rw-r--r--src/librustc_passes/ast_validation.rs11
-rw-r--r--src/librustc_passes/consts.rs20
-rw-r--r--src/librustc_privacy/lib.rs20
-rw-r--r--src/librustc_resolve/check_unused.rs13
-rw-r--r--src/librustc_resolve/lib.rs13
-rw-r--r--src/librustc_resolve/macros.rs4
-rw-r--r--src/librustc_resolve/resolve_imports.rs6
-rw-r--r--src/librustc_typeck/astconv.rs6
-rw-r--r--src/librustc_typeck/check/cast.rs24
-rw-r--r--src/librustc_typeck/check/mod.rs8
-rw-r--r--src/librustc_typeck/check/writeback.rs5
-rw-r--r--src/librustc_typeck/check_unused.rs2
-rw-r--r--src/librustc_typeck/collect.rs6
-rw-r--r--src/test/compile-fail-fulldeps/proc-macro/attributes-included.rs1
-rw-r--r--src/test/compile-fail/E0010.rs1
-rw-r--r--src/test/compile-fail/E0394.rs2
-rw-r--r--src/test/compile-fail/bad-lint-cap2.rs1
-rw-r--r--src/test/compile-fail/bad-lint-cap3.rs1
-rw-r--r--src/test/compile-fail/check-static-values-constraints.rs1
-rw-r--r--src/test/compile-fail/const-eval-overflow-2.rs2
-rw-r--r--src/test/compile-fail/const-match-pattern-arm.rs2
-rw-r--r--src/test/compile-fail/feature-gate-dropck-ugeh.rs1
-rw-r--r--src/test/compile-fail/issue-14227.rs2
-rw-r--r--src/test/compile-fail/issue-16538.rs2
-rw-r--r--src/test/compile-fail/issue-17450.rs2
-rw-r--r--src/test/compile-fail/issue-17718-const-naming.rs1
-rw-r--r--src/test/compile-fail/issue-17718-references.rs2
-rw-r--r--src/test/compile-fail/issue-18937.rs2
-rw-r--r--src/test/compile-fail/issue-28075.rs2
-rw-r--r--src/test/compile-fail/issue-28113.rs2
-rw-r--r--src/test/compile-fail/issue-28324.rs2
-rw-r--r--src/test/compile-fail/issue-30730.rs1
-rw-r--r--src/test/compile-fail/issue-37515.rs1
-rw-r--r--src/test/compile-fail/issue-7364.rs1
-rw-r--r--src/test/compile-fail/lint-removed-allow.rs3
-rw-r--r--src/test/compile-fail/lint-removed-cmdline.rs2
-rw-r--r--src/test/compile-fail/lint-renamed-allow.rs3
-rw-r--r--src/test/compile-fail/lint-stability-deprecated.rs222
-rw-r--r--src/test/compile-fail/lint-type-overflow2.rs20
-rw-r--r--src/test/compile-fail/lint-uppercase-variables.rs1
-rw-r--r--src/test/compile-fail/liveness-unused.rs1
-rw-r--r--src/test/compile-fail/never-assign-dead-code.rs14
-rw-r--r--src/test/compile-fail/private-inferred-type.rs1
-rw-r--r--src/test/compile-fail/private-type-in-interface.rs1
-rw-r--r--src/test/compile-fail/static-mut-not-constant.rs3
-rw-r--r--src/test/compile-fail/unreachable-try-pattern.rs15
-rw-r--r--src/test/ui-fulldeps/lint-plugin-cmdline-allow.rs1
-rw-r--r--src/test/ui-fulldeps/lint-plugin-cmdline-allow.stderr11
-rw-r--r--src/test/ui/check_match/issue-43253.rs3
-rw-r--r--src/test/ui/check_match/issue-43253.stderr18
-rw-r--r--src/test/ui/compare-method/proj-outlives-region.stderr2
-rw-r--r--src/test/ui/compare-method/region-unrelated.stderr2
-rw-r--r--src/test/ui/lint/fn_must_use.rs1
-rw-r--r--src/test/ui/lint/fn_must_use.stderr14
-rw-r--r--src/test/ui/lint/outer-forbid.rs8
-rw-r--r--src/test/ui/lint/outer-forbid.stderr24
-rw-r--r--src/test/ui/path-lookahead.rs2
-rw-r--r--src/test/ui/path-lookahead.stderr27
-rw-r--r--src/test/ui/reachable/expr_unary.stderr14
-rw-r--r--src/test/ui/span/issue-24690.rs7
-rw-r--r--src/test/ui/span/issue-24690.stderr49
-rw-r--r--src/test/ui/span/macro-span-replacement.rs2
-rw-r--r--src/test/ui/span/macro-span-replacement.stderr13
-rw-r--r--src/test/ui/span/multispan-import-lint.rs2
-rw-r--r--src/test/ui/span/multispan-import-lint.stderr11
-rw-r--r--src/tools/compiletest/src/runtest.rs18
84 files changed, 1342 insertions, 1064 deletions
diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs
index c6f9cb2fcea..5b609f192e1 100644
--- a/src/librustc/dep_graph/dep_node.rs
+++ b/src/librustc/dep_graph/dep_node.rs
@@ -521,6 +521,7 @@ define_dep_nodes!( <'tcx>
     [] IsAllocator(DefId),
     [] IsPanicRuntime(DefId),
     [] ExternCrate(DefId),
+    [] LintLevels,
 );
 
 trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug {
diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index 5269ec90def..cd64348044f 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -625,8 +625,6 @@ for ty::TypeckTables<'tcx> {
 
             ref cast_kinds,
 
-            // FIXME(#41184): This is still ignored at the moment.
-            lints: _,
             ref used_trait_imports,
             tainted_by_errors,
             ref free_region_map,
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index d67bca1df30..6ee06dc0a81 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -23,29 +23,25 @@
 //! previous lint state is pushed onto a stack and the ast is then recursed
 //! upon.  As the ast is traversed, this keeps track of the current lint level
 //! for all lint attributes.
+
 use self::TargetLint::*;
 
+use rustc_back::slice;
+use lint::{EarlyLintPassObject, LateLintPassObject};
+use lint::{Level, Lint, LintId, LintPass, LintBuffer};
+use lint::levels::{LintLevelSets, LintLevelsBuilder};
 use middle::privacy::AccessLevels;
+use rustc_serialize::{Decoder, Decodable, Encoder, Encodable};
+use session::{config, early_error, Session};
 use traits::Reveal;
 use ty::{self, TyCtxt};
-use session::{config, early_error, Session};
-use lint::{Level, LevelSource, Lint, LintId, LintPass, LintSource};
-use lint::{EarlyLintPassObject, LateLintPassObject};
-use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid};
-use lint::builtin;
-use rustc_serialize::{Decoder, Decodable, Encoder, Encodable};
 use util::nodemap::FxHashMap;
 
-use std::cmp;
 use std::default::Default as StdDefault;
-use std::mem;
-use std::fmt;
 use std::cell::{Ref, RefCell};
-use syntax::attr;
 use syntax::ast;
-use syntax::symbol::Symbol;
 use syntax_pos::{MultiSpan, Span};
-use errors::{self, Diagnostic, DiagnosticBuilder};
+use errors::DiagnosticBuilder;
 use hir;
 use hir::def_id::LOCAL_CRATE;
 use hir::intravisit as hir_visit;
@@ -69,9 +65,6 @@ pub struct LintStore {
     /// Lints indexed by name.
     by_name: FxHashMap<String, TargetLint>,
 
-    /// Current levels of each lint, and where they were set.
-    levels: LintLevels,
-
     /// Map of registered lint groups to what lints they expand to. The bool
     /// is true if the lint group was added by a plugin.
     lint_groups: FxHashMap<&'static str, (Vec<LintId>, bool)>,
@@ -81,78 +74,23 @@ pub struct LintStore {
     future_incompatible: FxHashMap<LintId, FutureIncompatibleInfo>,
 }
 
-
-#[derive(Default)]
-struct LintLevels {
-    /// Current levels of each lint, and where they were set.
-    levels: FxHashMap<LintId, LevelSource>,
-
-    /// Maximum level a lint can be
-    lint_cap: Option<Level>,
-}
-
-
 pub struct LintSession<'a, PassObject> {
     /// Reference to the store of registered lints.
     lints: Ref<'a, LintStore>,
 
-    /// The current lint levels.
-    levels: LintLevels,
-
-    /// When recursing into an attributed node of the ast which modifies lint
-    /// levels, this stack keeps track of the previous lint levels of whatever
-    /// was modified.
-    stack: Vec<(LintId, LevelSource)>,
-
     /// Trait objects for each lint pass.
     passes: Option<Vec<PassObject>>,
 }
 
 
-/// When you call `add_lint` on the session, you wind up storing one
-/// of these, which records a "potential lint" at a particular point.
-#[derive(PartialEq, RustcEncodable, RustcDecodable)]
-pub struct EarlyLint {
-    /// what lint is this? (e.g., `dead_code`)
-    pub id: LintId,
-
-    /// the main message
-    pub diagnostic: Diagnostic,
-}
-
-impl fmt::Debug for EarlyLint {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        f.debug_struct("EarlyLint")
-            .field("id", &self.id)
-            .field("span", &self.diagnostic.span)
-            .field("diagnostic", &self.diagnostic)
-            .finish()
-    }
-}
-
-pub trait IntoEarlyLint {
-    fn into_early_lint(self, id: LintId) -> EarlyLint;
-}
-
-impl<'a, S: Into<MultiSpan>> IntoEarlyLint for (S, &'a str) {
-    fn into_early_lint(self, id: LintId) -> EarlyLint {
-        let (span, msg) = self;
-        let mut diagnostic = Diagnostic::new(errors::Level::Warning, msg);
-        diagnostic.set_span(span);
-        EarlyLint {
-            id,
-            diagnostic,
-        }
-    }
-}
-
-impl IntoEarlyLint for Diagnostic {
-    fn into_early_lint(self, id: LintId) -> EarlyLint {
-        EarlyLint {
-            id,
-            diagnostic: self,
-        }
-    }
+/// Lints that are buffered up early on in the `Session` before the
+/// `LintLevels` is calculated
+#[derive(PartialEq, RustcEncodable, RustcDecodable, Debug)]
+pub struct BufferedEarlyLint {
+    pub lint_id: LintId,
+    pub ast_id: ast::NodeId,
+    pub span: MultiSpan,
+    pub msg: String,
 }
 
 /// Extra information for a future incompatibility lint. See the call
@@ -176,11 +114,20 @@ enum TargetLint {
     Removed(String),
 }
 
-enum FindLintError {
+pub enum FindLintError {
     NotFound,
     Removed,
 }
 
+pub enum CheckLintNameResult<'a> {
+    Ok(&'a [LintId]),
+    // Lint doesn't exist
+    NoLint,
+    // The lint is either renamed or removed. This is the warning
+    // message.
+    Warning(String),
+}
+
 impl LintStore {
     pub fn new() -> LintStore {
         LintStore {
@@ -188,7 +135,6 @@ impl LintStore {
             early_passes: Some(vec![]),
             late_passes: Some(vec![]),
             by_name: FxHashMap(),
-            levels: LintLevels::default(),
             future_incompatible: FxHashMap(),
             lint_groups: FxHashMap(),
         }
@@ -241,8 +187,6 @@ impl LintStore {
                     (Some(sess), true)  => sess.err(&msg[..]),
                 }
             }
-
-            self.levels.set(id, (lint.default_level, Default));
         }
     }
 
@@ -291,96 +235,93 @@ impl LintStore {
         self.by_name.insert(name.into(), Removed(reason.into()));
     }
 
-    fn find_lint(&self, lint_name: &str) -> Result<LintId, FindLintError> {
+    pub fn find_lints(&self, lint_name: &str) -> Result<Vec<LintId>, FindLintError> {
         match self.by_name.get(lint_name) {
-            Some(&Id(lint_id)) => Ok(lint_id),
+            Some(&Id(lint_id)) => Ok(vec![lint_id]),
             Some(&Renamed(_, lint_id)) => {
-                Ok(lint_id)
+                Ok(vec![lint_id])
             },
             Some(&Removed(_)) => {
                 Err(FindLintError::Removed)
             },
-            None => Err(FindLintError::NotFound)
-        }
-    }
-
-    pub fn process_command_line(&mut self, sess: &Session) {
-        for &(ref lint_name, level) in &sess.opts.lint_opts {
-            check_lint_name_cmdline(sess, self,
-                                    &lint_name[..], level);
-
-            let lint_flag_val = Symbol::intern(&lint_name);
-            match self.find_lint(&lint_name[..]) {
-                Ok(lint_id) => self.levels.set(lint_id, (level, CommandLine(lint_flag_val))),
-                Err(FindLintError::Removed) => { }
-                Err(_) => {
-                    match self.lint_groups.iter().map(|(&x, pair)| (x, pair.0.clone()))
-                                                 .collect::<FxHashMap<&'static str,
-                                                                      Vec<LintId>>>()
-                                                 .get(&lint_name[..]) {
-                        Some(v) => {
-                            for lint_id in v {
-                                self.levels.set(*lint_id, (level, CommandLine(lint_flag_val)));
-                            }
-                        }
-                        None => {
-                            // The lint or lint group doesn't exist.
-                            // This is an error, but it was handled
-                            // by check_lint_name_cmdline.
-                        }
-                    }
+            None => {
+                match self.lint_groups.get(lint_name) {
+                    Some(v) => Ok(v.0.clone()),
+                    None => Err(FindLintError::Removed)
                 }
             }
         }
-
-        self.levels.set_lint_cap(sess.opts.lint_cap);
     }
-}
-
 
-impl LintLevels {
-    fn get_source(&self, lint: LintId) -> LevelSource {
-        match self.levels.get(&lint) {
-            Some(&s) => s,
-            None => (Allow, Default),
-        }
-    }
+    // Checks the validity of lint names derived from the command line
+    pub fn check_lint_name_cmdline(&self,
+                                   sess: &Session,
+                                   lint_name: &str,
+                                   level: Level) {
+        let db = match self.check_lint_name(lint_name) {
+            CheckLintNameResult::Ok(_) => None,
+            CheckLintNameResult::Warning(ref msg) => {
+                Some(sess.struct_warn(msg))
+            },
+            CheckLintNameResult::NoLint => {
+                Some(struct_err!(sess, E0602, "unknown lint: `{}`", lint_name))
+            }
+        };
 
-    fn set(&mut self, lint: LintId, mut lvlsrc: LevelSource) {
-        if let Some(cap) = self.lint_cap {
-            lvlsrc.0 = cmp::min(lvlsrc.0, cap);
-        }
-        if lvlsrc.0 == Allow {
-            self.levels.remove(&lint);
-        } else {
-            self.levels.insert(lint, lvlsrc);
+        if let Some(mut db) = db {
+            let msg = format!("requested on the command line with `{} {}`",
+                              match level {
+                                  Level::Allow => "-A",
+                                  Level::Warn => "-W",
+                                  Level::Deny => "-D",
+                                  Level::Forbid => "-F",
+                              },
+                              lint_name);
+            db.note(&msg);
+            db.emit();
         }
     }
 
-    fn set_lint_cap(&mut self, lint_cap: Option<Level>) {
-        self.lint_cap = lint_cap;
-        if let Some(cap) = lint_cap {
-            for (_, level) in &mut self.levels {
-                level.0 = cmp::min(level.0, cap);
+    /// Checks the name of a lint for its existence, and whether it was
+    /// renamed or removed. Generates a DiagnosticBuilder containing a
+    /// warning for renamed and removed lints. This is over both lint
+    /// names from attributes and those passed on the command line. Since
+    /// it emits non-fatal warnings and there are *two* lint passes that
+    /// inspect attributes, this is only run from the late pass to avoid
+    /// printing duplicate warnings.
+    pub fn check_lint_name(&self, lint_name: &str) -> CheckLintNameResult {
+        match self.by_name.get(lint_name) {
+            Some(&Renamed(ref new_name, _)) => {
+                CheckLintNameResult::Warning(
+                    format!("lint {} has been renamed to {}", lint_name, new_name)
+                )
+            },
+            Some(&Removed(ref reason)) => {
+                CheckLintNameResult::Warning(
+                    format!("lint {} has been removed: {}", lint_name, reason)
+                )
+            },
+            None => {
+                match self.lint_groups.get(lint_name) {
+                    None => CheckLintNameResult::NoLint,
+                    Some(ids) => CheckLintNameResult::Ok(&ids.0),
+                }
             }
+            Some(&Id(ref id)) => CheckLintNameResult::Ok(slice::ref_slice(id)),
         }
     }
 }
 
-
 impl<'a, PassObject: LintPassObject> LintSession<'a, PassObject> {
     /// Creates a new `LintSession`, by moving out the `LintStore`'s initial
     /// lint levels and pass objects. These can be restored using the `restore`
     /// method.
     fn new(store: &'a RefCell<LintStore>) -> LintSession<'a, PassObject> {
         let mut s = store.borrow_mut();
-        let levels = mem::replace(&mut s.levels, LintLevels::default());
         let passes = PassObject::take_passes(&mut *s);
         drop(s);
         LintSession {
             lints: store.borrow(),
-            stack: Vec::new(),
-            levels,
             passes,
         }
     }
@@ -389,17 +330,10 @@ impl<'a, PassObject: LintPassObject> LintSession<'a, PassObject> {
     fn restore(self, store: &RefCell<LintStore>) {
         drop(self.lints);
         let mut s = store.borrow_mut();
-        s.levels = self.levels;
         PassObject::restore_passes(&mut *s, self.passes);
     }
-
-    fn get_source(&self, lint_id: LintId) -> LevelSource {
-        self.levels.get_source(lint_id)
-    }
 }
 
-
-
 /// Context for lint checking after type checking.
 pub struct LateContext<'a, 'tcx: 'a> {
     /// Type context we're checking in.
@@ -416,6 +350,8 @@ pub struct LateContext<'a, 'tcx: 'a> {
 
     /// The store of registered lints and the lint levels.
     lint_sess: LintSession<'tcx, LateLintPassObject>,
+
+    last_ast_node_with_lint_attrs: ast::NodeId,
 }
 
 /// Context for lint checking of the AST, after expansion, before lowering to
@@ -427,8 +363,12 @@ pub struct EarlyContext<'a> {
     /// The crate being checked.
     pub krate: &'a ast::Crate,
 
+    builder: LintLevelsBuilder<'a>,
+
     /// The store of registered lints and the lint levels.
     lint_sess: LintSession<'a, EarlyLintPassObject>,
+
+    buffered: LintBuffer,
 }
 
 /// Convenience macro for calling a `LintPass` method on every pass in the context.
@@ -442,135 +382,6 @@ macro_rules! run_lints { ($cx:expr, $f:ident, $ps:ident, $($args:expr),*) => ({
     $cx.lint_sess_mut().passes = Some(passes);
 }) }
 
-/// Parse the lint attributes into a vector, with `Err`s for malformed lint
-/// attributes. Writing this as an iterator is an enormous mess.
-// See also the hir version just below.
-pub fn gather_attrs(attrs: &[ast::Attribute]) -> Vec<Result<(ast::Name, Level, Span), Span>> {
-    let mut out = vec![];
-    for attr in attrs {
-        let r = gather_attr(attr);
-        out.extend(r.into_iter());
-    }
-    out
-}
-
-pub fn gather_attr(attr: &ast::Attribute) -> Vec<Result<(ast::Name, Level, Span), Span>> {
-    let mut out = vec![];
-
-    let level = match attr.name().and_then(|name| Level::from_str(&name.as_str())) {
-        None => return out,
-        Some(lvl) => lvl,
-    };
-
-    let meta = unwrap_or!(attr.meta(), return out);
-    attr::mark_used(attr);
-
-    let metas = if let Some(metas) = meta.meta_item_list() {
-        metas
-    } else {
-        out.push(Err(meta.span));
-        return out;
-    };
-
-    for li in metas {
-        out.push(li.word().map_or(Err(li.span), |word| Ok((word.name(), level, word.span))));
-    }
-
-    out
-}
-
-/// Emit a lint as a warning or an error (or not at all)
-/// according to `level`.
-///
-/// This lives outside of `Context` so it can be used by checks
-/// 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<S: Into<MultiSpan>>(sess: &Session,
-                                         lints: &LintStore,
-                                         lint: &'static Lint,
-                                         lvlsrc: LevelSource,
-                                         span: Option<S>,
-                                         msg: &str) {
-    raw_struct_lint(sess, lints, lint, lvlsrc, span, msg).emit();
-}
-
-pub fn raw_struct_lint<'a, S>(sess: &'a Session,
-                              lints: &LintStore,
-                              lint: &'static Lint,
-                              lvlsrc: LevelSource,
-                              span: Option<S>,
-                              msg: &str)
-                              -> DiagnosticBuilder<'a>
-    where S: Into<MultiSpan>
-{
-    let (level, source) = lvlsrc;
-    if level == Allow {
-        return sess.diagnostic().struct_dummy();
-    }
-
-    let name = lint.name_lower();
-
-    // Except for possible note details, forbid behaves like deny.
-    let effective_level = if level == Forbid { Deny } else { level };
-
-    let mut err = match (effective_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[..]),
-        _ => bug!("impossible level in raw_emit_lint"),
-    };
-
-    match source {
-        Default => {
-            sess.diag_note_once(&mut err, lint,
-                                &format!("#[{}({})] on by default", level.as_str(), name));
-        },
-        CommandLine(lint_flag_val) => {
-            let flag = match level {
-                Warn => "-W", Deny => "-D", Forbid => "-F",
-                Allow => bug!("earlier conditional return should handle Allow case")
-            };
-            let hyphen_case_lint_name = name.replace("_", "-");
-            if lint_flag_val.as_str() == name {
-                sess.diag_note_once(&mut err, lint,
-                                    &format!("requested on the command line with `{} {}`",
-                                             flag, hyphen_case_lint_name));
-            } else {
-                let hyphen_case_flag_val = lint_flag_val.as_str().replace("_", "-");
-                sess.diag_note_once(&mut err, lint,
-                                    &format!("`{} {}` implied by `{} {}`",
-                                             flag, hyphen_case_lint_name, flag,
-                                             hyphen_case_flag_val));
-            }
-        },
-        Node(lint_attr_name, src) => {
-            sess.diag_span_note_once(&mut err, lint, src, "lint level defined here");
-            if lint_attr_name.as_str() != name {
-                let level_str = level.as_str();
-                sess.diag_note_once(&mut err, lint,
-                                    &format!("#[{}({})] implied by #[{}({})]",
-                                             level_str, name, level_str, lint_attr_name));
-            }
-        }
-    }
-
-    // Check for future incompatibility lints and issue a stronger warning.
-    if let Some(future_incompatible) = lints.future_incompatible(LintId::of(lint)) {
-        let explanation = format!("this was previously accepted by the compiler \
-                                   but is being phased out; \
-                                   it will become a hard error in a future release!");
-        let citation = format!("for more information, see {}",
-                               future_incompatible.reference);
-        err.warn(&explanation);
-        err.note(&citation);
-    }
-
-    err
-}
-
-
 pub trait LintPassObject: Sized {
     fn take_passes(store: &mut LintStore) -> Option<Vec<Self>>;
     fn restore_passes(store: &mut LintStore, passes: Option<Vec<Self>>);
@@ -607,67 +418,24 @@ pub trait LintContext<'tcx>: Sized {
     fn enter_attrs(&mut self, attrs: &'tcx [ast::Attribute]);
     fn exit_attrs(&mut self, attrs: &'tcx [ast::Attribute]);
 
-    /// Get the level of `lint` at the current position of the lint
-    /// traversal.
-    fn current_level(&self, lint: &'static Lint) -> Level {
-        self.lint_sess().get_source(LintId::of(lint)).0
-    }
-
-    fn level_src(&self, lint: &'static Lint) -> Option<LevelSource> {
-        let ref levels = self.lint_sess().levels;
-        levels.levels.get(&LintId::of(lint)).map(|ls| match ls {
-            &(Warn, _) => {
-                let lint_id = LintId::of(builtin::WARNINGS);
-                let warn_src = levels.get_source(lint_id);
-                if warn_src.0 != Warn {
-                    warn_src
-                } else {
-                    *ls
-                }
-            }
-            _ => *ls
-        })
-    }
-
     fn lookup_and_emit<S: Into<MultiSpan>>(&self,
                                            lint: &'static Lint,
                                            span: Option<S>,
                                            msg: &str) {
-        let (level, src) = match self.level_src(lint) {
-            None => return,
-            Some(pair) => pair,
-        };
-
-        raw_emit_lint(&self.sess(), self.lints(), lint, (level, src), span, msg);
+        self.lookup(lint, span, msg).emit();
     }
 
     fn lookup<S: Into<MultiSpan>>(&self,
                                   lint: &'static Lint,
                                   span: Option<S>,
                                   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(), self.lints(), lint, (level, src), span, msg)
-    }
+                                  -> DiagnosticBuilder;
 
     /// Emit a lint at the appropriate level, for a particular span.
     fn span_lint<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: S, msg: &str) {
         self.lookup_and_emit(lint, Some(span), msg);
     }
 
-    fn early_lint(&self, early_lint: &EarlyLint) {
-        let span = early_lint.diagnostic.span.primary_span().expect("early lint w/o primary span");
-        let mut err = self.struct_span_lint(early_lint.id.lint,
-                                            span,
-                                            &early_lint.diagnostic.message());
-        err.copy_details_not_message(&early_lint.diagnostic);
-        err.emit();
-    }
-
     fn struct_span_lint<S: Into<MultiSpan>>(&self,
                                             lint: &'static Lint,
                                             span: S,
@@ -680,12 +448,10 @@ pub trait LintContext<'tcx>: Sized {
     fn span_lint_note(&self, lint: &'static Lint, span: Span, msg: &str,
                       note_span: Span, note: &str) {
         let mut err = self.lookup(lint, Some(span), msg);
-        if self.current_level(lint) != Level::Allow {
-            if note_span == span {
-                err.note(note);
-            } else {
-                err.span_note(note_span, note);
-            }
+        if note_span == span {
+            err.note(note);
+        } else {
+            err.span_note(note_span, note);
         }
         err.emit();
     }
@@ -695,9 +461,7 @@ pub trait LintContext<'tcx>: Sized {
                       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 {
-            err.span_help(span, help);
-        }
+        err.span_help(span, help);
         err.emit();
     }
 
@@ -710,94 +474,10 @@ pub trait LintContext<'tcx>: Sized {
     /// current lint context, call the provided function, then reset the
     /// lints in effect to their previous state.
     fn with_lint_attrs<F>(&mut self,
+                          id: ast::NodeId,
                           attrs: &'tcx [ast::Attribute],
                           f: F)
-        where F: FnOnce(&mut Self),
-    {
-        // Parse all of the lint attributes, and then add them all to the
-        // current dictionary of lint information. Along the way, keep a history
-        // of what we changed so we can roll everything back after invoking the
-        // specified closure
-        let mut pushed = 0;
-
-        for result in gather_attrs(attrs) {
-            let (is_group, lint_level_spans) = match result {
-                Err(span) => {
-                    span_err!(self.sess(), span, E0452,
-                              "malformed lint attribute");
-                    continue;
-                }
-                Ok((lint_name, level, span)) => {
-                    match self.lints().find_lint(&lint_name.as_str()) {
-                        Ok(lint_id) => (false, vec![(lint_id, level, span)]),
-                        Err(FindLintError::NotFound) => {
-                            match self.lints().lint_groups.get(&*lint_name.as_str()) {
-                                Some(&(ref v, _)) => (true,
-                                                      v.iter()
-                                                      .map(|lint_id: &LintId|
-                                                           (*lint_id, level, span))
-                                                      .collect()),
-                                None => {
-                                    // The lint or lint group doesn't exist.
-                                    // This is an error, but it was handled
-                                    // by check_lint_name_attribute.
-                                    continue;
-                                }
-                            }
-                        }
-                        Err(FindLintError::Removed) => continue,
-                    }
-                }
-            };
-
-            let lint_attr_name = result.expect("lint attribute should be well-formed").0;
-
-            for (lint_id, level, span) in lint_level_spans {
-                let (now, now_source) = self.lint_sess().get_source(lint_id);
-                if now == Forbid && level != Forbid {
-                    let forbidden_lint_name = match now_source {
-                        LintSource::Default => lint_id.to_string(),
-                        LintSource::Node(name, _) => name.to_string(),
-                        LintSource::CommandLine(name) => name.to_string(),
-                    };
-                    let mut diag_builder = struct_span_err!(self.sess(), span, E0453,
-                                                            "{}({}) overruled by outer forbid({})",
-                                                            level.as_str(), lint_attr_name,
-                                                            forbidden_lint_name);
-                    diag_builder.span_label(span, "overruled by previous forbid");
-                    match now_source {
-                        LintSource::Default => &mut diag_builder,
-                        LintSource::Node(_, forbid_source_span) => {
-                            diag_builder.span_label(forbid_source_span,
-                                                    "`forbid` level set here")
-                        },
-                        LintSource::CommandLine(_) => {
-                            diag_builder.note("`forbid` lint level was set on command line")
-                        }
-                    }.emit();
-                    if is_group { // don't set a separate error for every lint in the group
-                        break;
-                    }
-                } else if now != level {
-                    let cx = self.lint_sess_mut();
-                    cx.stack.push((lint_id, (now, now_source)));
-                    pushed += 1;
-                    cx.levels.set(lint_id, (level, Node(lint_attr_name, span)));
-                }
-            }
-        }
-
-        self.enter_attrs(attrs);
-        f(self);
-        self.exit_attrs(attrs);
-
-        // rollback
-        let cx = self.lint_sess_mut();
-        for _ in 0..pushed {
-            let (lint, lvlsrc) = cx.stack.pop().unwrap();
-            cx.levels.set(lint, lvlsrc);
-        }
-    }
+        where F: FnOnce(&mut Self);
 }
 
 
@@ -808,6 +488,16 @@ impl<'a> EarlyContext<'a> {
             sess,
             krate,
             lint_sess: LintSession::new(&sess.lint_store),
+            builder: LintLevelSets::builder(sess),
+            buffered: sess.buffered_lints.borrow_mut().take().unwrap(),
+        }
+    }
+
+    fn check_id(&mut self, id: ast::NodeId) {
+        for early_lint in self.buffered.take(id) {
+            self.lookup_and_emit(early_lint.lint_id.lint,
+                                 Some(early_lint.span.clone()),
+                                 &early_lint.msg);
         }
     }
 }
@@ -841,6 +531,32 @@ impl<'a, 'tcx> LintContext<'tcx> for LateContext<'a, 'tcx> {
         debug!("late context: exit_attrs({:?})", attrs);
         run_lints!(self, exit_lint_attrs, late_passes, attrs);
     }
+
+    fn lookup<S: Into<MultiSpan>>(&self,
+                                  lint: &'static Lint,
+                                  span: Option<S>,
+                                  msg: &str)
+                                  -> DiagnosticBuilder {
+        let id = self.last_ast_node_with_lint_attrs;
+        match span {
+            Some(s) => self.tcx.struct_span_lint_node(lint, id, s, msg),
+            None => self.tcx.struct_lint_node(lint, id, msg),
+        }
+    }
+
+    fn with_lint_attrs<F>(&mut self,
+                          id: ast::NodeId,
+                          attrs: &'tcx [ast::Attribute],
+                          f: F)
+        where F: FnOnce(&mut Self)
+    {
+        let prev = self.last_ast_node_with_lint_attrs;
+        self.last_ast_node_with_lint_attrs = id;
+        self.enter_attrs(attrs);
+        f(self);
+        self.exit_attrs(attrs);
+        self.last_ast_node_with_lint_attrs = prev;
+    }
 }
 
 impl<'a> LintContext<'a> for EarlyContext<'a> {
@@ -872,6 +588,28 @@ impl<'a> LintContext<'a> for EarlyContext<'a> {
         debug!("early context: exit_attrs({:?})", attrs);
         run_lints!(self, exit_lint_attrs, early_passes, attrs);
     }
+
+    fn lookup<S: Into<MultiSpan>>(&self,
+                                  lint: &'static Lint,
+                                  span: Option<S>,
+                                  msg: &str)
+                                  -> DiagnosticBuilder {
+        self.builder.struct_lint(lint, span.map(|s| s.into()), msg)
+    }
+
+    fn with_lint_attrs<F>(&mut self,
+                          id: ast::NodeId,
+                          attrs: &'a [ast::Attribute],
+                          f: F)
+        where F: FnOnce(&mut Self)
+    {
+        let push = self.builder.push(attrs);
+        self.check_id(id);
+        self.enter_attrs(attrs);
+        f(self);
+        self.exit_attrs(attrs);
+        self.builder.pop(push);
+    }
 }
 
 impl<'a, 'tcx> LateContext<'a, 'tcx> {
@@ -893,15 +631,6 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> {
         hir_visit::NestedVisitorMap::All(&self.tcx.hir)
     }
 
-    // Output any lints that were previously added to the session.
-    fn visit_id(&mut self, id: ast::NodeId) {
-        let lints = self.sess().lints.borrow_mut().take(id);
-        for early_lint in lints.iter().chain(self.tables.lints.get(id)) {
-            debug!("LateContext::visit_id: id={:?} early_lint={:?}", id, early_lint);
-            self.early_lint(early_lint);
-        }
-    }
-
     fn visit_nested_body(&mut self, body: hir::BodyId) {
         let old_tables = self.tables;
         self.tables = self.tcx.body_tables(body);
@@ -917,7 +646,7 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> {
     }
 
     fn visit_item(&mut self, it: &'tcx hir::Item) {
-        self.with_lint_attrs(&it.attrs, |cx| {
+        self.with_lint_attrs(it.id, &it.attrs, |cx| {
             cx.with_param_env(it.id, |cx| {
                 run_lints!(cx, check_item, late_passes, it);
                 hir_visit::walk_item(cx, it);
@@ -927,7 +656,7 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> {
     }
 
     fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem) {
-        self.with_lint_attrs(&it.attrs, |cx| {
+        self.with_lint_attrs(it.id, &it.attrs, |cx| {
             cx.with_param_env(it.id, |cx| {
                 run_lints!(cx, check_foreign_item, late_passes, it);
                 hir_visit::walk_foreign_item(cx, it);
@@ -942,7 +671,7 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> {
     }
 
     fn visit_expr(&mut self, e: &'tcx hir::Expr) {
-        self.with_lint_attrs(&e.attrs, |cx| {
+        self.with_lint_attrs(e.id, &e.attrs, |cx| {
             run_lints!(cx, check_expr, late_passes, e);
             hir_visit::walk_expr(cx, e);
             run_lints!(cx, check_expr_post, late_passes, e);
@@ -984,7 +713,7 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> {
     }
 
     fn visit_struct_field(&mut self, s: &'tcx hir::StructField) {
-        self.with_lint_attrs(&s.attrs, |cx| {
+        self.with_lint_attrs(s.id, &s.attrs, |cx| {
             run_lints!(cx, check_struct_field, late_passes, s);
             hir_visit::walk_struct_field(cx, s);
         })
@@ -994,7 +723,7 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> {
                      v: &'tcx hir::Variant,
                      g: &'tcx hir::Generics,
                      item_id: ast::NodeId) {
-        self.with_lint_attrs(&v.node.attrs, |cx| {
+        self.with_lint_attrs(v.node.data.id(), &v.node.attrs, |cx| {
             run_lints!(cx, check_variant, late_passes, v, g);
             hir_visit::walk_variant(cx, v, g, item_id);
             run_lints!(cx, check_variant_post, late_passes, v, g);
@@ -1017,7 +746,7 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> {
     }
 
     fn visit_local(&mut self, l: &'tcx hir::Local) {
-        self.with_lint_attrs(&l.attrs, |cx| {
+        self.with_lint_attrs(l.id, &l.attrs, |cx| {
             run_lints!(cx, check_local, late_passes, l);
             hir_visit::walk_local(cx, l);
         })
@@ -1045,7 +774,7 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> {
     }
 
     fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
-        self.with_lint_attrs(&trait_item.attrs, |cx| {
+        self.with_lint_attrs(trait_item.id, &trait_item.attrs, |cx| {
             cx.with_param_env(trait_item.id, |cx| {
                 run_lints!(cx, check_trait_item, late_passes, trait_item);
                 hir_visit::walk_trait_item(cx, trait_item);
@@ -1055,7 +784,7 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> {
     }
 
     fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
-        self.with_lint_attrs(&impl_item.attrs, |cx| {
+        self.with_lint_attrs(impl_item.id, &impl_item.attrs, |cx| {
             cx.with_param_env(impl_item.id, |cx| {
                 run_lints!(cx, check_impl_item, late_passes, impl_item);
                 hir_visit::walk_impl_item(cx, impl_item);
@@ -1080,14 +809,13 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> {
     }
 
     fn visit_attribute(&mut self, attr: &'tcx ast::Attribute) {
-        check_lint_name_attribute(self, attr);
         run_lints!(self, check_attribute, late_passes, attr);
     }
 }
 
 impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
     fn visit_item(&mut self, it: &'a ast::Item) {
-        self.with_lint_attrs(&it.attrs, |cx| {
+        self.with_lint_attrs(it.id, &it.attrs, |cx| {
             run_lints!(cx, check_item, early_passes, it);
             ast_visit::walk_item(cx, it);
             run_lints!(cx, check_item_post, early_passes, it);
@@ -1095,7 +823,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
     }
 
     fn visit_foreign_item(&mut self, it: &'a ast::ForeignItem) {
-        self.with_lint_attrs(&it.attrs, |cx| {
+        self.with_lint_attrs(it.id, &it.attrs, |cx| {
             run_lints!(cx, check_foreign_item, early_passes, it);
             ast_visit::walk_foreign_item(cx, it);
             run_lints!(cx, check_foreign_item_post, early_passes, it);
@@ -1104,11 +832,12 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
 
     fn visit_pat(&mut self, p: &'a ast::Pat) {
         run_lints!(self, check_pat, early_passes, p);
+        self.check_id(p.id);
         ast_visit::walk_pat(self, p);
     }
 
     fn visit_expr(&mut self, e: &'a ast::Expr) {
-        self.with_lint_attrs(&e.attrs, |cx| {
+        self.with_lint_attrs(e.id, &e.attrs, |cx| {
             run_lints!(cx, check_expr, early_passes, e);
             ast_visit::walk_expr(cx, e);
         })
@@ -1116,12 +845,14 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
 
     fn visit_stmt(&mut self, s: &'a ast::Stmt) {
         run_lints!(self, check_stmt, early_passes, s);
+        self.check_id(s.id);
         ast_visit::walk_stmt(self, s);
     }
 
     fn visit_fn(&mut self, fk: ast_visit::FnKind<'a>, decl: &'a ast::FnDecl,
                 span: Span, id: ast::NodeId) {
         run_lints!(self, check_fn, early_passes, fk, decl, span, id);
+        self.check_id(id);
         ast_visit::walk_fn(self, fk, decl, span);
         run_lints!(self, check_fn_post, early_passes, fk, decl, span, id);
     }
@@ -1133,19 +864,20 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
                         item_id: ast::NodeId,
                         _: Span) {
         run_lints!(self, check_struct_def, early_passes, s, ident, g, item_id);
+        self.check_id(s.id());
         ast_visit::walk_struct_def(self, s);
         run_lints!(self, check_struct_def_post, early_passes, s, ident, g, item_id);
     }
 
     fn visit_struct_field(&mut self, s: &'a ast::StructField) {
-        self.with_lint_attrs(&s.attrs, |cx| {
+        self.with_lint_attrs(s.id, &s.attrs, |cx| {
             run_lints!(cx, check_struct_field, early_passes, s);
             ast_visit::walk_struct_field(cx, s);
         })
     }
 
     fn visit_variant(&mut self, v: &'a ast::Variant, g: &'a ast::Generics, item_id: ast::NodeId) {
-        self.with_lint_attrs(&v.node.attrs, |cx| {
+        self.with_lint_attrs(item_id, &v.node.attrs, |cx| {
             run_lints!(cx, check_variant, early_passes, v, g);
             ast_visit::walk_variant(cx, v, g, item_id);
             run_lints!(cx, check_variant_post, early_passes, v, g);
@@ -1154,6 +886,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
 
     fn visit_ty(&mut self, t: &'a ast::Ty) {
         run_lints!(self, check_ty, early_passes, t);
+        self.check_id(t.id);
         ast_visit::walk_ty(self, t);
     }
 
@@ -1163,12 +896,13 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
 
     fn visit_mod(&mut self, m: &'a ast::Mod, s: Span, _a: &[ast::Attribute], n: ast::NodeId) {
         run_lints!(self, check_mod, early_passes, m, s, n);
+        self.check_id(n);
         ast_visit::walk_mod(self, m);
         run_lints!(self, check_mod_post, early_passes, m, s, n);
     }
 
     fn visit_local(&mut self, l: &'a ast::Local) {
-        self.with_lint_attrs(&l.attrs, |cx| {
+        self.with_lint_attrs(l.id, &l.attrs, |cx| {
             run_lints!(cx, check_local, early_passes, l);
             ast_visit::walk_local(cx, l);
         })
@@ -1176,6 +910,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
 
     fn visit_block(&mut self, b: &'a ast::Block) {
         run_lints!(self, check_block, early_passes, b);
+        self.check_id(b.id);
         ast_visit::walk_block(self, b);
         run_lints!(self, check_block_post, early_passes, b);
     }
@@ -1195,7 +930,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
     }
 
     fn visit_trait_item(&mut self, trait_item: &'a ast::TraitItem) {
-        self.with_lint_attrs(&trait_item.attrs, |cx| {
+        self.with_lint_attrs(trait_item.id, &trait_item.attrs, |cx| {
             run_lints!(cx, check_trait_item, early_passes, trait_item);
             ast_visit::walk_trait_item(cx, trait_item);
             run_lints!(cx, check_trait_item_post, early_passes, trait_item);
@@ -1203,7 +938,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
     }
 
     fn visit_impl_item(&mut self, impl_item: &'a ast::ImplItem) {
-        self.with_lint_attrs(&impl_item.attrs, |cx| {
+        self.with_lint_attrs(impl_item.id, &impl_item.attrs, |cx| {
             run_lints!(cx, check_impl_item, early_passes, impl_item);
             ast_visit::walk_impl_item(cx, impl_item);
             run_lints!(cx, check_impl_item_post, early_passes, impl_item);
@@ -1212,6 +947,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
 
     fn visit_lifetime(&mut self, lt: &'a ast::Lifetime) {
         run_lints!(self, check_lifetime, early_passes, lt);
+        self.check_id(lt.id);
     }
 
     fn visit_lifetime_def(&mut self, lt: &'a ast::LifetimeDef) {
@@ -1220,11 +956,13 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
 
     fn visit_path(&mut self, p: &'a ast::Path, id: ast::NodeId) {
         run_lints!(self, check_path, early_passes, p, id);
+        self.check_id(id);
         ast_visit::walk_path(self, p);
     }
 
     fn visit_path_list_item(&mut self, prefix: &'a ast::Path, item: &'a ast::PathListItem) {
         run_lints!(self, check_path_list_item, early_passes, item);
+        self.check_id(item.node.id);
         ast_visit::walk_path_list_item(self, prefix, item);
     }
 
@@ -1233,110 +971,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
     }
 
     fn visit_mac_def(&mut self, _mac: &'a ast::MacroDef, id: ast::NodeId) {
-        let lints = self.sess.lints.borrow_mut().take(id);
-        for early_lint in lints {
-            self.early_lint(&early_lint);
-        }
-    }
-}
-
-enum CheckLintNameResult {
-    Ok,
-    // Lint doesn't exist
-    NoLint,
-    // The lint is either renamed or removed. This is the warning
-    // message.
-    Warning(String),
-}
-
-/// Checks the name of a lint for its existence, and whether it was
-/// renamed or removed. Generates a DiagnosticBuilder containing a
-/// warning for renamed and removed lints. This is over both lint
-/// names from attributes and those passed on the command line. Since
-/// it emits non-fatal warnings and there are *two* lint passes that
-/// inspect attributes, this is only run from the late pass to avoid
-/// printing duplicate warnings.
-fn check_lint_name(lint_cx: &LintStore,
-                   lint_name: &str) -> CheckLintNameResult {
-    match lint_cx.by_name.get(lint_name) {
-        Some(&Renamed(ref new_name, _)) => {
-            CheckLintNameResult::Warning(
-                format!("lint {} has been renamed to {}", lint_name, new_name)
-            )
-        },
-        Some(&Removed(ref reason)) => {
-            CheckLintNameResult::Warning(
-                format!("lint {} has been removed: {}", lint_name, reason)
-            )
-        },
-        None => {
-            match lint_cx.lint_groups.get(lint_name) {
-                None => {
-                    CheckLintNameResult::NoLint
-                }
-                Some(_) => {
-                    /* lint group exists */
-                    CheckLintNameResult::Ok
-                }
-            }
-        }
-        Some(_) => {
-            /* lint exists */
-            CheckLintNameResult::Ok
-        }
-    }
-}
-
-// Checks the validity of lint names derived from attributes
-fn check_lint_name_attribute(cx: &LateContext, attr: &ast::Attribute) {
-    for result in gather_attr(attr) {
-        match result {
-            Err(_) => {
-                // Malformed lint attr. Reported by with_lint_attrs
-                continue;
-            }
-            Ok((lint_name, _, span)) => {
-                match check_lint_name(&cx.lint_sess.lints, &lint_name.as_str()) {
-                    CheckLintNameResult::Ok => (),
-                    CheckLintNameResult::Warning(ref msg) => {
-                        cx.span_lint(builtin::RENAMED_AND_REMOVED_LINTS,
-                                     span, msg);
-                    }
-                    CheckLintNameResult::NoLint => {
-                        cx.span_lint(builtin::UNKNOWN_LINTS, span,
-                                     &format!("unknown lint: `{}`",
-                                              lint_name));
-                    }
-                }
-            }
-        }
-    }
-}
-
-// Checks the validity of lint names derived from the command line
-fn check_lint_name_cmdline(sess: &Session, lint_cx: &LintStore,
-                           lint_name: &str, level: Level) {
-    let db = match check_lint_name(lint_cx, lint_name) {
-        CheckLintNameResult::Ok => None,
-        CheckLintNameResult::Warning(ref msg) => {
-            Some(sess.struct_warn(msg))
-        },
-        CheckLintNameResult::NoLint => {
-            Some(struct_err!(sess, E0602, "unknown lint: `{}`", lint_name))
-        }
-    };
-
-    if let Some(mut db) = db {
-        let msg = format!("requested on the command line with `{} {}`",
-                          match level {
-                              Level::Allow => "-A",
-                              Level::Warn => "-W",
-                              Level::Deny => "-D",
-                              Level::Forbid => "-F",
-                          },
-                          lint_name);
-        db.note(&msg);
-        db.emit();
+        self.check_id(id);
     }
 }
 
@@ -1355,10 +990,11 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
         param_env: ty::ParamEnv::empty(Reveal::UserFacing),
         access_levels,
         lint_sess: LintSession::new(&tcx.sess.lint_store),
+        last_ast_node_with_lint_attrs: ast::CRATE_NODE_ID,
     };
 
     // Visit the whole crate.
-    cx.with_lint_attrs(&krate.attrs, |cx| {
+    cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| {
         // since the root module isn't visited as an item (because it isn't an
         // item), warn for it here.
         run_lints!(cx, check_crate, late_passes, krate);
@@ -1368,16 +1004,6 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
         run_lints!(cx, check_crate_post, late_passes, krate);
     });
 
-    // If we missed any lints added to the session, then there's a bug somewhere
-    // in the iteration code.
-    if let Some((id, v)) = tcx.sess.lints.borrow().get_any() {
-        for early_lint in v {
-            span_bug!(early_lint.diagnostic.span.clone(),
-                      "unprocessed lint {:?} at {}",
-                      early_lint, tcx.hir.node_to_string(*id));
-        }
-    }
-
     // Put the lint store levels and passes back in the session.
     cx.lint_sess.restore(&tcx.sess.lint_store);
 }
@@ -1386,13 +1012,7 @@ pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) {
     let mut cx = EarlyContext::new(sess, krate);
 
     // Visit the whole crate.
-    cx.with_lint_attrs(&krate.attrs, |cx| {
-        // Lints may be assigned to the whole crate.
-        let lints = cx.sess.lints.borrow_mut().take(ast::CRATE_NODE_ID);
-        for early_lint in lints {
-            cx.early_lint(&early_lint);
-        }
-
+    cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| {
         // since the root module isn't visited as an item (because it isn't an
         // item), warn for it here.
         run_lints!(cx, check_crate, early_passes, krate);
@@ -1405,11 +1025,11 @@ pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) {
     // Put the lint store levels and passes back in the session.
     cx.lint_sess.restore(&sess.lint_store);
 
-    // If we missed any lints added to the session, then there's a bug somewhere
-    // in the iteration code.
-    for (_, v) in sess.lints.borrow().get_any() {
-        for early_lint in v {
-            span_bug!(early_lint.diagnostic.span.clone(), "unprocessed lint {:?}", early_lint);
+    // Emit all buffered lints from early on in the session now that we've
+    // calculated the lint levels for all AST nodes.
+    for (_id, lints) in cx.buffered.map {
+        for early_lint in lints {
+            span_bug!(early_lint.span, "failed to process bufferd lint here");
         }
     }
 }
@@ -1425,8 +1045,13 @@ impl Decodable for LintId {
     fn decode<D: Decoder>(d: &mut D) -> Result<LintId, D::Error> {
         let s = d.read_str()?;
         ty::tls::with(|tcx| {
-            match tcx.sess.lint_store.borrow().find_lint(&s) {
-                Ok(id) => Ok(id),
+            match tcx.sess.lint_store.borrow().find_lints(&s) {
+                Ok(ids) => {
+                    if ids.len() != 0 {
+                        panic!("invalid lint-id `{}`", s);
+                    }
+                    Ok(ids[0])
+                }
                 Err(_) => panic!("invalid lint-id `{}`", s),
             }
         })
diff --git a/src/librustc/lint/levels.rs b/src/librustc/lint/levels.rs
new file mode 100644
index 00000000000..ab9d4f75597
--- /dev/null
+++ b/src/librustc/lint/levels.rs
@@ -0,0 +1,343 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::cmp;
+
+use errors::DiagnosticBuilder;
+use hir::HirId;
+use lint::builtin;
+use lint::context::CheckLintNameResult;
+use lint::{self, Lint, LintId, Level, LintSource};
+use session::Session;
+use syntax::ast;
+use syntax::attr;
+use syntax::codemap::MultiSpan;
+use syntax::symbol::Symbol;
+use util::nodemap::FxHashMap;
+
+pub struct LintLevelSets {
+    list: Vec<LintSet>,
+    lint_cap: Level,
+}
+
+enum LintSet {
+    CommandLine {
+        // -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
+        // flag.
+        specs: FxHashMap<LintId, (Level, LintSource)>,
+    },
+
+    Node {
+        specs: FxHashMap<LintId, (Level, LintSource)>,
+        parent: u32,
+    },
+}
+
+impl LintLevelSets {
+    pub fn new(sess: &Session) -> LintLevelSets {
+        let mut me = LintLevelSets {
+            list: Vec::new(),
+            lint_cap: Level::Forbid,
+        };
+        me.process_command_line(sess);
+        return me
+    }
+
+    pub fn builder(sess: &Session) -> LintLevelsBuilder {
+        LintLevelsBuilder::new(sess, LintLevelSets::new(sess))
+    }
+
+    fn process_command_line(&mut self, sess: &Session) {
+        let store = sess.lint_store.borrow();
+        let mut specs = FxHashMap();
+        self.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid);
+
+        for &(ref lint_name, level) in &sess.opts.lint_opts {
+            store.check_lint_name_cmdline(sess, &lint_name, level);
+
+            // If the cap is less than this specified level, e.g. if we've got
+            // `--cap-lints allow` but we've also got `-D foo` then we ignore
+            // this specification as the lint cap will set it to allow anyway.
+            let level = cmp::min(level, self.lint_cap);
+
+            let lint_flag_val = Symbol::intern(lint_name);
+            let ids = match store.find_lints(&lint_name) {
+                Ok(ids) => ids,
+                Err(_) => continue, // errors handled in check_lint_name_cmdline above
+            };
+            for id in ids {
+                let src = LintSource::CommandLine(lint_flag_val);
+                specs.insert(id, (level, src));
+            }
+        }
+
+        self.list.push(LintSet::CommandLine {
+            specs: specs,
+        });
+    }
+
+    fn get_lint_level(&self, lint: &'static Lint, idx: u32)
+        -> (Level, LintSource)
+    {
+        let (level, mut src) = self.get_lint_id_level(LintId::of(lint), idx);
+
+        // If `level` is none then we actually assume the default level for this
+        // lint.
+        let mut level = level.unwrap_or(lint.default_level);
+
+        // If we're about to issue a warning, check at the last minute for any
+        // directives against the warnings "lint". If, for example, there's an
+        // `allow(warnings)` in scope then we want to respect that instead.
+        if level == Level::Warn {
+            let (warnings_level, warnings_src) =
+                self.get_lint_id_level(LintId::of(lint::builtin::WARNINGS), idx);
+            if let Some(configured_warning_level) = warnings_level {
+                if configured_warning_level != Level::Warn {
+                    level = configured_warning_level;
+                    src = warnings_src;
+                }
+            }
+        }
+
+        // Ensure that we never exceed the `--cap-lints` argument.
+        level = cmp::min(level, self.lint_cap);
+
+        return (level, src)
+    }
+
+    fn get_lint_id_level(&self, id: LintId, mut idx: u32)
+        -> (Option<Level>, LintSource)
+    {
+        loop {
+            match self.list[idx as usize] {
+                LintSet::CommandLine { ref specs } => {
+                    if let Some(&(level, src)) = specs.get(&id) {
+                        return (Some(level), src)
+                    }
+                    return (None, LintSource::Default)
+                }
+                LintSet::Node { ref specs, parent } => {
+                    if let Some(&(level, src)) = specs.get(&id) {
+                        return (Some(level), src)
+                    }
+                    idx = parent;
+                }
+            }
+        }
+    }
+}
+
+pub struct LintLevelsBuilder<'a> {
+    sess: &'a Session,
+    sets: LintLevelSets,
+    id_to_set: FxHashMap<HirId, u32>,
+    cur: u32,
+    warn_about_weird_lints: bool,
+}
+
+pub struct BuilderPush {
+    prev: u32,
+}
+
+impl<'a> LintLevelsBuilder<'a> {
+    pub fn new(sess: &'a Session, sets: LintLevelSets) -> LintLevelsBuilder<'a> {
+        assert_eq!(sets.list.len(), 1);
+        LintLevelsBuilder {
+            sess,
+            sets,
+            cur: 0,
+            id_to_set: FxHashMap(),
+            warn_about_weird_lints: sess.buffered_lints.borrow().is_some(),
+        }
+    }
+
+    /// Pushes a list of AST lint attributes onto this context.
+    ///
+    /// This function will return a `BuilderPush` object which should be be
+    /// passed to `pop` when this scope for the attributes provided is exited.
+    ///
+    /// This function will perform a number of tasks:
+    ///
+    /// * It'll validate all lint-related attributes in `attrs`
+    /// * It'll mark all lint-related attriutes as used
+    /// * Lint levels will be updated based on the attributes provided
+    /// * Lint attributes are validated, e.g. a #[forbid] can't be switched to
+    ///   #[allow]
+    ///
+    /// Don't forget to call `pop`!
+    pub fn push(&mut self, attrs: &[ast::Attribute]) -> BuilderPush {
+        let mut specs = FxHashMap();
+        let store = self.sess.lint_store.borrow();
+        let sess = self.sess;
+        let bad_attr = |span| {
+            span_err!(sess, span, E0452,
+                      "malformed lint attribute");
+        };
+        for attr in attrs {
+            let level = match attr.name().and_then(|name| Level::from_str(&name.as_str())) {
+                None => continue,
+                Some(lvl) => lvl,
+            };
+
+            let meta = unwrap_or!(attr.meta(), continue);
+            attr::mark_used(attr);
+
+            let metas = if let Some(metas) = meta.meta_item_list() {
+                metas
+            } else {
+                bad_attr(meta.span);
+                continue
+            };
+
+            for li in metas {
+                let word = match li.word() {
+                    Some(word) => word,
+                    None => {
+                        bad_attr(li.span);
+                        continue
+                    }
+                };
+                let name = word.name();
+                match store.check_lint_name(&name.as_str()) {
+                    CheckLintNameResult::Ok(ids) => {
+                        let src = LintSource::Node(name, li.span);
+                        for id in ids {
+                            specs.insert(*id, (level, src));
+                        }
+                    }
+                    CheckLintNameResult::Warning(ref msg) => {
+                        if self.warn_about_weird_lints {
+                            self.struct_lint(builtin::RENAMED_AND_REMOVED_LINTS,
+                                             Some(li.span.into()),
+                                             msg)
+                                .emit();
+                        }
+                    }
+                    CheckLintNameResult::NoLint => {
+                        if self.warn_about_weird_lints {
+                            self.struct_lint(builtin::UNKNOWN_LINTS,
+                                             Some(li.span.into()),
+                                             &format!("unknown lint: `{}`", name))
+                                .emit();
+                        }
+                    }
+                }
+            }
+        }
+
+        for (id, &(level, ref src)) in specs.iter() {
+            if level == Level::Forbid {
+                continue
+            }
+            let forbid_src = match self.sets.get_lint_id_level(*id, self.cur) {
+                (Some(Level::Forbid), src) => src,
+                _ => continue,
+            };
+            let forbidden_lint_name = match forbid_src {
+                LintSource::Default => id.to_string(),
+                LintSource::Node(name, _) => name.to_string(),
+                LintSource::CommandLine(name) => name.to_string(),
+            };
+            let (lint_attr_name, lint_attr_span) = match *src {
+                LintSource::Node(name, span) => (name, span),
+                _ => continue,
+            };
+            let mut diag_builder = struct_span_err!(self.sess,
+                                                    lint_attr_span,
+                                                    E0453,
+                                                    "{}({}) overruled by outer forbid({})",
+                                                    level.as_str(),
+                                                    lint_attr_name,
+                                                    forbidden_lint_name);
+            diag_builder.span_label(lint_attr_span, "overruled by previous forbid");
+            match forbid_src {
+                LintSource::Default => &mut diag_builder,
+                LintSource::Node(_, forbid_source_span) => {
+                    diag_builder.span_label(forbid_source_span,
+                                            "`forbid` level set here")
+                },
+                LintSource::CommandLine(_) => {
+                    diag_builder.note("`forbid` lint level was set on command line")
+                }
+            }.emit();
+            // don't set a separate error for every lint in the group
+            break
+        }
+
+        let prev = self.cur;
+        if specs.len() > 0 {
+            self.cur = self.sets.list.len() as u32;
+            self.sets.list.push(LintSet::Node {
+                specs: specs,
+                parent: prev,
+            });
+        }
+
+        BuilderPush {
+            prev: prev,
+        }
+    }
+
+    /// Called after `push` when the scope of a set of attributes are exited.
+    pub fn pop(&mut self, push: BuilderPush) {
+        self.cur = push.prev;
+    }
+
+    /// Used to emit a lint-related diagnostic based on the current state of
+    /// this lint context.
+    pub fn struct_lint(&self,
+                       lint: &'static Lint,
+                       span: Option<MultiSpan>,
+                       msg: &str)
+        -> DiagnosticBuilder<'a>
+    {
+        let (level, src) = self.sets.get_lint_level(lint, self.cur);
+        lint::struct_lint_level(self.sess, lint, level, src, span, msg)
+    }
+
+    /// Registers the ID provided with the current set of lints stored in
+    /// this context.
+    pub fn register_id(&mut self, id: HirId) {
+        self.id_to_set.insert(id, self.cur);
+    }
+
+    pub fn build(self) -> LintLevelSets {
+        self.sets
+    }
+
+    pub fn build_map(self) -> LintLevelMap {
+        LintLevelMap {
+            sets: self.sets,
+            id_to_set: self.id_to_set,
+        }
+    }
+}
+
+pub struct LintLevelMap {
+    sets: LintLevelSets,
+    id_to_set: FxHashMap<HirId, u32>,
+}
+
+impl LintLevelMap {
+    /// If the `id` was previously registered with `register_id` when building
+    /// this `LintLevelMap` this returns the corresponding lint level and source
+    /// of the lint level for the lint provided.
+    ///
+    /// If the `id` was not previously registered, returns `None`. If `None` is
+    /// returned then the parent of `id` should be acquired and this function
+    /// should be called again.
+    pub fn level_and_source(&self, lint: &'static Lint, id: HirId)
+        -> Option<(Level, LintSource)>
+    {
+        self.id_to_set.get(&id).map(|idx| {
+            self.sets.get_lint_level(lint, *idx)
+        })
+    }
+}
diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs
index f9222ac9400..c64e1c08082 100644
--- a/src/librustc/lint/mod.rs
+++ b/src/librustc/lint/mod.rs
@@ -31,20 +31,27 @@
 pub use self::Level::*;
 pub use self::LintSource::*;
 
+use std::rc::Rc;
+
+use errors::DiagnosticBuilder;
+use hir::def_id::{CrateNum, LOCAL_CRATE};
+use hir::intravisit::{self, FnKind};
 use hir;
-use hir::intravisit::FnKind;
-use std::hash;
+use session::Session;
 use std::ascii::AsciiExt;
-use syntax_pos::Span;
-use syntax::visit as ast_visit;
+use std::hash;
 use syntax::ast;
+use syntax::codemap::MultiSpan;
 use syntax::symbol::Symbol;
+use syntax::visit as ast_visit;
+use syntax_pos::Span;
+use ty::TyCtxt;
+use ty::maps::Providers;
+use util::nodemap::NodeMap;
 
 pub use lint::context::{LateContext, EarlyContext, LintContext, LintStore,
-                        raw_emit_lint, check_crate, check_ast_crate, gather_attrs,
-                        raw_struct_lint, FutureIncompatibleInfo, EarlyLint, IntoEarlyLint};
-
-pub use lint::table::LintTable;
+                        check_crate, check_ast_crate,
+                        FutureIncompatibleInfo, BufferedEarlyLint};
 
 /// Specification of a single lint.
 #[derive(Copy, Clone, Debug)]
@@ -351,4 +358,215 @@ pub type LevelSource = (Level, LintSource);
 
 pub mod builtin;
 mod context;
-mod table;
+mod levels;
+
+pub use self::levels::{LintLevelSets, LintLevelMap};
+
+pub struct LintBuffer {
+    map: NodeMap<Vec<BufferedEarlyLint>>,
+}
+
+impl LintBuffer {
+    pub fn new() -> LintBuffer {
+        LintBuffer { map: NodeMap() }
+    }
+
+    pub fn add_lint(&mut self,
+                    lint: &'static Lint,
+                    id: ast::NodeId,
+                    sp: MultiSpan,
+                    msg: &str) {
+        let early_lint = BufferedEarlyLint {
+            lint_id: LintId::of(lint),
+            ast_id: id,
+            span: sp,
+            msg: msg.to_string(),
+        };
+        let arr = self.map.entry(id).or_insert(Vec::new());
+        if !arr.contains(&early_lint) {
+            arr.push(early_lint);
+        }
+    }
+
+    pub fn take(&mut self, id: ast::NodeId) -> Vec<BufferedEarlyLint> {
+        self.map.remove(&id).unwrap_or(Vec::new())
+    }
+
+    pub fn get_any(&self) -> Option<&[BufferedEarlyLint]> {
+        let key = self.map.keys().next().map(|k| *k);
+        key.map(|k| &self.map[&k][..])
+    }
+}
+
+pub fn struct_lint_level<'a>(sess: &'a Session,
+                             lint: &'static Lint,
+                             level: Level,
+                             src: LintSource,
+                             span: Option<MultiSpan>,
+                             msg: &str)
+    -> DiagnosticBuilder<'a>
+{
+    let mut err = match (level, span) {
+        (Level::Allow, _) => return sess.diagnostic().struct_dummy(),
+        (Level::Warn, Some(span)) => sess.struct_span_warn(span, msg),
+        (Level::Warn, None) => sess.struct_warn(msg),
+        (Level::Deny, Some(span)) |
+        (Level::Forbid, Some(span)) => sess.struct_span_err(span, msg),
+        (Level::Deny, None) |
+        (Level::Forbid, None) => sess.struct_err(msg),
+    };
+
+    let name = lint.name_lower();
+    match src {
+        LintSource::Default => {
+            sess.diag_note_once(
+                &mut err,
+                lint,
+                &format!("#[{}({})] on by default", level.as_str(), name));
+        }
+        LintSource::CommandLine(lint_flag_val) => {
+            let flag = match level {
+                Level::Warn => "-W",
+                Level::Deny => "-D",
+                Level::Forbid => "-F",
+                Level::Allow => panic!(),
+            };
+            let hyphen_case_lint_name = name.replace("_", "-");
+            if lint_flag_val.as_str() == name {
+                sess.diag_note_once(
+                    &mut err,
+                    lint,
+                    &format!("requested on the command line with `{} {}`",
+                             flag, hyphen_case_lint_name));
+            } else {
+                let hyphen_case_flag_val = lint_flag_val.as_str().replace("_", "-");
+                sess.diag_note_once(
+                    &mut err,
+                    lint,
+                    &format!("`{} {}` implied by `{} {}`",
+                             flag, hyphen_case_lint_name, flag,
+                             hyphen_case_flag_val));
+            }
+        }
+        LintSource::Node(lint_attr_name, src) => {
+            sess.diag_span_note_once(&mut err, lint, src, "lint level defined here");
+            if lint_attr_name.as_str() != name {
+                let level_str = level.as_str();
+                sess.diag_note_once(&mut err, lint,
+                                    &format!("#[{}({})] implied by #[{}({})]",
+                                             level_str, name, level_str, lint_attr_name));
+            }
+        }
+    }
+
+    // Check for future incompatibility lints and issue a stronger warning.
+    let lints = sess.lint_store.borrow();
+    if let Some(future_incompatible) = lints.future_incompatible(LintId::of(lint)) {
+        let explanation = format!("this was previously accepted by the compiler \
+                                   but is being phased out; \
+                                   it will become a hard error in a future release!");
+        let citation = format!("for more information, see {}",
+                               future_incompatible.reference);
+        err.warn(&explanation);
+        err.note(&citation);
+    }
+
+    return err
+}
+
+fn lint_levels<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, cnum: CrateNum)
+    -> Rc<LintLevelMap>
+{
+    assert_eq!(cnum, LOCAL_CRATE);
+    let mut builder = LintLevelMapBuilder {
+        levels: LintLevelSets::builder(tcx.sess),
+        tcx: tcx,
+    };
+    let krate = tcx.hir.krate();
+
+    builder.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |builder| {
+        intravisit::walk_crate(builder, krate);
+    });
+
+    Rc::new(builder.levels.build_map())
+}
+
+struct LintLevelMapBuilder<'a, 'tcx: 'a> {
+    levels: levels::LintLevelsBuilder<'tcx>,
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+}
+
+impl<'a, 'tcx> LintLevelMapBuilder<'a, 'tcx> {
+    fn with_lint_attrs<F>(&mut self,
+                          id: ast::NodeId,
+                          attrs: &[ast::Attribute],
+                          f: F)
+        where F: FnOnce(&mut Self)
+    {
+        let push = self.levels.push(attrs);
+        self.levels.register_id(self.tcx.hir.definitions().node_to_hir_id(id));
+        f(self);
+        self.levels.pop(push);
+    }
+}
+
+impl<'a, 'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'a, 'tcx> {
+    fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
+        intravisit::NestedVisitorMap::All(&self.tcx.hir)
+    }
+
+    fn visit_item(&mut self, it: &'tcx hir::Item) {
+        self.with_lint_attrs(it.id, &it.attrs, |builder| {
+            intravisit::walk_item(builder, it);
+        });
+    }
+
+    fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem) {
+        self.with_lint_attrs(it.id, &it.attrs, |builder| {
+            intravisit::walk_foreign_item(builder, it);
+        })
+    }
+
+    fn visit_expr(&mut self, e: &'tcx hir::Expr) {
+        self.with_lint_attrs(e.id, &e.attrs, |builder| {
+            intravisit::walk_expr(builder, e);
+        })
+    }
+
+    fn visit_struct_field(&mut self, s: &'tcx hir::StructField) {
+        self.with_lint_attrs(s.id, &s.attrs, |builder| {
+            intravisit::walk_struct_field(builder, s);
+        })
+    }
+
+    fn visit_variant(&mut self,
+                     v: &'tcx hir::Variant,
+                     g: &'tcx hir::Generics,
+                     item_id: ast::NodeId) {
+        self.with_lint_attrs(v.node.data.id(), &v.node.attrs, |builder| {
+            intravisit::walk_variant(builder, v, g, item_id);
+        })
+    }
+
+    fn visit_local(&mut self, l: &'tcx hir::Local) {
+        self.with_lint_attrs(l.id, &l.attrs, |builder| {
+            intravisit::walk_local(builder, l);
+        })
+    }
+
+    fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
+        self.with_lint_attrs(trait_item.id, &trait_item.attrs, |builder| {
+            intravisit::walk_trait_item(builder, trait_item);
+        });
+    }
+
+    fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
+        self.with_lint_attrs(impl_item.id, &impl_item.attrs, |builder| {
+            intravisit::walk_impl_item(builder, impl_item);
+        });
+    }
+}
+
+pub fn provide(providers: &mut Providers) {
+    providers.lint_levels = lint_levels;
+}
diff --git a/src/librustc/lint/table.rs b/src/librustc/lint/table.rs
deleted file mode 100644
index f2dab25229a..00000000000
--- a/src/librustc/lint/table.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use syntax::ast;
-use syntax_pos::MultiSpan;
-use util::nodemap::NodeMap;
-
-use super::{Lint, LintId, EarlyLint, IntoEarlyLint};
-
-#[derive(RustcEncodable, RustcDecodable)]
-pub struct LintTable {
-    map: NodeMap<Vec<EarlyLint>>
-}
-
-impl LintTable {
-    pub fn new() -> Self {
-        LintTable { map: NodeMap() }
-    }
-
-    pub fn add_lint<S: Into<MultiSpan>>(&mut self,
-                                        lint: &'static Lint,
-                                        id: ast::NodeId,
-                                        sp: S,
-                                        msg: String)
-    {
-        self.add_lint_diagnostic(lint, id, (sp, &msg[..]))
-    }
-
-    pub fn add_lint_diagnostic<M>(&mut self,
-                                  lint: &'static Lint,
-                                  id: ast::NodeId,
-                                  msg: M)
-        where M: IntoEarlyLint,
-    {
-        let lint_id = LintId::of(lint);
-        let early_lint = msg.into_early_lint(lint_id);
-        let arr = self.map.entry(id).or_insert(vec![]);
-        if !arr.contains(&early_lint) {
-            arr.push(early_lint);
-        }
-    }
-
-    pub fn get(&self, id: ast::NodeId) -> &[EarlyLint] {
-        self.map.get(&id).map(|v| &v[..]).unwrap_or(&[])
-    }
-
-    pub fn take(&mut self, id: ast::NodeId) -> Vec<EarlyLint> {
-        self.map.remove(&id).unwrap_or(vec![])
-    }
-
-    pub fn transfer(&mut self, into: &mut LintTable) {
-        into.map.extend(self.map.drain());
-    }
-
-    /// Returns the first (id, lint) pair that is non-empty. Used to
-    /// implement a sanity check in lints that all node-ids are
-    /// visited.
-    pub fn get_any(&self) -> Option<(&ast::NodeId, &Vec<EarlyLint>)> {
-        self.map.iter()
-                .filter(|&(_, v)| !v.is_empty())
-                .next()
-    }
-}
-
diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs
index a525b4e13b7..4e08bc90c7c 100644
--- a/src/librustc/middle/dead.rs
+++ b/src/librustc/middle/dead.rs
@@ -17,11 +17,11 @@ use hir::{self, Item_, PatKind};
 use hir::intravisit::{self, Visitor, NestedVisitorMap};
 use hir::itemlikevisit::ItemLikeVisitor;
 
-use middle::privacy;
-use ty::{self, TyCtxt};
 use hir::def::Def;
 use hir::def_id::{DefId, LOCAL_CRATE};
 use lint;
+use middle::privacy;
+use ty::{self, TyCtxt};
 use util::nodemap::FxHashSet;
 
 use syntax::{ast, codemap};
@@ -299,7 +299,9 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> {
     }
 }
 
-fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool {
+fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt,
+                                    id: ast::NodeId,
+                                    attrs: &[ast::Attribute]) -> bool {
     if attr::contains_name(attrs, "lang") {
         return true;
     }
@@ -315,14 +317,7 @@ fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool {
         return true;
     }
 
-    let dead_code = lint::builtin::DEAD_CODE.name_lower();
-    for attr in lint::gather_attrs(attrs) {
-        match attr {
-            Ok((name, lint::Allow, _)) if name == &*dead_code => return true,
-            _ => (),
-        }
-    }
-    false
+    tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow
 }
 
 // This visitor seeds items that
@@ -338,14 +333,17 @@ fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool {
 //   or
 //   2) We are not sure to be live or not
 //     * Implementation of a trait method
-struct LifeSeeder<'k> {
+struct LifeSeeder<'k, 'tcx: 'k> {
     worklist: Vec<ast::NodeId>,
     krate: &'k hir::Crate,
+    tcx: TyCtxt<'k, 'tcx, 'tcx>,
 }
 
-impl<'v, 'k> ItemLikeVisitor<'v> for LifeSeeder<'k> {
+impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> {
     fn visit_item(&mut self, item: &hir::Item) {
-        let allow_dead_code = has_allow_dead_code_or_lang_attr(&item.attrs);
+        let allow_dead_code = has_allow_dead_code_or_lang_attr(self.tcx,
+                                                               item.id,
+                                                               &item.attrs);
         if allow_dead_code {
             self.worklist.push(item.id);
         }
@@ -360,7 +358,9 @@ impl<'v, 'k> ItemLikeVisitor<'v> for LifeSeeder<'k> {
                     match trait_item.node {
                         hir::TraitItemKind::Const(_, Some(_)) |
                         hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(_)) => {
-                            if has_allow_dead_code_or_lang_attr(&trait_item.attrs) {
+                            if has_allow_dead_code_or_lang_attr(self.tcx,
+                                                                trait_item.id,
+                                                                &trait_item.attrs) {
                                 self.worklist.push(trait_item.id);
                             }
                         }
@@ -372,7 +372,9 @@ impl<'v, 'k> ItemLikeVisitor<'v> for LifeSeeder<'k> {
                 for impl_item_ref in impl_item_refs {
                     let impl_item = self.krate.impl_item(impl_item_ref.id);
                     if opt_trait.is_some() ||
-                            has_allow_dead_code_or_lang_attr(&impl_item.attrs) {
+                            has_allow_dead_code_or_lang_attr(self.tcx,
+                                                             impl_item.id,
+                                                             &impl_item.attrs) {
                         self.worklist.push(impl_item_ref.id.node_id);
                     }
                 }
@@ -408,6 +410,7 @@ fn create_and_seed_worklist<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let mut life_seeder = LifeSeeder {
         worklist,
         krate,
+        tcx,
     };
     krate.visit_all_item_likes(&mut life_seeder);
 
@@ -472,17 +475,19 @@ impl<'a, 'tcx> DeadVisitor<'a, 'tcx> {
         !field.is_positional()
             && !self.symbol_is_live(field.id, None)
             && !is_marker_field
-            && !has_allow_dead_code_or_lang_attr(&field.attrs)
+            && !has_allow_dead_code_or_lang_attr(self.tcx, field.id, &field.attrs)
     }
 
     fn should_warn_about_variant(&mut self, variant: &hir::Variant_) -> bool {
         !self.symbol_is_live(variant.data.id(), None)
-            && !has_allow_dead_code_or_lang_attr(&variant.attrs)
+            && !has_allow_dead_code_or_lang_attr(self.tcx,
+                                                 variant.data.id(),
+                                                 &variant.attrs)
     }
 
     fn should_warn_about_foreign_item(&mut self, fi: &hir::ForeignItem) -> bool {
         !self.symbol_is_live(fi.id, None)
-            && !has_allow_dead_code_or_lang_attr(&fi.attrs)
+            && !has_allow_dead_code_or_lang_attr(self.tcx, fi.id, &fi.attrs)
     }
 
     // id := node id of an item's definition.
@@ -528,11 +533,10 @@ impl<'a, 'tcx> DeadVisitor<'a, 'tcx> {
                       node_type: &str) {
         if !name.as_str().starts_with("_") {
             self.tcx
-                .sess
-                .add_lint(lint::builtin::DEAD_CODE,
-                          id,
-                          span,
-                          format!("{} is never used: `{}`", node_type, name));
+                .lint_node(lint::builtin::DEAD_CODE,
+                           id,
+                           span,
+                           &format!("{} is never used: `{}`", node_type, name));
         }
     }
 }
diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs
index abd5cbcb89e..fcf366788b2 100644
--- a/src/librustc/middle/effect.rs
+++ b/src/librustc/middle/effect.rs
@@ -56,11 +56,11 @@ impl<'a, 'tcx> EffectCheckVisitor<'a, 'tcx> {
         match self.unsafe_context.root {
             SafeContext => {
                 if is_lint {
-                    self.tcx.sess.add_lint(lint::builtin::SAFE_EXTERN_STATICS,
-                                           node_id,
-                                           span,
-                                           format!("{} requires unsafe function or \
-                                                    block (error E0133)", description));
+                    self.tcx.lint_node(lint::builtin::SAFE_EXTERN_STATICS,
+                                       node_id,
+                                       span,
+                                       &format!("{} requires unsafe function or \
+                                                 block (error E0133)", description));
                 } else {
                     // Report an error.
                     struct_span_err!(
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 551a550442b..070ad515908 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -1482,12 +1482,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                 };
 
                 if is_assigned {
-                    self.ir.tcx.sess.add_lint(lint::builtin::UNUSED_VARIABLES, id, sp,
-                        format!("variable `{}` is assigned to, but never used",
-                                name));
+                    self.ir.tcx.lint_node(lint::builtin::UNUSED_VARIABLES, id, sp,
+                        &format!("variable `{}` is assigned to, but never used",
+                                 name));
                 } else if name != "self" {
-                    self.ir.tcx.sess.add_lint(lint::builtin::UNUSED_VARIABLES, id, sp,
-                        format!("unused variable: `{}`", name));
+                    self.ir.tcx.lint_node(lint::builtin::UNUSED_VARIABLES, id, sp,
+                        &format!("unused variable: `{}`", name));
                 }
             }
             true
@@ -1509,11 +1509,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
     fn report_dead_assign(&self, id: NodeId, sp: Span, var: Variable, is_argument: bool) {
         if let Some(name) = self.should_warn(var) {
             if is_argument {
-                self.ir.tcx.sess.add_lint(lint::builtin::UNUSED_ASSIGNMENTS, id, sp,
-                    format!("value passed to `{}` is never read", name));
+                self.ir.tcx.lint_node(lint::builtin::UNUSED_ASSIGNMENTS, id, sp,
+                    &format!("value passed to `{}` is never read", name));
             } else {
-                self.ir.tcx.sess.add_lint(lint::builtin::UNUSED_ASSIGNMENTS, id, sp,
-                    format!("value assigned to `{}` is never read", name));
+                self.ir.tcx.lint_node(lint::builtin::UNUSED_ASSIGNMENTS, id, sp,
+                    &format!("value assigned to `{}` is never read", name));
             }
         }
     }
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index 68c01db544a..5158c7e94af 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -493,7 +493,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
                 format!("use of deprecated item")
             };
 
-            self.sess.add_lint(lint::builtin::DEPRECATED, id, span, msg);
+            self.lint_node(lint::builtin::DEPRECATED, id, span, &msg);
         };
 
         // Deprecated attributes apply in-crate and cross-crate.
@@ -737,10 +737,10 @@ pub fn check_unused_or_stable_features<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
     for &(ref stable_lang_feature, span) in &sess.features.borrow().declared_stable_lang_features {
         let version = find_lang_feature_accepted_version(&stable_lang_feature.as_str())
             .expect("unexpectedly couldn't find version feature was stabilized");
-        sess.add_lint(lint::builtin::STABLE_FEATURES,
+        tcx.lint_node(lint::builtin::STABLE_FEATURES,
                       ast::CRATE_NODE_ID,
                       span,
-                      format_stable_since_msg(version));
+                      &format_stable_since_msg(version));
     }
 
     let index = tcx.stability.borrow();
@@ -748,10 +748,10 @@ pub fn check_unused_or_stable_features<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
         match remaining_lib_features.remove(used_lib_feature) {
             Some(span) => {
                 if let &attr::StabilityLevel::Stable { since: ref version } = level {
-                    sess.add_lint(lint::builtin::STABLE_FEATURES,
+                    tcx.lint_node(lint::builtin::STABLE_FEATURES,
                                   ast::CRATE_NODE_ID,
                                   span,
-                                  format_stable_since_msg(&version.as_str()));
+                                  &format_stable_since_msg(&version.as_str()));
                 }
             }
             None => ( /* used but undeclared, handled during the previous ast visit */ )
@@ -759,9 +759,9 @@ pub fn check_unused_or_stable_features<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
     }
 
     for &span in remaining_lib_features.values() {
-        sess.add_lint(lint::builtin::UNUSED_FEATURES,
+        tcx.lint_node(lint::builtin::UNUSED_FEATURES,
                       ast::CRATE_NODE_ID,
                       span,
-                      "unused or unknown feature".to_string());
+                      "unused or unknown feature");
     }
 }
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index fe6b24f3e1f..be39f95b988 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -79,7 +79,7 @@ pub struct Session {
     // if the value stored here has been affected by path remapping.
     pub working_dir: (String, bool),
     pub lint_store: RefCell<lint::LintStore>,
-    pub lints: RefCell<lint::LintTable>,
+    pub buffered_lints: RefCell<Option<lint::LintBuffer>>,
     /// Set of (LintId, Option<Span>, message) tuples tracking lint
     /// (sub)diagnostics that have been set once, but should not be set again,
     /// in order to avoid redundantly verbose output (Issue #24690).
@@ -307,22 +307,15 @@ impl Session {
         self.diagnostic().unimpl(msg)
     }
 
-    pub fn add_lint<S: Into<MultiSpan>>(&self,
-                                        lint: &'static lint::Lint,
-                                        id: ast::NodeId,
-                                        sp: S,
-                                        msg: String)
-    {
-        self.lints.borrow_mut().add_lint(lint, id, sp, msg);
-    }
-
-    pub fn add_lint_diagnostic<M>(&self,
-                                  lint: &'static lint::Lint,
-                                  id: ast::NodeId,
-                                  msg: M)
-        where M: lint::IntoEarlyLint,
-    {
-        self.lints.borrow_mut().add_lint_diagnostic(lint, id, msg);
+    pub fn buffer_lint<S: Into<MultiSpan>>(&self,
+                                           lint: &'static lint::Lint,
+                                           id: ast::NodeId,
+                                           sp: S,
+                                           msg: &str) {
+        match *self.buffered_lints.borrow_mut() {
+            Some(ref mut buffer) => buffer.add_lint(lint, id, sp.into(), msg),
+            None => bug!("can't buffer lints after HIR lowering"),
+        }
     }
 
     pub fn reserve_node_ids(&self, count: usize) -> ast::NodeId {
@@ -708,7 +701,7 @@ pub fn build_session_(sopts: config::Options,
         local_crate_source_file,
         working_dir,
         lint_store: RefCell::new(lint::LintStore::new()),
-        lints: RefCell::new(lint::LintTable::new()),
+        buffered_lints: RefCell::new(Some(lint::LintBuffer::new())),
         one_time_diagnostics: RefCell::new(FxHashSet()),
         plugin_llvm_passes: RefCell::new(Vec::new()),
         plugin_attributes: RefCell::new(Vec::new()),
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 65c0a9f8ffd..f0fc6998c9e 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -499,11 +499,21 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         // weird effect -- the diagnostic is reported as a lint, and
         // the builder which is returned is marked as canceled.
 
-        let mut err =
-            struct_span_err!(self.tcx.sess,
-                             error_span,
-                             E0276,
-                             "impl has stricter requirements than trait");
+        let msg = "impl has stricter requirements than trait";
+        let mut err = match lint_id {
+            Some(node_id) => {
+                self.tcx.struct_span_lint_node(EXTRA_REQUIREMENT_IN_IMPL,
+                                               node_id,
+                                               error_span,
+                                               msg)
+            }
+            None => {
+                struct_span_err!(self.tcx.sess,
+                                 error_span,
+                                 E0276,
+                                 "{}", msg)
+            }
+        };
 
         if let Some(trait_item_span) = self.tcx.hir.span_if_local(trait_item_def_id) {
             let span = self.tcx.sess.codemap().def_span(trait_item_span);
@@ -514,13 +524,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             error_span,
             format!("impl has extra requirement {}", requirement));
 
-        if let Some(node_id) = lint_id {
-            self.tcx.sess.add_lint_diagnostic(EXTRA_REQUIREMENT_IN_IMPL,
-                                              node_id,
-                                              (*err).clone());
-            err.cancel();
-        }
-
         err
     }
 
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index c2feb54c4db..e843ba35ce5 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -512,11 +512,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
             }
 
             if raise_warning {
-                tcx.sess.add_lint(lint::builtin::RESOLVE_TRAIT_ON_DEFAULTED_UNIT,
-                                  obligation.cause.body_id,
-                                  obligation.cause.span,
-                                  format!("code relies on type inference rules which are likely \
-                                           to change"));
+                tcx.lint_node(lint::builtin::RESOLVE_TRAIT_ON_DEFAULTED_UNIT,
+                              obligation.cause.body_id,
+                              obligation.cause.span,
+                              &format!("code relies on type inference rules which are likely \
+                                        to change"));
             }
         }
         Ok(ret)
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index be3cd99426d..6b9cbabf20e 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -11,14 +11,15 @@
 //! type context book-keeping
 
 use dep_graph::DepGraph;
+use errors::DiagnosticBuilder;
 use session::Session;
-use lint;
 use middle;
 use hir::TraitMap;
 use hir::def::{Def, ExportMap};
 use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
 use hir::map as hir_map;
 use hir::map::DefPathHash;
+use lint::{self, Lint};
 use middle::free_region::FreeRegionMap;
 use middle::lang_items;
 use middle::resolve_lifetime;
@@ -58,6 +59,7 @@ use std::rc::Rc;
 use syntax::abi;
 use syntax::ast::{self, Name, NodeId};
 use syntax::attr;
+use syntax::codemap::MultiSpan;
 use syntax::symbol::{Symbol, keywords};
 use syntax_pos::Span;
 
@@ -254,9 +256,6 @@ pub struct TypeckTables<'tcx> {
     /// *from* expression of the cast, not the cast itself.
     pub cast_kinds: NodeMap<ty::cast::CastKind>,
 
-    /// Lints for the body of this fn generated by typeck.
-    pub lints: lint::LintTable,
-
     /// Set of trait imports actually used in the method resolution.
     /// This is used for warning unused imports.
     pub used_trait_imports: DefIdSet,
@@ -285,7 +284,6 @@ impl<'tcx> TypeckTables<'tcx> {
             liberated_fn_sigs: NodeMap(),
             fru_field_types: NodeMap(),
             cast_kinds: NodeMap(),
-            lints: lint::LintTable::new(),
             used_trait_imports: DefIdSet(),
             tainted_by_errors: false,
             free_region_map: FreeRegionMap::new(),
@@ -1515,6 +1513,59 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     {
         self.mk_substs(iter::once(s).chain(t.into_iter().cloned()).map(Kind::from))
     }
+
+    pub fn lint_node<S: Into<MultiSpan>>(self,
+                                         lint: &'static Lint,
+                                         id: NodeId,
+                                         span: S,
+                                         msg: &str) {
+        self.struct_span_lint_node(lint, id, span.into(), msg).emit()
+    }
+
+    pub fn lint_level_at_node(self, lint: &'static Lint, mut id: NodeId)
+        -> (lint::Level, lint::LintSource)
+    {
+        // Right now we insert a `with_ignore` node in the dep graph here to
+        // ignore the fact that `lint_levels` below depends on the entire crate.
+        // For now this'll prevent false positives of recompiling too much when
+        // anything changes.
+        //
+        // Once red/green incremental compilation lands we should be able to
+        // remove this because while the crate changes often the lint level map
+        // will change rarely.
+        self.dep_graph.with_ignore(|| {
+            let sets = self.lint_levels(LOCAL_CRATE);
+            loop {
+                let hir_id = self.hir.definitions().node_to_hir_id(id);
+                if let Some(pair) = sets.level_and_source(lint, hir_id) {
+                    return pair
+                }
+                let next = self.hir.get_parent_node(id);
+                if next == id {
+                    bug!("lint traversal reached the root of the crate");
+                }
+                id = next;
+            }
+        })
+    }
+
+    pub fn struct_span_lint_node<S: Into<MultiSpan>>(self,
+                                                     lint: &'static Lint,
+                                                     id: NodeId,
+                                                     span: S,
+                                                     msg: &str)
+        -> DiagnosticBuilder<'tcx>
+    {
+        let (level, src) = self.lint_level_at_node(lint, id);
+        lint::struct_lint_level(self.sess, lint, level, src, Some(span.into()), msg)
+    }
+
+    pub fn struct_lint_node(self, lint: &'static Lint, id: NodeId, msg: &str)
+        -> DiagnosticBuilder<'tcx>
+    {
+        let (level, src) = self.lint_level_at_node(lint, id);
+        lint::struct_lint_level(self.sess, lint, level, src, None, msg)
+    }
 }
 
 pub trait InternAs<T: ?Sized, R> {
diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs
index 5d25ce2fbec..b871b36c948 100644
--- a/src/librustc/ty/maps.rs
+++ b/src/librustc/ty/maps.rs
@@ -12,6 +12,7 @@ use dep_graph::{DepConstructor, DepNode, DepNodeIndex};
 use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
 use hir::def::Def;
 use hir;
+use lint;
 use middle::const_val;
 use middle::cstore::{ExternCrate, LinkagePreference};
 use middle::privacy::AccessLevels;
@@ -506,6 +507,12 @@ impl<'tcx> QueryDescription for queries::extern_crate<'tcx> {
     }
 }
 
+impl<'tcx> QueryDescription for queries::lint_levels<'tcx> {
+    fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
+        format!("computing the lint levels for items in this crate")
+    }
+}
+
 macro_rules! define_maps {
     (<$tcx:tt>
      $($(#[$attr:meta])*
@@ -988,6 +995,8 @@ define_maps! { <'tcx>
     [] is_panic_runtime: IsPanicRuntime(DefId) -> bool,
 
     [] extern_crate: ExternCrate(DefId) -> Rc<Option<ExternCrate>>,
+
+    [] lint_levels: lint_levels(CrateNum) -> Rc<lint::LintLevelMap>,
 }
 
 fn type_param_predicates<'tcx>((item_id, param_id): (DefId, DefId)) -> DepConstructor<'tcx> {
@@ -1059,3 +1068,7 @@ fn needs_drop_dep_node<'tcx>(_: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstruct
 fn layout_dep_node<'tcx>(_: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> DepConstructor<'tcx> {
     DepConstructor::Layout
 }
+
+fn lint_levels<'tcx>(_: CrateNum) -> DepConstructor<'tcx> {
+    DepConstructor::LintLevels
+}
diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs
index 060ff503d4e..a3f8aae472c 100644
--- a/src/librustc_const_eval/check_match.rs
+++ b/src/librustc_const_eval/check_match.rs
@@ -23,7 +23,7 @@ use rustc::session::Session;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::subst::Substs;
 use rustc::lint;
-use rustc_errors::{Diagnostic, Level, DiagnosticBuilder};
+use rustc_errors::DiagnosticBuilder;
 
 use rustc::hir::def::*;
 use rustc::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap};
@@ -351,12 +351,10 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
                             match arm_index {
                                 // The arm with the user-specified pattern.
                                 0 => {
-                                    let mut diagnostic = Diagnostic::new(Level::Warning,
-                                                                         "unreachable pattern");
-                                    diagnostic.set_span(pat.span);
-                                    cx.tcx.sess.add_lint_diagnostic(
+                                    cx.tcx.lint_node(
                                             lint::builtin::UNREACHABLE_PATTERNS,
-                                            hir_pat.id, diagnostic);
+                                        hir_pat.id, pat.span,
+                                        "unreachable pattern");
                                 },
                                 // The arm with the wildcard pattern.
                                 1 => {
@@ -371,16 +369,18 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
 
                         hir::MatchSource::ForLoopDesugar |
                         hir::MatchSource::Normal => {
-                            let mut diagnostic = Diagnostic::new(Level::Warning,
-                                                                 "unreachable pattern");
-                            diagnostic.set_span(pat.span);
+                            let mut err = cx.tcx.struct_span_lint_node(
+                                lint::builtin::UNREACHABLE_PATTERNS,
+                                hir_pat.id,
+                                pat.span,
+                                "unreachable pattern",
+                            );
                             // if we had a catchall pattern, hint at that
                             if let Some(catchall) = catchall {
-                                diagnostic.span_label(pat.span, "this is an unreachable pattern");
-                                diagnostic.span_note(catchall, "this pattern matches any value");
+                                err.span_label(pat.span, "this is an unreachable pattern");
+                                err.span_note(catchall, "this pattern matches any value");
                             }
-                            cx.tcx.sess.add_lint_diagnostic(lint::builtin::UNREACHABLE_PATTERNS,
-                                                            hir_pat.id, diagnostic);
+                            err.emit();
                         },
 
                         // Unreachable patterns in try expressions occur when one of the arms
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 12cb556afda..3c01d4bafab 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -638,7 +638,6 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
         super::describe_lints(&sess.lint_store.borrow(), true);
         return Err(CompileIncomplete::Stopped);
     }
-    sess.track_errors(|| sess.lint_store.borrow_mut().process_command_line(sess))?;
 
     // Currently, we ignore the name resolution data structures for the purposes of dependency
     // tracking. Instead we will run name resolution and include its output in the hash of each
@@ -708,8 +707,8 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
         missing_fragment_specifiers.sort();
         for span in missing_fragment_specifiers {
             let lint = lint::builtin::MISSING_FRAGMENT_SPECIFIER;
-            let msg = "missing fragment specifier".to_string();
-            sess.add_lint(lint, ast::CRATE_NODE_ID, span, msg);
+            let msg = "missing fragment specifier";
+            sess.buffer_lint(lint, ast::CRATE_NODE_ID, span, msg);
         }
         if ecx.parse_sess.span_diagnostic.err_count() - ecx.resolve_err_count > err_count {
             ecx.parse_sess.span_diagnostic.abort_if_errors();
@@ -773,10 +772,6 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
          || no_asm::check_crate(sess, &krate));
 
     time(time_passes,
-         "early lint checks",
-         || lint::check_ast_crate(sess, &krate));
-
-    time(time_passes,
          "AST validation",
          || ast_validation::check_crate(sess, &krate));
 
@@ -800,6 +795,10 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
         })
     })?;
 
+    time(time_passes,
+         "early lint checks",
+         || lint::check_ast_crate(sess, &krate));
+
     // Lower ast -> hir.
     let hir_forest = time(time_passes, "lowering ast -> hir", || {
         let hir_crate = lower_crate(sess, &krate, &mut resolver);
@@ -908,6 +907,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
     rustc_const_eval::provide(&mut local_providers);
     middle::region::provide(&mut local_providers);
     cstore::provide_local(&mut local_providers);
+    lint::provide(&mut local_providers);
 
     let mut extern_providers = ty::maps::Providers::default();
     cstore::provide(&mut extern_providers);
@@ -1194,10 +1194,10 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<c
                          }
                          Some(ref n) if *n == "bin" => Some(config::CrateTypeExecutable),
                          Some(_) => {
-                             session.add_lint(lint::builtin::UNKNOWN_CRATE_TYPES,
-                                              ast::CRATE_NODE_ID,
-                                              a.span,
-                                              "invalid `crate_type` value".to_string());
+                             session.buffer_lint(lint::builtin::UNKNOWN_CRATE_TYPES,
+                                                 ast::CRATE_NODE_ID,
+                                                 a.span,
+                                                 "invalid `crate_type` value");
                              None
                          }
                          _ => {
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index db27fa874f4..88432e64290 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -36,7 +36,7 @@ use rustc::ty::{self, Ty};
 use rustc::traits::{self, Reveal};
 use rustc::hir::map as hir_map;
 use util::nodemap::NodeSet;
-use lint::{Level, LateContext, LintContext, LintArray};
+use lint::{LateContext, LintContext, LintArray};
 use lint::{LintPass, LateLintPass, EarlyLintPass, EarlyContext};
 
 use std::collections::HashSet;
@@ -876,16 +876,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnconditionalRecursion {
             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 {
-                // offer some help to the programmer.
-                for call in &self_call_spans {
-                    db.span_note(*call, "recursive call site");
-                }
-                db.help("a `loop` may express intention \
-                         better if this is on purpose");
+            // offer some help to the programmer.
+            for call in &self_call_spans {
+                db.span_note(*call, "recursive call site");
             }
+            db.help("a `loop` may express intention \
+                     better if this is on purpose");
             db.emit();
         }
 
diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs
index 38d8555334c..2643ed2a3c0 100644
--- a/src/librustc_passes/ast_validation.rs
+++ b/src/librustc_passes/ast_validation.rs
@@ -237,10 +237,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         self.check_trait_fn_not_const(sig.constness);
                         if block.is_none() {
                             self.check_decl_no_pat(&sig.decl, |span, _| {
-                                self.session.add_lint(lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY,
-                                                      trait_item.id, span,
-                                                      "patterns aren't allowed in methods \
-                                                       without bodies".to_string());
+                                self.session.buffer_lint(
+                                    lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY,
+                                    trait_item.id, span,
+                                    "patterns aren't allowed in methods \
+                                     without bodies");
                             });
                         }
                     }
@@ -252,7 +253,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 if item.attrs.iter().any(|attr| attr.check_name("warn_directory_ownership")) {
                     let lint = lint::builtin::LEGACY_DIRECTORY_OWNERSHIP;
                     let msg = "cannot declare a new module at this location";
-                    self.session.add_lint(lint, item.id, item.span, msg.to_string());
+                    self.session.buffer_lint(lint, item.id, item.span, msg);
                 }
             }
             ItemKind::Union(ref vdata, _) => {
diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs
index a881bf9eac7..8443cc8267d 100644
--- a/src/librustc_passes/consts.rs
+++ b/src/librustc_passes/consts.rs
@@ -76,12 +76,12 @@ impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
                 ErroneousReferencedConstant(_) => {}
                 TypeckError => {}
                 _ => {
-                    self.tcx.sess.add_lint(CONST_ERR,
-                                           expr.id,
-                                           expr.span,
-                                           format!("constant evaluation error: {}. This will \
-                                                    become a HARD ERROR in the future",
-                                                   err.description().into_oneline()))
+                    self.tcx.lint_node(CONST_ERR,
+                                       expr.id,
+                                       expr.span,
+                                       &format!("constant evaluation error: {}. This will \
+                                                 become a HARD ERROR in the future",
+                                                err.description().into_oneline()));
                 }
             }
         }
@@ -260,10 +260,10 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
                     kind: LayoutError(ty::layout::LayoutError::Unknown(_)), ..
                 }) => {}
                 Err(msg) => {
-                    self.tcx.sess.add_lint(CONST_ERR,
-                                           ex.id,
-                                           msg.span,
-                                           msg.description().into_oneline().into_owned())
+                    self.tcx.lint_node(CONST_ERR,
+                                       ex.id,
+                                       msg.span,
+                                       &msg.description().into_oneline().into_owned());
                 }
             }
         }
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index 64af24d92ee..180d3dedf4b 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -1345,11 +1345,11 @@ impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
                                     "private trait can't be public"))
                         .emit();
                 } else {
-                    self.tcx.sess.add_lint(lint::builtin::PRIVATE_IN_PUBLIC,
-                                           node_id,
-                                           self.span,
-                                           format!("private trait `{}` in public \
-                                                    interface (error E0445)", trait_ref));
+                    self.tcx.lint_node(lint::builtin::PRIVATE_IN_PUBLIC,
+                                       node_id,
+                                       self.span,
+                                       &format!("private trait `{}` in public \
+                                                 interface (error E0445)", trait_ref));
                 }
             }
         }
@@ -1393,11 +1393,11 @@ impl<'a, 'tcx: 'a> TypeVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<'
                         err.span_label(self.span, "can't leak private type");
                         err.emit();
                     } else {
-                        self.tcx.sess.add_lint(lint::builtin::PRIVATE_IN_PUBLIC,
-                                               node_id,
-                                               self.span,
-                                               format!("private type `{}` in public \
-                                                        interface (error E0446)", ty));
+                        self.tcx.lint_node(lint::builtin::PRIVATE_IN_PUBLIC,
+                                           node_id,
+                                           self.span,
+                                           &format!("private type `{}` in public \
+                                                     interface (error E0446)", ty));
                     }
                 }
             }
diff --git a/src/librustc_resolve/check_unused.rs b/src/librustc_resolve/check_unused.rs
index d150ff1ff81..a8bb6619bbd 100644
--- a/src/librustc_resolve/check_unused.rs
+++ b/src/librustc_resolve/check_unused.rs
@@ -122,13 +122,13 @@ pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) {
                  directive.span.source_equal(&DUMMY_SP) => {}
             ImportDirectiveSubclass::ExternCrate => {
                 let lint = lint::builtin::UNUSED_EXTERN_CRATES;
-                let msg = "unused extern crate".to_string();
-                resolver.session.add_lint(lint, directive.id, directive.span, msg);
+                let msg = "unused extern crate";
+             ;   resolver.session.buffer_lint(lint, directive.id, directive.span, msg)
             }
             ImportDirectiveSubclass::MacroUse => {
                 let lint = lint::builtin::UNUSED_IMPORTS;
-                let msg = "unused `#[macro_use]` import".to_string();
-                resolver.session.add_lint(lint, directive.id, directive.span, msg);
+                let msg = "unused `#[macro_use]` import";
+                resolver.session.buffer_lint(lint, directive.id, directive.span, msg);
             }
             _ => {}
         }
@@ -160,9 +160,6 @@ pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) {
                           } else {
                               String::new()
                           });
-        visitor.session.add_lint(lint::builtin::UNUSED_IMPORTS,
-                                 *id,
-                                 ms,
-                                 msg);
+        visitor.session.buffer_lint(lint::builtin::UNUSED_IMPORTS, *id, ms, &msg);
     }
 }
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 2317e36a0ab..b4f9ba4e8f7 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -2552,9 +2552,10 @@ impl<'a> Resolver<'a> {
                                 = self.struct_constructors.get(&def_id).cloned() {
                             if is_expected(ctor_def) && self.is_accessible(ctor_vis) {
                                 let lint = lint::builtin::LEGACY_CONSTRUCTOR_VISIBILITY;
-                                self.session.add_lint(lint, id, span,
+                                self.session.buffer_lint(lint, id, span,
                                     "private struct constructors are not usable through \
-                                     reexports in outer modules".to_string());
+                                     reexports in outer modules",
+                                );
                                 res = Some(PathResolution::new(ctor_def));
                             }
                         }
@@ -2748,7 +2749,7 @@ impl<'a> Resolver<'a> {
             };
             if result.base_def() == unqualified_result {
                 let lint = lint::builtin::UNUSED_QUALIFICATIONS;
-                self.session.add_lint(lint, id, span, "unnecessary qualification".to_string());
+                self.session.buffer_lint(lint, id, span, "unnecessary qualification")
             }
         }
 
@@ -3486,7 +3487,7 @@ impl<'a> Resolver<'a> {
                 span.push_span_label(b1.span, msg1);
                 span.push_span_label(b2.span, msg2);
                 let msg = format!("`{}` is ambiguous", name);
-                self.session.add_lint(lint::builtin::LEGACY_IMPORTS, id, span, msg);
+                self.session.buffer_lint(lint::builtin::LEGACY_IMPORTS, id, span, &msg);
             } else {
                 let mut err =
                     self.session.struct_span_err(span, &format!("`{}` is ambiguous", name));
@@ -3607,8 +3608,8 @@ impl<'a> Resolver<'a> {
 
     fn warn_legacy_self_import(&self, directive: &'a ImportDirective<'a>) {
         let (id, span) = (directive.id, directive.span);
-        let msg = "`self` no longer imports values".to_string();
-        self.session.add_lint(lint::builtin::LEGACY_IMPORTS, id, span, msg);
+        let msg = "`self` no longer imports values";
+        self.session.buffer_lint(lint::builtin::LEGACY_IMPORTS, id, span, msg);
     }
 
     fn check_proc_macro_attrs(&mut self, attrs: &[ast::Attribute]) {
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index 4d4f6aadce4..98eaa056177 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -319,8 +319,8 @@ impl<'a> base::Resolver for Resolver<'a> {
             };
             if let Some((id, span)) = id_span {
                 let lint = lint::builtin::UNUSED_MACROS;
-                let msg = "unused macro definition".to_string();
-                self.session.add_lint(lint, id, span, msg);
+                let msg = "unused macro definition";
+                self.session.buffer_lint(lint, id, span, msg);
             } else {
                 bug!("attempted to create unused macro error, but span not available");
             }
diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs
index 5e799b14f20..984ef3a44e1 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -745,8 +745,10 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
                 let msg = format!("extern crate `{}` is private, and cannot be reexported \
                                    (error E0365), consider declaring with `pub`",
                                    ident);
-                self.session.add_lint(PUB_USE_OF_PRIVATE_EXTERN_CRATE,
-                                      directive.id, directive.span, msg);
+                self.session.buffer_lint(PUB_USE_OF_PRIVATE_EXTERN_CRATE,
+                                         directive.id,
+                                         directive.span,
+                                         &msg);
             } else if ns == TypeNS {
                 struct_span_err!(self.session, directive.span, E0365,
                                  "`{}` is private, and cannot be reexported", ident)
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 1ec850ad7f3..2910d25486e 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -985,9 +985,9 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                     .span_label(data.span, "only traits may use parentheses")
                     .emit();
             } else {
-                let msg = "parenthesized parameters may only be used with a trait".to_string();
-                self.tcx().sess.add_lint(PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES,
-                                         ast::CRATE_NODE_ID, data.span, msg);
+                let msg = "parenthesized parameters may only be used with a trait";
+                self.tcx().lint_node(PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES,
+                                     ast::CRATE_NODE_ID, data.span, msg);
             }
         }
     }
diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs
index b3f62de5b57..5f256eab9a9 100644
--- a/src/librustc_typeck/check/cast.rs
+++ b/src/librustc_typeck/check/cast.rs
@@ -291,25 +291,25 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
         let t_cast = self.cast_ty;
         let t_expr = self.expr_ty;
         if t_cast.is_numeric() && t_expr.is_numeric() {
-            fcx.tables.borrow_mut().lints.add_lint(
+            fcx.tcx.lint_node(
                 lint::builtin::TRIVIAL_NUMERIC_CASTS,
                 self.expr.id,
                 self.span,
-                format!("trivial numeric cast: `{}` as `{}`. Cast can be \
-                         replaced by coercion, this might require type \
-                         ascription or a temporary variable",
-                        fcx.ty_to_string(t_expr),
-                        fcx.ty_to_string(t_cast)));
+                &format!("trivial numeric cast: `{}` as `{}`. Cast can be \
+                          replaced by coercion, this might require type \
+                          ascription or a temporary variable",
+                         fcx.ty_to_string(t_expr),
+                         fcx.ty_to_string(t_cast)));
         } else {
-            fcx.tables.borrow_mut().lints.add_lint(
+            fcx.tcx.lint_node(
                 lint::builtin::TRIVIAL_CASTS,
                 self.expr.id,
                 self.span,
-                format!("trivial cast: `{}` as `{}`. Cast can be \
-                         replaced by coercion, this might require type \
-                         ascription or a temporary variable",
-                        fcx.ty_to_string(t_expr),
-                        fcx.ty_to_string(t_cast)));
+                &format!("trivial cast: `{}` as `{}`. Cast can be \
+                          replaced by coercion, this might require type \
+                          ascription or a temporary variable",
+                         fcx.ty_to_string(t_expr),
+                         fcx.ty_to_string(t_cast)));
         }
 
     }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index e53e5e7b08c..6c9a6524d67 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1713,10 +1713,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
             debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
 
-            self.tables.borrow_mut().lints.add_lint(
+            self.tcx().lint_node(
                 lint::builtin::UNREACHABLE_CODE,
                 id, span,
-                format!("unreachable {}", kind));
+                &format!("unreachable {}", kind));
         }
     }
 
@@ -4746,8 +4746,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             } else {
                 let mut multispan = MultiSpan::from_span(lifetimes[0].span);
                 multispan.push_span_label(span_late, note_msg.to_string());
-                self.tcx.sess.add_lint(lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS,
-                                       lifetimes[0].id, multispan, primary_msg.to_string());
+                self.tcx.lint_node(lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS,
+                                   lifetimes[0].id, multispan, primary_msg);
             }
             return;
         }
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index 0a323efabec..9e5cf5137c2 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -43,7 +43,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         wbcx.visit_fru_field_types();
         wbcx.visit_anon_types();
         wbcx.visit_cast_types();
-        wbcx.visit_lints();
         wbcx.visit_free_region_map();
 
         let used_trait_imports = mem::replace(&mut self.tables.borrow_mut().used_trait_imports,
@@ -234,10 +233,6 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
             self.fcx.tables.borrow().cast_kinds.iter().map(|(&key, &value)| (key, value)));
     }
 
-    fn visit_lints(&mut self) {
-        self.fcx.tables.borrow_mut().lints.transfer(&mut self.tables.lints);
-    }
-
     fn visit_free_region_map(&mut self) {
         let free_region_map = self.tcx().lift_to_global(&self.fcx.tables.borrow().free_region_map);
         let free_region_map = free_region_map.expect("all regions in free-region-map are global");
diff --git a/src/librustc_typeck/check_unused.rs b/src/librustc_typeck/check_unused.rs
index 1af55d4d840..e95d49f00bf 100644
--- a/src/librustc_typeck/check_unused.rs
+++ b/src/librustc_typeck/check_unused.rs
@@ -39,7 +39,7 @@ impl<'a, 'tcx> CheckVisitor<'a, 'tcx> {
         } else {
             "unused import".to_string()
         };
-        self.tcx.sess.add_lint(lint::builtin::UNUSED_IMPORTS, id, span, msg);
+        self.tcx.lint_node(lint::builtin::UNUSED_IMPORTS, id, span, &msg);
     }
 }
 
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index f25a6cf58a7..fba32dbb889 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -999,12 +999,12 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
         if !allow_defaults && p.default.is_some() {
             if !tcx.sess.features.borrow().default_type_parameter_fallback {
-                tcx.sess.add_lint(
+                tcx.lint_node(
                     lint::builtin::INVALID_TYPE_PARAM_DEFAULT,
                     p.id,
                     p.span,
-                    format!("defaults for type parameters are only allowed in `struct`, \
-                             `enum`, `type`, or `trait` definitions."));
+                    &format!("defaults for type parameters are only allowed in `struct`, \
+                              `enum`, `type`, or `trait` definitions."));
             }
         }
 
diff --git a/src/test/compile-fail-fulldeps/proc-macro/attributes-included.rs b/src/test/compile-fail-fulldeps/proc-macro/attributes-included.rs
index 508f8dac571..f42d1006bf5 100644
--- a/src/test/compile-fail-fulldeps/proc-macro/attributes-included.rs
+++ b/src/test/compile-fail-fulldeps/proc-macro/attributes-included.rs
@@ -11,6 +11,7 @@
 // aux-build:attributes-included.rs
 
 #![feature(proc_macro, rustc_attrs)]
+#![warn(unused)]
 
 extern crate attributes_included;
 
diff --git a/src/test/compile-fail/E0010.rs b/src/test/compile-fail/E0010.rs
index 8a666168c86..ccaf01932d4 100644
--- a/src/test/compile-fail/E0010.rs
+++ b/src/test/compile-fail/E0010.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 #![feature(box_syntax)]
+#![allow(warnings)]
 
 const CON : Box<i32> = box 0; //~ ERROR E0010
                               //~| NOTE allocation not allowed in
diff --git a/src/test/compile-fail/E0394.rs b/src/test/compile-fail/E0394.rs
index e35d038248c..c7d5665cd2c 100644
--- a/src/test/compile-fail/E0394.rs
+++ b/src/test/compile-fail/E0394.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![allow(warnings)]
+
 static A: u32 = 0;
 static B: u32 = A;
 //~^ ERROR E0394
diff --git a/src/test/compile-fail/bad-lint-cap2.rs b/src/test/compile-fail/bad-lint-cap2.rs
index 5a97d7b1a79..cb9fb973a01 100644
--- a/src/test/compile-fail/bad-lint-cap2.rs
+++ b/src/test/compile-fail/bad-lint-cap2.rs
@@ -10,6 +10,7 @@
 
 // compile-flags: --cap-lints deny
 
+#![warn(unused)]
 #![deny(warnings)]
 
 use std::option; //~ ERROR
diff --git a/src/test/compile-fail/bad-lint-cap3.rs b/src/test/compile-fail/bad-lint-cap3.rs
index e03ba6ecb64..c9394954c5f 100644
--- a/src/test/compile-fail/bad-lint-cap3.rs
+++ b/src/test/compile-fail/bad-lint-cap3.rs
@@ -10,6 +10,7 @@
 
 // compile-flags: --cap-lints warn
 
+#![warn(unused)]
 #![deny(warnings)]
 #![feature(rustc_attrs)]
 
diff --git a/src/test/compile-fail/check-static-values-constraints.rs b/src/test/compile-fail/check-static-values-constraints.rs
index f65968e0e11..3642add3259 100644
--- a/src/test/compile-fail/check-static-values-constraints.rs
+++ b/src/test/compile-fail/check-static-values-constraints.rs
@@ -12,6 +12,7 @@
 
 // gate-test-drop_types_in_const
 
+#![allow(warnings)]
 #![feature(box_syntax)]
 
 use std::marker;
diff --git a/src/test/compile-fail/const-eval-overflow-2.rs b/src/test/compile-fail/const-eval-overflow-2.rs
index 9b045ed1d02..0fd41a17b2c 100644
--- a/src/test/compile-fail/const-eval-overflow-2.rs
+++ b/src/test/compile-fail/const-eval-overflow-2.rs
@@ -11,7 +11,7 @@
 // Evaluation of constants in refutable patterns goes through
 // different compiler control-flow paths.
 
-#![allow(unused_imports)]
+#![allow(unused_imports, warnings)]
 
 use std::fmt;
 use std::{i8, i16, i32, i64, isize};
diff --git a/src/test/compile-fail/const-match-pattern-arm.rs b/src/test/compile-fail/const-match-pattern-arm.rs
index 452aa87d6ba..bc944948f3d 100644
--- a/src/test/compile-fail/const-match-pattern-arm.rs
+++ b/src/test/compile-fail/const-match-pattern-arm.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![allow(warnings)]
+
 const x: bool = match Some(true) {
     Some(value) => true,
     //~^ ERROR: constant contains unimplemented expression type [E0019]
diff --git a/src/test/compile-fail/feature-gate-dropck-ugeh.rs b/src/test/compile-fail/feature-gate-dropck-ugeh.rs
index d9664fda2b3..360895d30b0 100644
--- a/src/test/compile-fail/feature-gate-dropck-ugeh.rs
+++ b/src/test/compile-fail/feature-gate-dropck-ugeh.rs
@@ -28,7 +28,6 @@ struct Foo<T> { data: Vec<T> }
 impl<T> Drop for Foo<T> {
     #[unsafe_destructor_blind_to_params] // This is the UGEH attribute
     //~^ ERROR unsafe_destructor_blind_to_params has been replaced
-    //~^^ WARN: use of deprecated attribute
     fn drop(&mut self) { }
 }
 
diff --git a/src/test/compile-fail/issue-14227.rs b/src/test/compile-fail/issue-14227.rs
index c4846a64f29..d8f9f5543e4 100644
--- a/src/test/compile-fail/issue-14227.rs
+++ b/src/test/compile-fail/issue-14227.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![allow(safe_extern_statics, warnings)]
+
 extern {
     pub static symbol: ();
 }
diff --git a/src/test/compile-fail/issue-16538.rs b/src/test/compile-fail/issue-16538.rs
index 6c41450796c..08c3f7a7c15 100644
--- a/src/test/compile-fail/issue-16538.rs
+++ b/src/test/compile-fail/issue-16538.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![allow(safe_extern_statics)]
+
 mod Y {
     pub type X = usize;
     extern {
diff --git a/src/test/compile-fail/issue-17450.rs b/src/test/compile-fail/issue-17450.rs
index 5471d8522df..cde1bbbe492 100644
--- a/src/test/compile-fail/issue-17450.rs
+++ b/src/test/compile-fail/issue-17450.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![allow(dead_code)]
+#![allow(dead_code, warnings)]
 
 static mut x: isize = 3;
 static mut y: isize = unsafe {
diff --git a/src/test/compile-fail/issue-17718-const-naming.rs b/src/test/compile-fail/issue-17718-const-naming.rs
index 06719e2756b..4857c2fb446 100644
--- a/src/test/compile-fail/issue-17718-const-naming.rs
+++ b/src/test/compile-fail/issue-17718-const-naming.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![warn(unused)]
 #[deny(warnings)]
 
 const foo: isize = 3;
diff --git a/src/test/compile-fail/issue-17718-references.rs b/src/test/compile-fail/issue-17718-references.rs
index c159168030b..8e0df283cdb 100644
--- a/src/test/compile-fail/issue-17718-references.rs
+++ b/src/test/compile-fail/issue-17718-references.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![allow(warnings)]
+
 struct Struct { a: usize }
 
 const C: usize = 1;
diff --git a/src/test/compile-fail/issue-18937.rs b/src/test/compile-fail/issue-18937.rs
index f57f3477a8a..5996c8e5438 100644
--- a/src/test/compile-fail/issue-18937.rs
+++ b/src/test/compile-fail/issue-18937.rs
@@ -26,7 +26,7 @@ trait A<'a> {
 }
 
 impl<'a> A<'a> for B {
-    fn foo<F>(&mut self, f: F) //~ ERROR E0276
+    fn foo<F>(&mut self, f: F) //~ ERROR impl has stricter
         //~^ WARNING future release
         where F: fmt::Debug + 'static,
     {
diff --git a/src/test/compile-fail/issue-28075.rs b/src/test/compile-fail/issue-28075.rs
index cd73a456411..057c99f9305 100644
--- a/src/test/compile-fail/issue-28075.rs
+++ b/src/test/compile-fail/issue-28075.rs
@@ -12,7 +12,7 @@
 
 // aux-build:lint_stability.rs
 
-#![allow(unused_imports)]
+#![allow(warnings)]
 
 extern crate lint_stability;
 
diff --git a/src/test/compile-fail/issue-28113.rs b/src/test/compile-fail/issue-28113.rs
index 5c697b69c80..7d2541966a4 100644
--- a/src/test/compile-fail/issue-28113.rs
+++ b/src/test/compile-fail/issue-28113.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![allow(warnings)]
+
 const X: u8 =
     || -> u8 { 5 }()
     //~^ ERROR calls in constants are limited to constant functions
diff --git a/src/test/compile-fail/issue-28324.rs b/src/test/compile-fail/issue-28324.rs
index 13ce41f4dcc..3c4d6b42b50 100644
--- a/src/test/compile-fail/issue-28324.rs
+++ b/src/test/compile-fail/issue-28324.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![allow(safe_extern_statics)]
+
 extern {
     static error_message_count: u32;
 }
diff --git a/src/test/compile-fail/issue-30730.rs b/src/test/compile-fail/issue-30730.rs
index 086938334c7..d1af39a6c18 100644
--- a/src/test/compile-fail/issue-30730.rs
+++ b/src/test/compile-fail/issue-30730.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![warn(unused)]
 #![deny(warnings)] //~ NOTE: lint level defined here
 use std::thread;
 //~^ ERROR: unused import
diff --git a/src/test/compile-fail/issue-37515.rs b/src/test/compile-fail/issue-37515.rs
index fa452d6e74a..d5733f98193 100644
--- a/src/test/compile-fail/issue-37515.rs
+++ b/src/test/compile-fail/issue-37515.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 #![feature(rustc_attrs)]
+#![warn(unused)]
 
 type Z = for<'x> Send;
 //~^ WARN type alias is never used
diff --git a/src/test/compile-fail/issue-7364.rs b/src/test/compile-fail/issue-7364.rs
index 16b407baad1..bd32317ae78 100644
--- a/src/test/compile-fail/issue-7364.rs
+++ b/src/test/compile-fail/issue-7364.rs
@@ -10,6 +10,7 @@
 
 #![feature(box_syntax)]
 #![feature(const_fn)]
+#![allow(warnings)]
 
 use std::cell::RefCell;
 
diff --git a/src/test/compile-fail/lint-removed-allow.rs b/src/test/compile-fail/lint-removed-allow.rs
index 1498ed4d17e..9f84190ea18 100644
--- a/src/test/compile-fail/lint-removed-allow.rs
+++ b/src/test/compile-fail/lint-removed-allow.rs
@@ -11,7 +11,8 @@
 // No warnings about removed lint when
 // allow(renamed_and_removed_lints)
 
+#![allow(renamed_and_removed_lints)]
+
 #[deny(raw_pointer_derive)]
-#[allow(renamed_and_removed_lints)]
 #[deny(unused_variables)]
 fn main() { let unused = (); } //~ ERROR unused
diff --git a/src/test/compile-fail/lint-removed-cmdline.rs b/src/test/compile-fail/lint-removed-cmdline.rs
index d6bfd1eec39..e1da5086612 100644
--- a/src/test/compile-fail/lint-removed-cmdline.rs
+++ b/src/test/compile-fail/lint-removed-cmdline.rs
@@ -16,5 +16,7 @@
 // error-pattern:lint raw_pointer_derive has been removed
 // error-pattern:requested on the command line with `-D raw_pointer_derive`
 
+#![warn(unused)]
+
 #[deny(warnings)]
 fn main() { let unused = (); }
diff --git a/src/test/compile-fail/lint-renamed-allow.rs b/src/test/compile-fail/lint-renamed-allow.rs
index ea26c3656e6..ae010b64bfd 100644
--- a/src/test/compile-fail/lint-renamed-allow.rs
+++ b/src/test/compile-fail/lint-renamed-allow.rs
@@ -11,7 +11,8 @@
 // No warnings about renamed lint when
 // allow(renamed_and_removed_lints)
 
+#![allow(renamed_and_removed_lints)]
+
 #[deny(unknown_features)]
-#[allow(renamed_and_removed_lints)]
 #[deny(unused)]
 fn main() { let unused = (); } //~ ERROR unused
diff --git a/src/test/compile-fail/lint-stability-deprecated.rs b/src/test/compile-fail/lint-stability-deprecated.rs
index d8813b6a610..8443518b3f5 100644
--- a/src/test/compile-fail/lint-stability-deprecated.rs
+++ b/src/test/compile-fail/lint-stability-deprecated.rs
@@ -13,9 +13,9 @@
 // aux-build:stability_cfg1.rs
 // aux-build:stability_cfg2.rs
 
-#![deny(deprecated)]
+#![warn(deprecated)]
 #![allow(dead_code)]
-#![feature(staged_api, test_feature)]
+#![feature(staged_api, test_feature, rustc_attrs)]
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
@@ -32,41 +32,41 @@ mod cross_crate {
         type Foo = MethodTester;
         let foo = MethodTester;
 
-        deprecated(); //~ ERROR use of deprecated item
-        foo.method_deprecated(); //~ ERROR use of deprecated item
-        Foo::method_deprecated(&foo); //~ ERROR use of deprecated item
-        <Foo>::method_deprecated(&foo); //~ ERROR use of deprecated item
-        foo.trait_deprecated(); //~ ERROR use of deprecated item
-        Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item
-        <Foo>::trait_deprecated(&foo); //~ ERROR use of deprecated item
-        <Foo as Trait>::trait_deprecated(&foo); //~ ERROR use of deprecated item
-
-        deprecated_text(); //~ ERROR use of deprecated item: text
-        foo.method_deprecated_text(); //~ ERROR use of deprecated item: text
-        Foo::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text
-        <Foo>::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text
-        foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
-        Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
-        <Foo>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
-        <Foo as Trait>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
-
-        deprecated_unstable(); //~ ERROR use of deprecated item
-        foo.method_deprecated_unstable(); //~ ERROR use of deprecated item
-        Foo::method_deprecated_unstable(&foo); //~ ERROR use of deprecated item
-        <Foo>::method_deprecated_unstable(&foo); //~ ERROR use of deprecated item
-        foo.trait_deprecated_unstable(); //~ ERROR use of deprecated item
-        Trait::trait_deprecated_unstable(&foo); //~ ERROR use of deprecated item
-        <Foo>::trait_deprecated_unstable(&foo); //~ ERROR use of deprecated item
-        <Foo as Trait>::trait_deprecated_unstable(&foo); //~ ERROR use of deprecated item
-
-        deprecated_unstable_text(); //~ ERROR use of deprecated item: text
-        foo.method_deprecated_unstable_text(); //~ ERROR use of deprecated item: text
-        Foo::method_deprecated_unstable_text(&foo); //~ ERROR use of deprecated item: text
-        <Foo>::method_deprecated_unstable_text(&foo); //~ ERROR use of deprecated item: text
-        foo.trait_deprecated_unstable_text(); //~ ERROR use of deprecated item: text
-        Trait::trait_deprecated_unstable_text(&foo); //~ ERROR use of deprecated item: text
-        <Foo>::trait_deprecated_unstable_text(&foo); //~ ERROR use of deprecated item: text
-        <Foo as Trait>::trait_deprecated_unstable_text(&foo); //~ ERROR use of deprecated item: text
+        deprecated(); //~ WARN use of deprecated item
+        foo.method_deprecated(); //~ WARN use of deprecated item
+        Foo::method_deprecated(&foo); //~ WARN use of deprecated item
+        <Foo>::method_deprecated(&foo); //~ WARN use of deprecated item
+        foo.trait_deprecated(); //~ WARN use of deprecated item
+        Trait::trait_deprecated(&foo); //~ WARN use of deprecated item
+        <Foo>::trait_deprecated(&foo); //~ WARN use of deprecated item
+        <Foo as Trait>::trait_deprecated(&foo); //~ WARN use of deprecated item
+
+        deprecated_text(); //~ WARN use of deprecated item: text
+        foo.method_deprecated_text(); //~ WARN use of deprecated item: text
+        Foo::method_deprecated_text(&foo); //~ WARN use of deprecated item: text
+        <Foo>::method_deprecated_text(&foo); //~ WARN use of deprecated item: text
+        foo.trait_deprecated_text(); //~ WARN use of deprecated item: text
+        Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text
+        <Foo>::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text
+        <Foo as Trait>::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text
+
+        deprecated_unstable(); //~ WARN use of deprecated item
+        foo.method_deprecated_unstable(); //~ WARN use of deprecated item
+        Foo::method_deprecated_unstable(&foo); //~ WARN use of deprecated item
+        <Foo>::method_deprecated_unstable(&foo); //~ WARN use of deprecated item
+        foo.trait_deprecated_unstable(); //~ WARN use of deprecated item
+        Trait::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item
+        <Foo>::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item
+        <Foo as Trait>::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item
+
+        deprecated_unstable_text(); //~ WARN use of deprecated item: text
+        foo.method_deprecated_unstable_text(); //~ WARN use of deprecated item: text
+        Foo::method_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text
+        <Foo>::method_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text
+        foo.trait_deprecated_unstable_text(); //~ WARN use of deprecated item: text
+        Trait::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text
+        <Foo>::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text
+        <Foo as Trait>::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text
 
         unstable();
         foo.method_unstable();
@@ -106,30 +106,30 @@ mod cross_crate {
 
         struct S1<T: TraitWithAssociatedTypes>(T::TypeUnstable);
         struct S2<T: TraitWithAssociatedTypes>(T::TypeDeprecated);
-        //~^ ERROR use of deprecated item
+        //~^ WARN use of deprecated item
 
-        let _ = DeprecatedStruct { //~ ERROR use of deprecated item
-            i: 0 //~ ERROR use of deprecated item
+        let _ = DeprecatedStruct { //~ WARN use of deprecated item
+            i: 0 //~ WARN use of deprecated item
         };
         let _ = DeprecatedUnstableStruct {
-            //~^ ERROR use of deprecated item
-            i: 0 //~ ERROR use of deprecated item
+            //~^ WARN use of deprecated item
+            i: 0 //~ WARN use of deprecated item
         };
         let _ = UnstableStruct { i: 0 };
         let _ = StableStruct { i: 0 };
 
-        let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item
-        let _ = DeprecatedUnstableUnitStruct; //~ ERROR use of deprecated item
+        let _ = DeprecatedUnitStruct; //~ WARN use of deprecated item
+        let _ = DeprecatedUnstableUnitStruct; //~ WARN use of deprecated item
         let _ = UnstableUnitStruct;
         let _ = StableUnitStruct;
 
-        let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item
-        let _ = Enum::DeprecatedUnstableVariant; //~ ERROR use of deprecated item
+        let _ = Enum::DeprecatedVariant; //~ WARN use of deprecated item
+        let _ = Enum::DeprecatedUnstableVariant; //~ WARN use of deprecated item
         let _ = Enum::UnstableVariant;
         let _ = Enum::StableVariant;
 
-        let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item
-        let _ = DeprecatedUnstableTupleStruct (1); //~ ERROR use of deprecated item
+        let _ = DeprecatedTupleStruct (1); //~ WARN use of deprecated item
+        let _ = DeprecatedUnstableTupleStruct (1); //~ WARN use of deprecated item
         let _ = UnstableTupleStruct (1);
         let _ = StableTupleStruct (1);
 
@@ -138,28 +138,28 @@ mod cross_crate {
         // Eventually, we will want to lint the contents of the
         // macro in the module *defining* it. Also, stability levels
         // on macros themselves are not yet linted.
-        macro_test_arg!(deprecated_text()); //~ ERROR use of deprecated item: text
-        macro_test_arg!(deprecated_unstable_text()); //~ ERROR use of deprecated item: text
-        macro_test_arg!(macro_test_arg!(deprecated_text())); //~ ERROR use of deprecated item: text
+        macro_test_arg!(deprecated_text()); //~ WARN use of deprecated item: text
+        macro_test_arg!(deprecated_unstable_text()); //~ WARN use of deprecated item: text
+        macro_test_arg!(macro_test_arg!(deprecated_text())); //~ WARN use of deprecated item: text
     }
 
     fn test_method_param<Foo: Trait>(foo: Foo) {
-        foo.trait_deprecated(); //~ ERROR use of deprecated item
-        Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item
-        <Foo>::trait_deprecated(&foo); //~ ERROR use of deprecated item
-        <Foo as Trait>::trait_deprecated(&foo); //~ ERROR use of deprecated item
-        foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
-        Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
-        <Foo>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
-        <Foo as Trait>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
-        foo.trait_deprecated_unstable(); //~ ERROR use of deprecated item
-        Trait::trait_deprecated_unstable(&foo); //~ ERROR use of deprecated item
-        <Foo>::trait_deprecated_unstable(&foo); //~ ERROR use of deprecated item
-        <Foo as Trait>::trait_deprecated_unstable(&foo); //~ ERROR use of deprecated item
-        foo.trait_deprecated_unstable_text(); //~ ERROR use of deprecated item: text
-        Trait::trait_deprecated_unstable_text(&foo); //~ ERROR use of deprecated item: text
-        <Foo>::trait_deprecated_unstable_text(&foo); //~ ERROR use of deprecated item: text
-        <Foo as Trait>::trait_deprecated_unstable_text(&foo); //~ ERROR use of deprecated item: text
+        foo.trait_deprecated(); //~ WARN use of deprecated item
+        Trait::trait_deprecated(&foo); //~ WARN use of deprecated item
+        <Foo>::trait_deprecated(&foo); //~ WARN use of deprecated item
+        <Foo as Trait>::trait_deprecated(&foo); //~ WARN use of deprecated item
+        foo.trait_deprecated_text(); //~ WARN use of deprecated item: text
+        Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text
+        <Foo>::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text
+        <Foo as Trait>::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text
+        foo.trait_deprecated_unstable(); //~ WARN use of deprecated item
+        Trait::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item
+        <Foo>::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item
+        <Foo as Trait>::trait_deprecated_unstable(&foo); //~ WARN use of deprecated item
+        foo.trait_deprecated_unstable_text(); //~ WARN use of deprecated item: text
+        Trait::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text
+        <Foo>::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text
+        <Foo as Trait>::trait_deprecated_unstable_text(&foo); //~ WARN use of deprecated item: text
         foo.trait_unstable();
         Trait::trait_unstable(&foo);
         <Foo>::trait_unstable(&foo);
@@ -175,10 +175,10 @@ mod cross_crate {
     }
 
     fn test_method_object(foo: &Trait) {
-        foo.trait_deprecated(); //~ ERROR use of deprecated item
-        foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
-        foo.trait_deprecated_unstable(); //~ ERROR use of deprecated item
-        foo.trait_deprecated_unstable_text(); //~ ERROR use of deprecated item: text
+        foo.trait_deprecated(); //~ WARN use of deprecated item
+        foo.trait_deprecated_text(); //~ WARN use of deprecated item: text
+        foo.trait_deprecated_unstable(); //~ WARN use of deprecated item
+        foo.trait_deprecated_unstable_text(); //~ WARN use of deprecated item: text
         foo.trait_unstable();
         foo.trait_unstable_text();
         foo.trait_stable();
@@ -187,9 +187,9 @@ mod cross_crate {
     struct S;
 
     impl UnstableTrait for S { }
-    impl DeprecatedTrait for S {} //~ ERROR use of deprecated item: text
+    impl DeprecatedTrait for S {} //~ WARN use of deprecated item: text
     trait LocalTrait : UnstableTrait { }
-    trait LocalTrait2 : DeprecatedTrait { } //~ ERROR use of deprecated item: text
+    trait LocalTrait2 : DeprecatedTrait { } //~ WARN use of deprecated item: text
 
     impl Trait for S {
         fn trait_stable(&self) {}
@@ -208,7 +208,7 @@ mod inheritance {
         stable_mod::unstable();
         stable_mod::stable();
 
-        unstable_mod::deprecated(); //~ ERROR use of deprecated item
+        unstable_mod::deprecated(); //~ WARN use of deprecated item
         unstable_mod::unstable();
 
         let _ = Unstable::UnstableVariant;
@@ -330,23 +330,23 @@ mod this_crate {
         type Foo = MethodTester;
         let foo = MethodTester;
 
-        deprecated(); //~ ERROR use of deprecated item
-        foo.method_deprecated(); //~ ERROR use of deprecated item
-        Foo::method_deprecated(&foo); //~ ERROR use of deprecated item
-        <Foo>::method_deprecated(&foo); //~ ERROR use of deprecated item
-        foo.trait_deprecated(); //~ ERROR use of deprecated item
-        Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item
-        <Foo>::trait_deprecated(&foo); //~ ERROR use of deprecated item
-        <Foo as Trait>::trait_deprecated(&foo); //~ ERROR use of deprecated item
-
-        deprecated_text(); //~ ERROR use of deprecated item: text
-        foo.method_deprecated_text(); //~ ERROR use of deprecated item: text
-        Foo::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text
-        <Foo>::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text
-        foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
-        Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
-        <Foo>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
-        <Foo as Trait>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+        deprecated(); //~ WARN use of deprecated item
+        foo.method_deprecated(); //~ WARN use of deprecated item
+        Foo::method_deprecated(&foo); //~ WARN use of deprecated item
+        <Foo>::method_deprecated(&foo); //~ WARN use of deprecated item
+        foo.trait_deprecated(); //~ WARN use of deprecated item
+        Trait::trait_deprecated(&foo); //~ WARN use of deprecated item
+        <Foo>::trait_deprecated(&foo); //~ WARN use of deprecated item
+        <Foo as Trait>::trait_deprecated(&foo); //~ WARN use of deprecated item
+
+        deprecated_text(); //~ WARN use of deprecated item: text
+        foo.method_deprecated_text(); //~ WARN use of deprecated item: text
+        Foo::method_deprecated_text(&foo); //~ WARN use of deprecated item: text
+        <Foo>::method_deprecated_text(&foo); //~ WARN use of deprecated item: text
+        foo.trait_deprecated_text(); //~ WARN use of deprecated item: text
+        Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text
+        <Foo>::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text
+        <Foo as Trait>::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text
 
         unstable();
         foo.method_unstable();
@@ -385,34 +385,34 @@ mod this_crate {
         <Foo as Trait>::trait_stable_text(&foo);
 
         let _ = DeprecatedStruct {
-            //~^ ERROR use of deprecated item
-            i: 0 //~ ERROR use of deprecated item
+            //~^ WARN use of deprecated item
+            i: 0 //~ WARN use of deprecated item
         };
         let _ = UnstableStruct { i: 0 };
         let _ = StableStruct { i: 0 };
 
-        let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item
+        let _ = DeprecatedUnitStruct; //~ WARN use of deprecated item
         let _ = UnstableUnitStruct;
         let _ = StableUnitStruct;
 
-        let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item
+        let _ = Enum::DeprecatedVariant; //~ WARN use of deprecated item
         let _ = Enum::UnstableVariant;
         let _ = Enum::StableVariant;
 
-        let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item
+        let _ = DeprecatedTupleStruct (1); //~ WARN use of deprecated item
         let _ = UnstableTupleStruct (1);
         let _ = StableTupleStruct (1);
     }
 
     fn test_method_param<Foo: Trait>(foo: Foo) {
-        foo.trait_deprecated(); //~ ERROR use of deprecated item
-        Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item
-        <Foo>::trait_deprecated(&foo); //~ ERROR use of deprecated item
-        <Foo as Trait>::trait_deprecated(&foo); //~ ERROR use of deprecated item
-        foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
-        Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
-        <Foo>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
-        <Foo as Trait>::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text
+        foo.trait_deprecated(); //~ WARN use of deprecated item
+        Trait::trait_deprecated(&foo); //~ WARN use of deprecated item
+        <Foo>::trait_deprecated(&foo); //~ WARN use of deprecated item
+        <Foo as Trait>::trait_deprecated(&foo); //~ WARN use of deprecated item
+        foo.trait_deprecated_text(); //~ WARN use of deprecated item: text
+        Trait::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text
+        <Foo>::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text
+        <Foo as Trait>::trait_deprecated_text(&foo); //~ WARN use of deprecated item: text
         foo.trait_unstable();
         Trait::trait_unstable(&foo);
         <Foo>::trait_unstable(&foo);
@@ -428,8 +428,8 @@ mod this_crate {
     }
 
     fn test_method_object(foo: &Trait) {
-        foo.trait_deprecated(); //~ ERROR use of deprecated item
-        foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
+        foo.trait_deprecated(); //~ WARN use of deprecated item
+        foo.trait_deprecated_text(); //~ WARN use of deprecated item: text
         foo.trait_unstable();
         foo.trait_unstable_text();
         foo.trait_stable();
@@ -439,7 +439,7 @@ mod this_crate {
     #[rustc_deprecated(since = "1.0.0", reason = "text")]
     fn test_fn_body() {
         fn fn_in_body() {}
-        fn_in_body(); //~ ERROR use of deprecated item: text
+        fn_in_body(); //~ WARN use of deprecated item: text
     }
 
     impl MethodTester {
@@ -447,7 +447,7 @@ mod this_crate {
         #[rustc_deprecated(since = "1.0.0", reason = "text")]
         fn test_method_body(&self) {
             fn fn_in_body() {}
-            fn_in_body(); //~ ERROR use of deprecated item: text
+            fn_in_body(); //~ WARN use of deprecated item: text
         }
     }
 
@@ -459,9 +459,9 @@ mod this_crate {
 
     struct S;
 
-    impl DeprecatedTrait for S { } //~ ERROR use of deprecated item
+    impl DeprecatedTrait for S { } //~ WARN use of deprecated item
 
-    trait LocalTrait : DeprecatedTrait { } //~ ERROR use of deprecated item
+    trait LocalTrait : DeprecatedTrait { } //~ WARN use of deprecated item
 }
 
-fn main() {}
+#[rustc_error] fn main() {} //~ ERROR: compilation successful
diff --git a/src/test/compile-fail/lint-type-overflow2.rs b/src/test/compile-fail/lint-type-overflow2.rs
index d399fda3286..55934997583 100644
--- a/src/test/compile-fail/lint-type-overflow2.rs
+++ b/src/test/compile-fail/lint-type-overflow2.rs
@@ -9,16 +9,18 @@
 // except according to those terms.
 //
 
-#![deny(overflowing_literals)]
-#![deny(const_err)]
+#![warn(overflowing_literals)]
+#![warn(const_err)]
+#![feature(rustc_attrs)]
 
 #[allow(unused_variables)]
-fn main() {
-    let x2: i8 = --128; //~ error: literal out of range for i8
-    //~^ error: attempt to negate with overflow
+#[rustc_error]
+fn main() { //~ ERROR: compilation successful
+    let x2: i8 = --128; //~ warn: literal out of range for i8
+    //~^ warn: attempt to negate with overflow
 
-    let x = -3.40282357e+38_f32; //~ error: literal out of range for f32
-    let x =  3.40282357e+38_f32; //~ error: literal out of range for f32
-    let x = -1.7976931348623159e+308_f64; //~ error: literal out of range for f64
-    let x =  1.7976931348623159e+308_f64; //~ error: literal out of range for f64
+    let x = -3.40282357e+38_f32; //~ warn: literal out of range for f32
+    let x =  3.40282357e+38_f32; //~ warn: literal out of range for f32
+    let x = -1.7976931348623159e+308_f64; //~ warn: literal out of range for f64
+    let x =  1.7976931348623159e+308_f64; //~ warn: literal out of range for f64
 }
diff --git a/src/test/compile-fail/lint-uppercase-variables.rs b/src/test/compile-fail/lint-uppercase-variables.rs
index 1615af40071..1d947684792 100644
--- a/src/test/compile-fail/lint-uppercase-variables.rs
+++ b/src/test/compile-fail/lint-uppercase-variables.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![warn(unused)]
 #![allow(dead_code)]
 #![deny(non_snake_case)]
 
diff --git a/src/test/compile-fail/liveness-unused.rs b/src/test/compile-fail/liveness-unused.rs
index 3aab953eb79..d056d6be806 100644
--- a/src/test/compile-fail/liveness-unused.rs
+++ b/src/test/compile-fail/liveness-unused.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![warn(unused)]
 #![deny(unused_variables)]
 #![deny(unused_assignments)]
 #![allow(dead_code, non_camel_case_types, trivial_numeric_casts)]
diff --git a/src/test/compile-fail/never-assign-dead-code.rs b/src/test/compile-fail/never-assign-dead-code.rs
index d8752e1c050..1e0cc0f5357 100644
--- a/src/test/compile-fail/never-assign-dead-code.rs
+++ b/src/test/compile-fail/never-assign-dead-code.rs
@@ -10,12 +10,12 @@
 
 // Test that an assignment of type ! makes the rest of the block dead code.
 
-#![feature(never_type)]
-#![deny(unused, unreachable_code)]
+#![feature(never_type, rustc_attrs)]
+#![warn(unused)]
 
-fn main() {
-    let x: ! = panic!("aah"); //~ ERROR unused
-    drop(x); //~ ERROR unreachable
-    //~^ ERROR unreachable
+#[rustc_error]
+fn main() { //~ ERROR: compilation successful
+    let x: ! = panic!("aah"); //~ WARN unused
+    drop(x); //~ WARN unreachable
+    //~^ WARN unreachable
 }
-
diff --git a/src/test/compile-fail/private-inferred-type.rs b/src/test/compile-fail/private-inferred-type.rs
index 140891027d5..4d41f8ba47d 100644
--- a/src/test/compile-fail/private-inferred-type.rs
+++ b/src/test/compile-fail/private-inferred-type.rs
@@ -11,6 +11,7 @@
 #![feature(associated_consts)]
 #![feature(conservative_impl_trait)]
 #![feature(decl_macro)]
+#![allow(warnings)]
 
 mod m {
     fn priv_fn() {}
diff --git a/src/test/compile-fail/private-type-in-interface.rs b/src/test/compile-fail/private-type-in-interface.rs
index 925d692f8ae..a5581664f74 100644
--- a/src/test/compile-fail/private-type-in-interface.rs
+++ b/src/test/compile-fail/private-type-in-interface.rs
@@ -11,6 +11,7 @@
 // aux-build:private-inferred-type.rs
 
 #![feature(conservative_impl_trait)]
+#![allow(warnings)]
 
 extern crate private_inferred_type as ext;
 
diff --git a/src/test/compile-fail/static-mut-not-constant.rs b/src/test/compile-fail/static-mut-not-constant.rs
index 7c37abf7657..a855b08f066 100644
--- a/src/test/compile-fail/static-mut-not-constant.rs
+++ b/src/test/compile-fail/static-mut-not-constant.rs
@@ -14,6 +14,7 @@
 
 static mut a: Box<isize> = box 3;
 //~^ ERROR allocations are not allowed in statics
-//~^^ ERROR destructors in statics are an unstable feature
+//~| ERROR destructors in statics are an unstable feature
+//~| WARN: constant evaluation error
 
 fn main() {}
diff --git a/src/test/compile-fail/unreachable-try-pattern.rs b/src/test/compile-fail/unreachable-try-pattern.rs
index f4817ba33b5..46ea4a06a3b 100644
--- a/src/test/compile-fail/unreachable-try-pattern.rs
+++ b/src/test/compile-fail/unreachable-try-pattern.rs
@@ -8,9 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(never_type)]
-#![deny(unreachable_code)]
-#![deny(unreachable_patterns)]
+#![feature(never_type, rustc_attrs)]
+#![warn(unreachable_code)]
+#![warn(unreachable_patterns)]
 
 enum Void {}
 
@@ -26,8 +26,8 @@ fn bar(x: Result<!, i32>) -> Result<u32, i32> {
 
 fn foo(x: Result<!, i32>) -> Result<u32, i32> {
     let y = (match x { Ok(n) => Ok(n as u32), Err(e) => Err(e) })?;
-    //~^ ERROR unreachable pattern
-    //~| ERROR unreachable expression
+    //~^ WARN unreachable pattern
+    //~| WARN unreachable expression
     Ok(y)
 }
 
@@ -37,11 +37,12 @@ fn qux(x: Result<u32, Void>) -> Result<u32, i32> {
 
 fn vom(x: Result<u32, Void>) -> Result<u32, i32> {
     let y = (match x { Ok(n) => Ok(n), Err(e) => Err(e) })?;
-    //~^ ERROR unreachable pattern
+    //~^ WARN unreachable pattern
     Ok(y)
 }
 
-fn main() {
+#[rustc_error]
+fn main() { //~ ERROR: compilation successful
     let _ = bar(Err(123));
     let _ = foo(Err(123));
     let _ = qux(Ok(123));
diff --git a/src/test/ui-fulldeps/lint-plugin-cmdline-allow.rs b/src/test/ui-fulldeps/lint-plugin-cmdline-allow.rs
index 1e428629cc2..19ce2362134 100644
--- a/src/test/ui-fulldeps/lint-plugin-cmdline-allow.rs
+++ b/src/test/ui-fulldeps/lint-plugin-cmdline-allow.rs
@@ -14,6 +14,7 @@
 // compile-flags: -A test-lint
 
 #![feature(plugin)]
+#![warn(unused)]
 #![plugin(lint_plugin_test)]
 
 fn lintme() { }
diff --git a/src/test/ui-fulldeps/lint-plugin-cmdline-allow.stderr b/src/test/ui-fulldeps/lint-plugin-cmdline-allow.stderr
index b8513b95d36..7c9c4e99039 100644
--- a/src/test/ui-fulldeps/lint-plugin-cmdline-allow.stderr
+++ b/src/test/ui-fulldeps/lint-plugin-cmdline-allow.stderr
@@ -1,8 +1,13 @@
 warning: function is never used: `lintme`
-  --> $DIR/lint-plugin-cmdline-allow.rs:19:1
+  --> $DIR/lint-plugin-cmdline-allow.rs:20:1
    |
-19 | fn lintme() { }
+20 | fn lintme() { }
    | ^^^^^^^^^^^^^^^
    |
-   = note: #[warn(dead_code)] on by default
+note: lint level defined here
+  --> $DIR/lint-plugin-cmdline-allow.rs:17:9
+   |
+17 | #![warn(unused)]
+   |         ^^^^^^
+   = note: #[warn(dead_code)] implied by #[warn(unused)]
 
diff --git a/src/test/ui/check_match/issue-43253.rs b/src/test/ui/check_match/issue-43253.rs
index c77fa74f6e0..108d7e1ea22 100644
--- a/src/test/ui/check_match/issue-43253.rs
+++ b/src/test/ui/check_match/issue-43253.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 #![feature(exclusive_range_pattern)]
+#![warn(unreachable_patterns)]
 
 fn main() {
     // These cases should generate no warning.
@@ -48,4 +49,4 @@ fn main() {
         9...9 => {},
         _ => {},
     }
-}
\ No newline at end of file
+}
diff --git a/src/test/ui/check_match/issue-43253.stderr b/src/test/ui/check_match/issue-43253.stderr
index 0d4a2ecf512..a1cb3963914 100644
--- a/src/test/ui/check_match/issue-43253.stderr
+++ b/src/test/ui/check_match/issue-43253.stderr
@@ -1,20 +1,24 @@
 warning: unreachable pattern
-  --> $DIR/issue-43253.rs:36:9
+  --> $DIR/issue-43253.rs:37:9
    |
-36 |         9 => {},
+37 |         9 => {},
    |         ^
    |
-   = note: #[warn(unreachable_patterns)] on by default
+note: lint level defined here
+  --> $DIR/issue-43253.rs:12:9
+   |
+12 | #![warn(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
 
 warning: unreachable pattern
-  --> $DIR/issue-43253.rs:42:9
+  --> $DIR/issue-43253.rs:43:9
    |
-42 |         8...9 => {},
+43 |         8...9 => {},
    |         ^^^^^
 
 warning: unreachable pattern
-  --> $DIR/issue-43253.rs:48:9
+  --> $DIR/issue-43253.rs:49:9
    |
-48 |         9...9 => {},
+49 |         9...9 => {},
    |         ^^^^^
 
diff --git a/src/test/ui/compare-method/proj-outlives-region.stderr b/src/test/ui/compare-method/proj-outlives-region.stderr
index b1b5aedea70..e58251c846f 100644
--- a/src/test/ui/compare-method/proj-outlives-region.stderr
+++ b/src/test/ui/compare-method/proj-outlives-region.stderr
@@ -1,4 +1,4 @@
-error[E0276]: impl has stricter requirements than trait
+error: impl has stricter requirements than trait
   --> $DIR/proj-outlives-region.rs:19:5
    |
 14 |     fn foo() where T: 'a;
diff --git a/src/test/ui/compare-method/region-unrelated.stderr b/src/test/ui/compare-method/region-unrelated.stderr
index b02aa5eeb2f..95db68fea5c 100644
--- a/src/test/ui/compare-method/region-unrelated.stderr
+++ b/src/test/ui/compare-method/region-unrelated.stderr
@@ -1,4 +1,4 @@
-error[E0276]: impl has stricter requirements than trait
+error: impl has stricter requirements than trait
   --> $DIR/region-unrelated.rs:19:5
    |
 14 |     fn foo() where T: 'a;
diff --git a/src/test/ui/lint/fn_must_use.rs b/src/test/ui/lint/fn_must_use.rs
index ea2197f3fd1..5aea5f2ca06 100644
--- a/src/test/ui/lint/fn_must_use.rs
+++ b/src/test/ui/lint/fn_must_use.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![warn(unused_must_use)]
 
 struct MyStruct {
     n: usize
diff --git a/src/test/ui/lint/fn_must_use.stderr b/src/test/ui/lint/fn_must_use.stderr
index 7057c8e9aaa..20eb7452aea 100644
--- a/src/test/ui/lint/fn_must_use.stderr
+++ b/src/test/ui/lint/fn_must_use.stderr
@@ -1,14 +1,18 @@
 warning: unused return value of `need_to_use_this_value` which must be used: it's important
-  --> $DIR/fn_must_use.rs:29:5
+  --> $DIR/fn_must_use.rs:30:5
    |
-29 |     need_to_use_this_value();
+30 |     need_to_use_this_value();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: #[warn(unused_must_use)] on by default
+note: lint level defined here
+  --> $DIR/fn_must_use.rs:11:9
+   |
+11 | #![warn(unused_must_use)]
+   |         ^^^^^^^^^^^^^^^
 
 warning: unused return value of `MyStruct::need_to_use_this_method_value` which must be used
-  --> $DIR/fn_must_use.rs:32:5
+  --> $DIR/fn_must_use.rs:33:5
    |
-32 |     m.need_to_use_this_method_value();
+33 |     m.need_to_use_this_method_value();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/src/test/ui/lint/outer-forbid.rs b/src/test/ui/lint/outer-forbid.rs
index d71da58829a..a79dacbc1c9 100644
--- a/src/test/ui/lint/outer-forbid.rs
+++ b/src/test/ui/lint/outer-forbid.rs
@@ -16,7 +16,13 @@
 
 #![forbid(unused, non_snake_case)]
 
-#[allow(unused, unused_variables, bad_style)]
+#[allow(unused_variables)]
+fn foo() {}
+
+#[allow(unused)]
+fn bar() {}
+
+#[allow(bad_style)]
 fn main() {
     println!("hello forbidden world")
 }
diff --git a/src/test/ui/lint/outer-forbid.stderr b/src/test/ui/lint/outer-forbid.stderr
index df62f5acc00..67a1f4f88ad 100644
--- a/src/test/ui/lint/outer-forbid.stderr
+++ b/src/test/ui/lint/outer-forbid.stderr
@@ -1,29 +1,29 @@
-error[E0453]: allow(unused) overruled by outer forbid(unused)
+error[E0453]: allow(unused_variables) overruled by outer forbid(unused)
   --> $DIR/outer-forbid.rs:19:9
    |
 17 | #![forbid(unused, non_snake_case)]
    |           ------ `forbid` level set here
 18 | 
-19 | #[allow(unused, unused_variables, bad_style)]
-   |         ^^^^^^ overruled by previous forbid
+19 | #[allow(unused_variables)]
+   |         ^^^^^^^^^^^^^^^^ overruled by previous forbid
 
-error[E0453]: allow(unused_variables) overruled by outer forbid(unused)
-  --> $DIR/outer-forbid.rs:19:17
+error[E0453]: allow(unused) overruled by outer forbid(unused)
+  --> $DIR/outer-forbid.rs:22:9
    |
 17 | #![forbid(unused, non_snake_case)]
    |           ------ `forbid` level set here
-18 | 
-19 | #[allow(unused, unused_variables, bad_style)]
-   |                 ^^^^^^^^^^^^^^^^ overruled by previous forbid
+...
+22 | #[allow(unused)]
+   |         ^^^^^^ overruled by previous forbid
 
 error[E0453]: allow(bad_style) overruled by outer forbid(non_snake_case)
-  --> $DIR/outer-forbid.rs:19:35
+  --> $DIR/outer-forbid.rs:25:9
    |
 17 | #![forbid(unused, non_snake_case)]
    |                   -------------- `forbid` level set here
-18 | 
-19 | #[allow(unused, unused_variables, bad_style)]
-   |                                   ^^^^^^^^^ overruled by previous forbid
+...
+25 | #[allow(bad_style)]
+   |         ^^^^^^^^^ overruled by previous forbid
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/path-lookahead.rs b/src/test/ui/path-lookahead.rs
index c43f2229cdc..367a2560155 100644
--- a/src/test/ui/path-lookahead.rs
+++ b/src/test/ui/path-lookahead.rs
@@ -10,6 +10,8 @@
 
 // run-pass
 
+#![warn(unused)]
+
 // Parser test for #37765
 
 fn with_parens<T: ToString>(arg: T) -> String { //~WARN function is never used: `with_parens`
diff --git a/src/test/ui/path-lookahead.stderr b/src/test/ui/path-lookahead.stderr
index 8fd1b8de687..9936a1eb81e 100644
--- a/src/test/ui/path-lookahead.stderr
+++ b/src/test/ui/path-lookahead.stderr
@@ -1,26 +1,31 @@
 warning: unnecessary parentheses around `return` value
-  --> $DIR/path-lookahead.rs:16:10
+  --> $DIR/path-lookahead.rs:18:10
    |
-16 |   return (<T as ToString>::to_string(&arg)); //~WARN unnecessary parentheses around `return` value
+18 |   return (<T as ToString>::to_string(&arg)); //~WARN unnecessary parentheses around `return` value
    |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: #[warn(unused_parens)] on by default
 
 warning: function is never used: `with_parens`
-  --> $DIR/path-lookahead.rs:15:1
+  --> $DIR/path-lookahead.rs:17:1
    |
-15 | / fn with_parens<T: ToString>(arg: T) -> String { //~WARN function is never used: `with_parens`
-16 | |   return (<T as ToString>::to_string(&arg)); //~WARN unnecessary parentheses around `return` value
-17 | | }
+17 | / fn with_parens<T: ToString>(arg: T) -> String { //~WARN function is never used: `with_parens`
+18 | |   return (<T as ToString>::to_string(&arg)); //~WARN unnecessary parentheses around `return` value
+19 | | }
    | |_^
    |
-   = note: #[warn(dead_code)] on by default
+note: lint level defined here
+  --> $DIR/path-lookahead.rs:13:9
+   |
+13 | #![warn(unused)]
+   |         ^^^^^^
+   = note: #[warn(dead_code)] implied by #[warn(unused)]
 
 warning: function is never used: `no_parens`
-  --> $DIR/path-lookahead.rs:19:1
+  --> $DIR/path-lookahead.rs:21:1
    |
-19 | / fn no_parens<T: ToString>(arg: T) -> String { //~WARN function is never used: `no_parens`
-20 | |   return <T as ToString>::to_string(&arg);
-21 | | }
+21 | / fn no_parens<T: ToString>(arg: T) -> String { //~WARN function is never used: `no_parens`
+22 | |   return <T as ToString>::to_string(&arg);
+23 | | }
    | |_^
 
diff --git a/src/test/ui/reachable/expr_unary.stderr b/src/test/ui/reachable/expr_unary.stderr
index 328b75fd236..9f4562fe297 100644
--- a/src/test/ui/reachable/expr_unary.stderr
+++ b/src/test/ui/reachable/expr_unary.stderr
@@ -1,8 +1,20 @@
+error: unreachable expression
+  --> $DIR/expr_unary.rs:18:28
+   |
+18 |     let x: ! = ! { return; 22 };
+   |                            ^^
+   |
+note: lint level defined here
+  --> $DIR/expr_unary.rs:14:9
+   |
+14 | #![deny(unreachable_code)]
+   |         ^^^^^^^^^^^^^^^^
+
 error[E0600]: cannot apply unary operator `!` to type `!`
   --> $DIR/expr_unary.rs:18:16
    |
 18 |     let x: ! = ! { return; 22 };
    |                ^^^^^^^^^^^^^^^^
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/span/issue-24690.rs b/src/test/ui/span/issue-24690.rs
index def0d9aced3..f59d2845108 100644
--- a/src/test/ui/span/issue-24690.rs
+++ b/src/test/ui/span/issue-24690.rs
@@ -11,10 +11,13 @@
 //! A test to ensure that helpful `note` messages aren't emitted more often
 //! than necessary.
 
-// Although there are three errors, we should only get two "lint level defined
+#![feature(rustc_attrs)]
+
+// Although there are three warnings, we should only get two "lint level defined
 // here" notes pointing at the `warnings` span, one for each error type.
-#![deny(warnings)]
+#![warn(unused)]
 
+#[rustc_error]
 fn main() {
     let theTwo = 2;
     let theOtherTwo = 2;
diff --git a/src/test/ui/span/issue-24690.stderr b/src/test/ui/span/issue-24690.stderr
index 8332ba50a73..4f1c870d874 100644
--- a/src/test/ui/span/issue-24690.stderr
+++ b/src/test/ui/span/issue-24690.stderr
@@ -1,34 +1,37 @@
-error: variable `theTwo` should have a snake case name such as `the_two`
-  --> $DIR/issue-24690.rs:19:9
+warning: unused variable: `theOtherTwo`
+  --> $DIR/issue-24690.rs:23:9
    |
-19 |     let theTwo = 2;
-   |         ^^^^^^
+23 |     let theOtherTwo = 2;
+   |         ^^^^^^^^^^^
    |
 note: lint level defined here
-  --> $DIR/issue-24690.rs:16:9
+  --> $DIR/issue-24690.rs:18:9
    |
-16 | #![deny(warnings)]
-   |         ^^^^^^^^
-   = note: #[deny(non_snake_case)] implied by #[deny(warnings)]
+18 | #![warn(unused)]
+   |         ^^^^^^
+   = note: #[warn(unused_variables)] implied by #[warn(unused)]
 
-error: variable `theOtherTwo` should have a snake case name such as `the_other_two`
-  --> $DIR/issue-24690.rs:20:9
+warning: variable `theTwo` should have a snake case name such as `the_two`
+  --> $DIR/issue-24690.rs:22:9
    |
-20 |     let theOtherTwo = 2;
-   |         ^^^^^^^^^^^
+22 |     let theTwo = 2;
+   |         ^^^^^^
+   |
+   = note: #[warn(non_snake_case)] on by default
 
-error: unused variable: `theOtherTwo`
-  --> $DIR/issue-24690.rs:20:9
+warning: variable `theOtherTwo` should have a snake case name such as `the_other_two`
+  --> $DIR/issue-24690.rs:23:9
    |
-20 |     let theOtherTwo = 2;
+23 |     let theOtherTwo = 2;
    |         ^^^^^^^^^^^
-   |
-note: lint level defined here
-  --> $DIR/issue-24690.rs:16:9
-   |
-16 | #![deny(warnings)]
-   |         ^^^^^^^^
-   = note: #[deny(unused_variables)] implied by #[deny(warnings)]
 
-error: aborting due to 3 previous errors
+error: compilation successful
+  --> $DIR/issue-24690.rs:21:1
+   |
+21 | / fn main() {
+22 | |     let theTwo = 2;
+23 | |     let theOtherTwo = 2;
+24 | |     println!("{}", theTwo);
+25 | | }
+   | |_^
 
diff --git a/src/test/ui/span/macro-span-replacement.rs b/src/test/ui/span/macro-span-replacement.rs
index d779bec4ace..b7aae39c469 100644
--- a/src/test/ui/span/macro-span-replacement.rs
+++ b/src/test/ui/span/macro-span-replacement.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![warn(unused)]
+
 macro_rules! m {
     ($a:tt $b:tt) => {
         $b $a;
diff --git a/src/test/ui/span/macro-span-replacement.stderr b/src/test/ui/span/macro-span-replacement.stderr
index e7336697f48..af03aa6a369 100644
--- a/src/test/ui/span/macro-span-replacement.stderr
+++ b/src/test/ui/span/macro-span-replacement.stderr
@@ -1,11 +1,16 @@
 warning: struct is never used: `S`
-  --> $DIR/macro-span-replacement.rs:13:9
+  --> $DIR/macro-span-replacement.rs:15:9
    |
-13 |         $b $a;
+15 |         $b $a;
    |         ^^^^^^
 ...
-18 |     m!(S struct);
+20 |     m!(S struct);
    |     ------------- in this macro invocation
    |
-   = note: #[warn(dead_code)] on by default
+note: lint level defined here
+  --> $DIR/macro-span-replacement.rs:11:9
+   |
+11 | #![warn(unused)]
+   |         ^^^^^^
+   = note: #[warn(dead_code)] implied by #[warn(unused)]
 
diff --git a/src/test/ui/span/multispan-import-lint.rs b/src/test/ui/span/multispan-import-lint.rs
index 43b6cd8f85f..66536b29c02 100644
--- a/src/test/ui/span/multispan-import-lint.rs
+++ b/src/test/ui/span/multispan-import-lint.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![warn(unused)]
+
 use std::cmp::{Eq, Ord, min, PartialEq, PartialOrd};
 
 fn main() {
diff --git a/src/test/ui/span/multispan-import-lint.stderr b/src/test/ui/span/multispan-import-lint.stderr
index 4b1ca7f98bb..1fecdea7d0d 100644
--- a/src/test/ui/span/multispan-import-lint.stderr
+++ b/src/test/ui/span/multispan-import-lint.stderr
@@ -1,8 +1,13 @@
 warning: unused imports: `Eq`, `Ord`, `PartialEq`, `PartialOrd`
-  --> $DIR/multispan-import-lint.rs:11:16
+  --> $DIR/multispan-import-lint.rs:13:16
    |
-11 | use std::cmp::{Eq, Ord, min, PartialEq, PartialOrd};
+13 | use std::cmp::{Eq, Ord, min, PartialEq, PartialOrd};
    |                ^^  ^^^       ^^^^^^^^^  ^^^^^^^^^^
    |
-   = note: #[warn(unused_imports)] on by default
+note: lint level defined here
+  --> $DIR/multispan-import-lint.rs:11:9
+   |
+11 | #![warn(unused)]
+   |         ^^^^^^
+   = note: #[warn(unused_imports)] implied by #[warn(unused)]
 
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 93696561708..769748c63c0 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1156,9 +1156,21 @@ actual:\n\
     fn compile_test(&self) -> ProcRes {
         let aux_dir = self.aux_output_dir_name();
         // FIXME (#9639): This needs to handle non-utf8 paths
-        let link_args = vec!["-L".to_owned(),
-                             aux_dir.to_str().unwrap().to_owned()];
-        let args = self.make_compile_args(link_args,
+        let mut extra_args = vec!["-L".to_owned(),
+                                  aux_dir.to_str().unwrap().to_owned()];
+        match self.config.mode {
+            CompileFail | Ui => {
+                // compile-fail and ui tests tend to have tons of unused code as
+                // it's just testing various pieces of the compile, but we don't
+                // want to actually assert warnings about all this code. Instead
+                // let's just ignore unused code warnings by defaults and tests
+                // can turn it back on if needed.
+                extra_args.push("-A".to_owned());
+                extra_args.push("unused".to_owned());
+            }
+            _ => {}
+        }
+        let args = self.make_compile_args(extra_args,
                                           &self.testpaths.file,
                                           TargetLocation::ThisFile(self.make_exe_name()));
         self.compose_and_run_compiler(args, None)