about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-08-10 11:20:15 +0000
committerbors <bors@rust-lang.org>2017-08-10 11:20:15 +0000
commit2400ebfe76225745d1591c8a63c54570174ab22c (patch)
treec4b7b845fa0ca8ed21de3c3d6ab2b1718bf2e1f2
parentd21ec9b4efd1da012979b050bc0a0426fe45fcdf (diff)
parent0374e6aab7ef60b523872556ae4aca33c59fbfc9 (diff)
downloadrust-2400ebfe76225745d1591c8a63c54570174ab22c.tar.gz
rust-2400ebfe76225745d1591c8a63c54570174ab22c.zip
Auto merge of #43522 - alexcrichton:rewrite-lints, r=michaelwoerister
rustc: Rearchitect lints to be emitted more eagerly

In preparation for incremental compilation this commit refactors the lint
handling infrastructure in the compiler to be more "eager" and overall more
incremental-friendly. Many passes of the compiler can emit lints at various
points but before this commit all lints were buffered in a table to be emitted
at the very end of compilation. This commit changes these lints to be emitted
immediately during compilation using pre-calculated lint level-related data
structures.

Linting today is split into two phases, one set of "early" lints run on the
`syntax::ast` and a "late" set of lints run on the HIR. This commit moves the
"early" lints to running as late as possible in compilation, just before HIR
lowering. This notably means that we're catching resolve-related lints just
before HIR lowering. The early linting remains a pass very similar to how it was
before, maintaining context of the current lint level as it walks the tree.

Post-HIR, however, linting is structured as a method on the `TyCtxt` which
transitively executes a query to calculate lint levels. Each request to lint on
a `TyCtxt` will query the entire crate's 'lint level data structure' and then go
from there about whether the lint should be emitted or not.

The query depends on the entire HIR crate but should be very quick to calculate
(just a quick walk of the HIR) and the red-green system should notice that the
lint level data structure rarely changes, and should hopefully preserve
incrementality.

Overall this resulted in a pretty big change to the test suite now that lints
are emitted much earlier in compilation (on-demand vs only at the end). This in
turn necessitated the addition of many `#![allow(warnings)]` directives
throughout the compile-fail test suite and a number of updates to the UI test
suite.

Closes https://github.com/rust-lang/rust/issues/42511
-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 b365096b23f..3e39d592135 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 22f98454f6c..c689b3d241c 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -645,7 +645,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
@@ -715,8 +714,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();
@@ -780,10 +779,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));
 
@@ -807,6 +802,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);
@@ -915,6 +914,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);
@@ -1201,10 +1201,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 3caa9ad34d3..9fa5fea20d9 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 41f1f5877d8..26c398379dc 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 bd325474fe5..7c683ce3f20 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));
         }
     }
 
@@ -4769,8 +4769,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)