From e3ed7b0501a24f4def3aff775c6fbf9481e3c77e Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov <vadim.petrochenkov@gmail.com>
Date: Fri, 4 Dec 2015 19:34:28 +0300
Subject: [PATCH] Implement `#[deprecated]` attribute (RFC 1270)

---
 src/doc/reference.md                          |   2 +
 src/librustc/middle/cstore.rs                 |   2 +
 src/librustc/middle/stability.rs              | 169 ++++++--
 src/librustc_lint/builtin.rs                  |  63 ++-
 src/librustc_lint/lib.rs                      |   2 +-
 src/librustc_metadata/common.rs               |   2 +
 src/librustc_metadata/csearch.rs              |   6 +
 src/librustc_metadata/decoder.rs              |   8 +
 src/librustc_metadata/encoder.rs              |  60 ++-
 src/librustdoc/clean/inline.rs                |  10 +-
 src/librustdoc/clean/mod.rs                   |  10 +-
 src/librustdoc/visit_ast.rs                   |   2 +-
 src/libsyntax/attr.rs                         |  91 ++++-
 src/libsyntax/feature_gate.rs                 |   7 +
 src/test/auxiliary/deprecation-lint.rs        |  87 ++++
 .../compile-fail/deprecation-in-staged-api.rs |  18 +
 src/test/compile-fail/deprecation-lint-2.rs   |  23 ++
 src/test/compile-fail/deprecation-lint-3.rs   |  24 ++
 src/test/compile-fail/deprecation-lint.rs     | 384 ++++++++++++++++++
 src/test/compile-fail/deprecation-sanity.rs   |  39 ++
 20 files changed, 899 insertions(+), 110 deletions(-)
 create mode 100644 src/test/auxiliary/deprecation-lint.rs
 create mode 100644 src/test/compile-fail/deprecation-in-staged-api.rs
 create mode 100644 src/test/compile-fail/deprecation-lint-2.rs
 create mode 100644 src/test/compile-fail/deprecation-lint-3.rs
 create mode 100644 src/test/compile-fail/deprecation-lint.rs
 create mode 100644 src/test/compile-fail/deprecation-sanity.rs

diff --git a/src/doc/reference.md b/src/doc/reference.md
index 0262ff5a71a..a20d2571152 100644
--- a/src/doc/reference.md
+++ b/src/doc/reference.md
@@ -2390,6 +2390,8 @@ The currently implemented features of the reference compiler are:
 * - `stmt_expr_attributes` - Allows attributes on expressions and
                              non-item statements.
 
+* - `deprecated` - Allows using the `#[deprecated]` attribute.
+
 If a feature is promoted to a language feature, then all existing programs will
 start to receive compilation warnings about `#![feature]` directives which enabled
 the new feature (because the directive is no longer necessary). However, if a
diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs
index 22a4ddd2f68..2c3b89bf2fb 100644
--- a/src/librustc/middle/cstore.rs
+++ b/src/librustc/middle/cstore.rs
@@ -135,6 +135,7 @@ pub enum FoundAst<'ast> {
 pub trait CrateStore<'tcx> : Any {
     // item info
     fn stability(&self, def: DefId) -> Option<attr::Stability>;
+    fn deprecation(&self, def: DefId) -> Option<attr::Deprecation>;
     fn closure_kind(&self, tcx: &ty::ctxt<'tcx>, def_id: DefId)
                     -> ty::ClosureKind;
     fn closure_ty(&self, tcx: &ty::ctxt<'tcx>, def_id: DefId)
@@ -292,6 +293,7 @@ pub struct DummyCrateStore;
 impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
     // item info
     fn stability(&self, def: DefId) -> Option<attr::Stability> { unimplemented!() }
+    fn deprecation(&self, def: DefId) -> Option<attr::Deprecation> { unimplemented!() }
     fn closure_kind(&self, tcx: &ty::ctxt<'tcx>, def_id: DefId)
                     -> ty::ClosureKind  { unimplemented!() }
     fn closure_ty(&self, tcx: &ty::ctxt<'tcx>, def_id: DefId)
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index 0d92c3da83c..f7e2135d5a4 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -25,7 +25,7 @@ use syntax::codemap::{Span, DUMMY_SP};
 use syntax::ast;
 use syntax::ast::{NodeId, Attribute};
 use syntax::feature_gate::{GateIssue, emit_feature_err};
-use syntax::attr::{self, Stability, AttrMetaMethods};
+use syntax::attr::{self, Stability, Deprecation, AttrMetaMethods};
 use util::nodemap::{DefIdMap, FnvHashSet, FnvHashMap};
 
 use rustc_front::hir;
@@ -61,7 +61,8 @@ enum AnnotationKind {
 pub struct Index<'tcx> {
     /// This is mostly a cache, except the stabilities of local items
     /// are filled by the annotator.
-    map: DefIdMap<Option<&'tcx Stability>>,
+    stab_map: DefIdMap<Option<&'tcx Stability>>,
+    depr_map: DefIdMap<Option<Deprecation>>,
 
     /// Maps for each crate whether it is part of the staged API.
     staged_api: FnvHashMap<ast::CrateNum, bool>
@@ -71,7 +72,8 @@ pub struct Index<'tcx> {
 struct Annotator<'a, 'tcx: 'a> {
     tcx: &'a ty::ctxt<'tcx>,
     index: &'a mut Index<'tcx>,
-    parent: Option<&'tcx Stability>,
+    parent_stab: Option<&'tcx Stability>,
+    parent_depr: Option<Deprecation>,
     access_levels: &'a AccessLevels,
     in_trait_impl: bool,
     in_enum: bool,
@@ -86,22 +88,26 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
     {
         if self.index.staged_api[&LOCAL_CRATE] && self.tcx.sess.features.borrow().staged_api {
             debug!("annotate(id = {:?}, attrs = {:?})", id, attrs);
+            if let Some(..) = attr::find_deprecation(self.tcx.sess.diagnostic(), attrs, item_sp) {
+                self.tcx.sess.span_err(item_sp, "`#[deprecated]` cannot be used in staged api, \
+                                                 use `#[rustc_deprecated]` instead");
+            }
             if let Some(mut stab) = attr::find_stability(self.tcx.sess.diagnostic(),
                                                          attrs, item_sp) {
                 // Error if prohibited, or can't inherit anything from a container
                 if kind == AnnotationKind::Prohibited ||
                    (kind == AnnotationKind::Container &&
                     stab.level.is_stable() &&
-                    stab.depr.is_none()) {
+                    stab.rustc_depr.is_none()) {
                     self.tcx.sess.span_err(item_sp, "This stability annotation is useless");
                 }
 
                 debug!("annotate: found {:?}", stab);
                 // If parent is deprecated and we're not, inherit this by merging
                 // deprecated_since and its reason.
-                if let Some(parent_stab) = self.parent {
-                    if parent_stab.depr.is_some() && stab.depr.is_none() {
-                        stab.depr = parent_stab.depr.clone()
+                if let Some(parent_stab) = self.parent_stab {
+                    if parent_stab.rustc_depr.is_some() && stab.rustc_depr.is_none() {
+                        stab.rustc_depr = parent_stab.rustc_depr.clone()
                     }
                 }
 
@@ -109,8 +115,8 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
 
                 // Check if deprecated_since < stable_since. If it is,
                 // this is *almost surely* an accident.
-                if let (&Some(attr::Deprecation {since: ref dep_since, ..}),
-                        &attr::Stable {since: ref stab_since}) = (&stab.depr, &stab.level) {
+                if let (&Some(attr::RustcDeprecation {since: ref dep_since, ..}),
+                        &attr::Stable {since: ref stab_since}) = (&stab.rustc_depr, &stab.level) {
                     // Explicit version of iter::order::lt to handle parse errors properly
                     for (dep_v, stab_v) in dep_since.split(".").zip(stab_since.split(".")) {
                         if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::<u64>(), stab_v.parse()) {
@@ -134,20 +140,20 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
                 }
 
                 let def_id = self.tcx.map.local_def_id(id);
-                self.index.map.insert(def_id, Some(stab));
+                self.index.stab_map.insert(def_id, Some(stab));
 
-                let parent = replace(&mut self.parent, Some(stab));
+                let orig_parent_stab = replace(&mut self.parent_stab, Some(stab));
                 visit_children(self);
-                self.parent = parent;
+                self.parent_stab = orig_parent_stab;
             } else {
-                debug!("annotate: not found, parent = {:?}", self.parent);
+                debug!("annotate: not found, parent = {:?}", self.parent_stab);
                 let mut is_error = kind == AnnotationKind::Required &&
                                    self.access_levels.is_reachable(id) &&
                                    !self.tcx.sess.opts.test;
-                if let Some(stab) = self.parent {
+                if let Some(stab) = self.parent_stab {
                     if stab.level.is_unstable() {
                         let def_id = self.tcx.map.local_def_id(id);
-                        self.index.map.insert(def_id, Some(stab));
+                        self.index.stab_map.insert(def_id, Some(stab));
                         is_error = false;
                     }
                 }
@@ -165,9 +171,35 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
                     attr::mark_used(attr);
                     self.tcx.sess.span_err(attr.span(), "stability attributes may not be used \
                                                          outside of the standard library");
+                } else if tag == "deprecated" {
+                    if !self.tcx.sess.features.borrow().deprecated {
+                        self.tcx.sess.span_err(attr.span(),
+                                               "`#[deprecated]` attribute is unstable");
+                        fileline_help!(self.tcx.sess, attr.span(), "add #![feature(deprecated)] to \
+                                                                    the crate features to enable");
+                    }
                 }
             }
-            visit_children(self);
+
+            if let Some(depr) = attr::find_deprecation(self.tcx.sess.diagnostic(), attrs, item_sp) {
+                if kind == AnnotationKind::Prohibited {
+                    self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless");
+                }
+
+                // `Deprecation` is just two pointers, no need to intern it
+                let def_id = self.tcx.map.local_def_id(id);
+                self.index.depr_map.insert(def_id, Some(depr.clone()));
+
+                let orig_parent_depr = replace(&mut self.parent_depr, Some(depr));
+                visit_children(self);
+                self.parent_depr = orig_parent_depr;
+            } else if let Some(depr) = self.parent_depr.clone() {
+                let def_id = self.tcx.map.local_def_id(id);
+                self.index.depr_map.insert(def_id, Some(depr));
+                visit_children(self);
+            } else {
+                visit_children(self);
+            }
         }
     }
 }
@@ -269,7 +301,8 @@ impl<'tcx> Index<'tcx> {
         let mut annotator = Annotator {
             tcx: tcx,
             index: self,
-            parent: None,
+            parent_stab: None,
+            parent_depr: None,
             access_levels: access_levels,
             in_trait_impl: false,
             in_enum: false,
@@ -291,7 +324,8 @@ impl<'tcx> Index<'tcx> {
         staged_api.insert(LOCAL_CRATE, is_staged_api);
         Index {
             staged_api: staged_api,
-            map: DefIdMap(),
+            stab_map: DefIdMap(),
+            depr_map: DefIdMap(),
         }
     }
 }
@@ -327,7 +361,11 @@ struct Checker<'a, 'tcx: 'a> {
 }
 
 impl<'a, 'tcx> Checker<'a, 'tcx> {
-    fn check(&mut self, id: DefId, span: Span, stab: &Option<&Stability>) {
+    fn check(&mut self, id: DefId, span: Span,
+             stab: &Option<&Stability>, _depr: &Option<Deprecation>) {
+        if !is_staged_api(self.tcx, id) {
+            return;
+        }
         // Only the cross-crate scenario matters when checking unstable APIs
         let cross_crate = !id.is_local();
         if !cross_crate {
@@ -395,31 +433,31 @@ impl<'a, 'v, 'tcx> Visitor<'v> for Checker<'a, 'tcx> {
         if item.span == DUMMY_SP && item.name.as_str() == "__test" { return }
 
         check_item(self.tcx, item, true,
-                   &mut |id, sp, stab| self.check(id, sp, stab));
+                   &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
         intravisit::walk_item(self, item);
     }
 
     fn visit_expr(&mut self, ex: &hir::Expr) {
         check_expr(self.tcx, ex,
-                   &mut |id, sp, stab| self.check(id, sp, stab));
+                   &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
         intravisit::walk_expr(self, ex);
     }
 
     fn visit_path(&mut self, path: &hir::Path, id: ast::NodeId) {
         check_path(self.tcx, path, id,
-                   &mut |id, sp, stab| self.check(id, sp, stab));
+                   &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
         intravisit::walk_path(self, path)
     }
 
     fn visit_path_list_item(&mut self, prefix: &hir::Path, item: &hir::PathListItem) {
         check_path_list_item(self.tcx, item,
-                   &mut |id, sp, stab| self.check(id, sp, stab));
+                   &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
         intravisit::walk_path_list_item(self, prefix, item)
     }
 
     fn visit_pat(&mut self, pat: &hir::Pat) {
         check_pat(self.tcx, pat,
-                  &mut |id, sp, stab| self.check(id, sp, stab));
+                  &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
         intravisit::walk_pat(self, pat)
     }
 
@@ -441,7 +479,7 @@ impl<'a, 'v, 'tcx> Visitor<'v> for Checker<'a, 'tcx> {
 
 /// Helper for discovering nodes to check for stability
 pub fn check_item(tcx: &ty::ctxt, item: &hir::Item, warn_about_defns: bool,
-                  cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
+                  cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) {
     match item.node {
         hir::ItemExternCrate(_) => {
             // compiler-generated `extern crate` items have a dummy span.
@@ -478,7 +516,7 @@ pub fn check_item(tcx: &ty::ctxt, item: &hir::Item, warn_about_defns: bool,
 
 /// Helper for discovering nodes to check for stability
 pub fn check_expr(tcx: &ty::ctxt, e: &hir::Expr,
-                  cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
+                  cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) {
     let span;
     let id = match e.node {
         hir::ExprMethodCall(i, _, _) => {
@@ -539,7 +577,7 @@ pub fn check_expr(tcx: &ty::ctxt, e: &hir::Expr,
 }
 
 pub fn check_path(tcx: &ty::ctxt, path: &hir::Path, id: ast::NodeId,
-                  cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
+                  cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) {
     match tcx.def_map.borrow().get(&id).map(|d| d.full_def()) {
         Some(def::DefPrimTy(..)) => {}
         Some(def::DefSelfTy(..)) => {}
@@ -551,7 +589,7 @@ pub fn check_path(tcx: &ty::ctxt, path: &hir::Path, id: ast::NodeId,
 }
 
 pub fn check_path_list_item(tcx: &ty::ctxt, item: &hir::PathListItem,
-                  cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
+                  cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) {
     match tcx.def_map.borrow().get(&item.node.id()).map(|d| d.full_def()) {
         Some(def::DefPrimTy(..)) => {}
         Some(def) => {
@@ -562,7 +600,7 @@ pub fn check_path_list_item(tcx: &ty::ctxt, item: &hir::PathListItem,
 }
 
 pub fn check_pat(tcx: &ty::ctxt, pat: &hir::Pat,
-                 cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
+                 cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) {
     debug!("check_pat(pat = {:?})", pat);
     if is_internal(tcx, pat.span) { return; }
 
@@ -591,21 +629,21 @@ pub fn check_pat(tcx: &ty::ctxt, pat: &hir::Pat,
 }
 
 fn maybe_do_stability_check(tcx: &ty::ctxt, id: DefId, span: Span,
-                            cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
-    if !is_staged_api(tcx, id) {
-        debug!("maybe_do_stability_check: \
-                skipping id={:?} since it is not staged_api", id);
-        return;
-    }
+                            cb: &mut FnMut(DefId, Span,
+                                           &Option<&Stability>, &Option<Deprecation>)) {
     if is_internal(tcx, span) {
         debug!("maybe_do_stability_check: \
                 skipping span={:?} since it is internal", span);
         return;
     }
-    let ref stability = lookup(tcx, id);
+    let (stability, deprecation) = if is_staged_api(tcx, id) {
+        (lookup_stability(tcx, id), None)
+    } else {
+        (None, lookup_deprecation(tcx, id))
+    };
     debug!("maybe_do_stability_check: \
             inspecting id={:?} span={:?} of stability={:?}", id, span, stability);
-    cb(id, span, stability);
+    cb(id, span, &stability, &deprecation);
 }
 
 fn is_internal(tcx: &ty::ctxt, span: Span) -> bool {
@@ -627,24 +665,34 @@ fn is_staged_api(tcx: &ty::ctxt, id: DefId) -> bool {
 
 /// Lookup the stability for a node, loading external crate
 /// metadata as necessary.
-pub fn lookup<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> {
-    if let Some(st) = tcx.stability.borrow().map.get(&id) {
+pub fn lookup_stability<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> {
+    if let Some(st) = tcx.stability.borrow().stab_map.get(&id) {
         return *st;
     }
 
-    let st = lookup_uncached(tcx, id);
-    tcx.stability.borrow_mut().map.insert(id, st);
+    let st = lookup_stability_uncached(tcx, id);
+    tcx.stability.borrow_mut().stab_map.insert(id, st);
     st
 }
 
-fn lookup_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> {
+pub fn lookup_deprecation<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<Deprecation> {
+    if let Some(depr) = tcx.stability.borrow().depr_map.get(&id) {
+        return depr.clone();
+    }
+
+    let depr = lookup_deprecation_uncached(tcx, id);
+    tcx.stability.borrow_mut().depr_map.insert(id, depr.clone());
+    depr
+}
+
+fn lookup_stability_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> {
     debug!("lookup(id={:?})", id);
 
     // is this definition the implementation of a trait method?
     match tcx.trait_item_of_item(id) {
         Some(ty::MethodTraitItemId(trait_method_id)) if trait_method_id != id => {
             debug!("lookup: trait_method_id={:?}", trait_method_id);
-            return lookup(tcx, trait_method_id)
+            return lookup_stability(tcx, trait_method_id)
         }
         _ => {}
     }
@@ -663,7 +711,40 @@ fn lookup_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stabil
                 // unmarked impls for it. See FIXME above for more details.
 
                 debug!("lookup: trait_id={:?}", trait_id);
-                return lookup(tcx, trait_id);
+                return lookup_stability(tcx, trait_id);
+            }
+        }
+        None
+    })
+}
+
+fn lookup_deprecation_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<Deprecation> {
+    debug!("lookup(id={:?})", id);
+
+    // is this definition the implementation of a trait method?
+    match tcx.trait_item_of_item(id) {
+        Some(ty::MethodTraitItemId(trait_method_id)) if trait_method_id != id => {
+            debug!("lookup: trait_method_id={:?}", trait_method_id);
+            return lookup_deprecation(tcx, trait_method_id)
+        }
+        _ => {}
+    }
+
+    let item_depr = if id.is_local() {
+        None // The stability cache is filled partially lazily
+    } else {
+        tcx.sess.cstore.deprecation(id)
+    };
+
+    item_depr.or_else(|| {
+        if tcx.is_impl(id) {
+            if let Some(trait_id) = tcx.trait_id_of_impl(id) {
+                // FIXME (#18969): for the time being, simply use the
+                // stability of the trait to determine the stability of any
+                // unmarked impls for it. See FIXME above for more details.
+
+                debug!("lookup: trait_id={:?}", trait_id);
+                return lookup_deprecation(tcx, trait_id);
             }
         }
         None
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 90d9bcfffee..249504cbd8d 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -575,74 +575,71 @@ impl LateLintPass for MissingDebugImplementations {
 declare_lint! {
     DEPRECATED,
     Warn,
-    "detects use of #[rustc_deprecated] items"
+    "detects use of `#[deprecated]` or `#[rustc_deprecated]` items"
 }
 
-/// Checks for use of items with `#[rustc_deprecated]` attributes
+/// Checks for use of items with `#[deprecated]` or `#[rustc_deprecated]` attributes
 #[derive(Copy, Clone)]
-pub struct Stability;
+pub struct Deprecated;
 
-impl Stability {
-    fn lint(&self, cx: &LateContext, _id: DefId,
-            span: Span, stability: &Option<&attr::Stability>) {
+impl Deprecated {
+    fn lint(&self, cx: &LateContext, _id: DefId, span: Span,
+            stability: &Option<&attr::Stability>, deprecation: &Option<attr::Deprecation>) {
         // Deprecated attributes apply in-crate and cross-crate.
-        let (lint, label) = match *stability {
-            Some(&attr::Stability { depr: Some(_), .. }) =>
-                (DEPRECATED, "deprecated"),
-            _ => return
-        };
+        if let Some(&attr::Stability{rustc_depr: Some(attr::RustcDeprecation{ref reason, ..}), ..})
+                = *stability {
+            output(cx, DEPRECATED, span, Some(&reason))
+        } else if let Some(attr::Deprecation{ref note, ..}) = *deprecation {
+            output(cx, DEPRECATED, span, note.as_ref().map(|x| &**x))
+        }
 
-        output(cx, span, stability, lint, label);
-
-        fn output(cx: &LateContext, span: Span, stability: &Option<&attr::Stability>,
-                  lint: &'static Lint, label: &'static str) {
-            let msg = match *stability {
-                Some(&attr::Stability {depr: Some(attr::Deprecation {ref reason, ..}), ..}) => {
-                    format!("use of {} item: {}", label, reason)
-                }
-                _ => format!("use of {} item", label)
+        fn output(cx: &LateContext, lint: &'static Lint, span: Span, note: Option<&str>) {
+            let msg = if let Some(note) = note {
+                format!("use of deprecated item: {}", note)
+            } else {
+                format!("use of deprecated item")
             };
 
-            cx.span_lint(lint, span, &msg[..]);
+            cx.span_lint(lint, span, &msg);
         }
     }
 }
 
-impl LintPass for Stability {
+impl LintPass for Deprecated {
     fn get_lints(&self) -> LintArray {
         lint_array!(DEPRECATED)
     }
 }
 
-impl LateLintPass for Stability {
+impl LateLintPass for Deprecated {
     fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
         stability::check_item(cx.tcx, item, false,
-                              &mut |id, sp, stab|
-                                self.lint(cx, id, sp, &stab));
+                              &mut |id, sp, stab, depr|
+                                self.lint(cx, id, sp, &stab, &depr));
     }
 
     fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
         stability::check_expr(cx.tcx, e,
-                              &mut |id, sp, stab|
-                                self.lint(cx, id, sp, &stab));
+                              &mut |id, sp, stab, depr|
+                                self.lint(cx, id, sp, &stab, &depr));
     }
 
     fn check_path(&mut self, cx: &LateContext, path: &hir::Path, id: ast::NodeId) {
         stability::check_path(cx.tcx, path, id,
-                              &mut |id, sp, stab|
-                                self.lint(cx, id, sp, &stab));
+                              &mut |id, sp, stab, depr|
+                                self.lint(cx, id, sp, &stab, &depr));
     }
 
     fn check_path_list_item(&mut self, cx: &LateContext, item: &hir::PathListItem) {
         stability::check_path_list_item(cx.tcx, item,
-                                         &mut |id, sp, stab|
-                                           self.lint(cx, id, sp, &stab));
+                                         &mut |id, sp, stab, depr|
+                                           self.lint(cx, id, sp, &stab, &depr));
     }
 
     fn check_pat(&mut self, cx: &LateContext, pat: &hir::Pat) {
         stability::check_pat(cx.tcx, pat,
-                             &mut |id, sp, stab|
-                                self.lint(cx, id, sp, &stab));
+                             &mut |id, sp, stab, depr|
+                                self.lint(cx, id, sp, &stab, &depr));
     }
 }
 
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index 1d7431404f5..69fd569c8d4 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -124,7 +124,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
                  UnusedAllocation,
                  MissingCopyImplementations,
                  UnstableFeatures,
-                 Stability,
+                 Deprecated,
                  UnconditionalRecursion,
                  InvalidNoMangleItems,
                  PluginAsLibrary,
diff --git a/src/librustc_metadata/common.rs b/src/librustc_metadata/common.rs
index 5186c969133..479ab759278 100644
--- a/src/librustc_metadata/common.rs
+++ b/src/librustc_metadata/common.rs
@@ -237,6 +237,8 @@ pub const tag_impl_coerce_unsized_kind: usize = 0xa5;
 
 pub const tag_items_data_item_constness: usize = 0xa6;
 
+pub const tag_items_data_item_deprecation: usize = 0xa7;
+
 pub const tag_rustc_version: usize = 0x10f;
 pub fn rustc_version() -> String {
     format!(
diff --git a/src/librustc_metadata/csearch.rs b/src/librustc_metadata/csearch.rs
index ad00ef29e5f..ecbc8402330 100644
--- a/src/librustc_metadata/csearch.rs
+++ b/src/librustc_metadata/csearch.rs
@@ -42,6 +42,12 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
         decoder::get_stability(&*cdata, def.index)
     }
 
+    fn deprecation(&self, def: DefId) -> Option<attr::Deprecation>
+    {
+        let cdata = self.get_crate_data(def.krate);
+        decoder::get_deprecation(&*cdata, def.index)
+    }
+
     fn closure_kind(&self, _tcx: &ty::ctxt<'tcx>, def_id: DefId) -> ty::ClosureKind
     {
         assert!(!def_id.is_local());
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index d1917b29b9f..357158c24ba 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -526,6 +526,14 @@ pub fn get_stability(cdata: Cmd, id: DefIndex) -> Option<attr::Stability> {
     })
 }
 
+pub fn get_deprecation(cdata: Cmd, id: DefIndex) -> Option<attr::Deprecation> {
+    let item = cdata.lookup_item(id);
+    reader::maybe_get_doc(item, tag_items_data_item_deprecation).map(|doc| {
+        let mut decoder = reader::Decoder::new(doc);
+        Decodable::decode(&mut decoder).unwrap()
+    })
+}
+
 pub fn get_repr_attrs(cdata: Cmd, id: DefIndex) -> Vec<attr::ReprAttr> {
     let item = cdata.lookup_item(id);
     match reader::maybe_get_doc(item, tag_items_data_item_repr).map(|doc| {
diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index a627eeb6880..888776eaa56 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -342,8 +342,10 @@ fn encode_enum_variant_info<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_attributes(rbml_w, &attrs);
         encode_repr_attrs(rbml_w, ecx, &attrs);
 
-        let stab = stability::lookup(ecx.tcx, vid);
+        let stab = stability::lookup_stability(ecx.tcx, vid);
+        let depr = stability::lookup_deprecation(ecx.tcx, vid);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
 
         encode_struct_fields(rbml_w, variant);
 
@@ -450,8 +452,10 @@ fn encode_info_for_mod(ecx: &EncodeContext,
     encode_path(rbml_w, path.clone());
     encode_visibility(rbml_w, vis);
 
-    let stab = stability::lookup(ecx.tcx, ecx.tcx.map.local_def_id(id));
+    let stab = stability::lookup_stability(ecx.tcx, ecx.tcx.map.local_def_id(id));
+    let depr = stability::lookup_deprecation(ecx.tcx, ecx.tcx.map.local_def_id(id));
     encode_stability(rbml_w, stab);
+    encode_deprecation(rbml_w, depr);
 
     // Encode the reexports of this module, if this module is public.
     if vis == hir::Public {
@@ -538,8 +542,10 @@ fn encode_field<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
     encode_bounds_and_type_for_item(rbml_w, ecx, index, id);
     encode_def_id_and_key(ecx, rbml_w, field.did);
 
-    let stab = stability::lookup(ecx.tcx, field.did);
+    let stab = stability::lookup_stability(ecx.tcx, field.did);
+    let depr = stability::lookup_deprecation(ecx.tcx, field.did);
     encode_stability(rbml_w, stab);
+    encode_deprecation(rbml_w, depr);
 
     rbml_w.end_tag();
 }
@@ -565,8 +571,10 @@ fn encode_info_for_struct_ctor<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_symbol(ecx, rbml_w, ctor_id);
     }
 
-    let stab = stability::lookup(ecx.tcx, ecx.tcx.map.local_def_id(ctor_id));
+    let stab = stability::lookup_stability(ecx.tcx, ecx.tcx.map.local_def_id(ctor_id));
+    let depr= stability::lookup_deprecation(ecx.tcx, ecx.tcx.map.local_def_id(ctor_id));
     encode_stability(rbml_w, stab);
+    encode_deprecation(rbml_w, depr);
 
     // indicate that this is a tuple struct ctor, because downstream users will normally want
     // the tuple struct definition, but without this there is no way for them to tell that
@@ -700,8 +708,10 @@ fn encode_info_for_associated_const<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
     encode_bounds_and_type_for_item(rbml_w, ecx, index,
                                     ecx.local_id(associated_const.def_id));
 
-    let stab = stability::lookup(ecx.tcx, associated_const.def_id);
+    let stab = stability::lookup_stability(ecx.tcx, associated_const.def_id);
+    let depr = stability::lookup_deprecation(ecx.tcx, associated_const.def_id);
     encode_stability(rbml_w, stab);
+    encode_deprecation(rbml_w, depr);
 
     let elem = ast_map::PathName(associated_const.name);
     encode_path(rbml_w, impl_path.chain(Some(elem)));
@@ -735,8 +745,10 @@ fn encode_info_for_method<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
     encode_parent_item(rbml_w, ecx.tcx.map.local_def_id(parent_id));
     encode_item_sort(rbml_w, 'r');
 
-    let stab = stability::lookup(ecx.tcx, m.def_id);
+    let stab = stability::lookup_stability(ecx.tcx, m.def_id);
+    let depr = stability::lookup_deprecation(ecx.tcx, m.def_id);
     encode_stability(rbml_w, stab);
+    encode_deprecation(rbml_w, depr);
 
     let m_node_id = ecx.local_id(m.def_id);
     encode_bounds_and_type_for_item(rbml_w, ecx, index, m_node_id);
@@ -789,8 +801,10 @@ fn encode_info_for_associated_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
     encode_parent_item(rbml_w, ecx.tcx.map.local_def_id(parent_id));
     encode_item_sort(rbml_w, 't');
 
-    let stab = stability::lookup(ecx.tcx, associated_type.def_id);
+    let stab = stability::lookup_stability(ecx.tcx, associated_type.def_id);
+    let depr = stability::lookup_deprecation(ecx.tcx, associated_type.def_id);
     encode_stability(rbml_w, stab);
+    encode_deprecation(rbml_w, depr);
 
     let elem = ast_map::PathName(associated_type.name);
     encode_path(rbml_w, impl_path.chain(Some(elem)));
@@ -891,6 +905,14 @@ fn encode_stability(rbml_w: &mut Encoder, stab_opt: Option<&attr::Stability>) {
     });
 }
 
+fn encode_deprecation(rbml_w: &mut Encoder, depr_opt: Option<attr::Deprecation>) {
+    depr_opt.map(|depr| {
+        rbml_w.start_tag(tag_items_data_item_deprecation);
+        depr.encode(rbml_w).unwrap();
+        rbml_w.end_tag();
+    });
+}
+
 fn encode_xrefs<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
                           rbml_w: &mut Encoder,
                           xrefs: FnvHashMap<XRef<'tcx>, u32>)
@@ -931,7 +953,8 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
            tcx.sess.codemap().span_to_string(item.span));
 
     let def_id = ecx.tcx.map.local_def_id(item.id);
-    let stab = stability::lookup(tcx, ecx.tcx.map.local_def_id(item.id));
+    let stab = stability::lookup_stability(tcx, ecx.tcx.map.local_def_id(item.id));
+    let depr = stability::lookup_deprecation(tcx, ecx.tcx.map.local_def_id(item.id));
 
     match item.node {
       hir::ItemStatic(_, m, _) => {
@@ -949,6 +972,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_path(rbml_w, path);
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         encode_attributes(rbml_w, &item.attrs);
         rbml_w.end_tag();
       }
@@ -964,6 +988,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_inlined_item(ecx, rbml_w, InlinedItemRef::Item(item));
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         rbml_w.end_tag();
       }
       hir::ItemFn(ref decl, _, constness, _, ref generics, _) => {
@@ -986,6 +1011,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_constness(rbml_w, constness);
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         encode_method_argument_names(rbml_w, &**decl);
         rbml_w.end_tag();
       }
@@ -1015,6 +1041,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         }
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         rbml_w.end_tag();
       }
       hir::ItemTy(..) => {
@@ -1027,6 +1054,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_path(rbml_w, path);
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         rbml_w.end_tag();
       }
       hir::ItemEnum(ref enum_definition, _) => {
@@ -1051,6 +1079,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
 
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         rbml_w.end_tag();
 
         encode_enum_variant_info(ecx,
@@ -1077,6 +1106,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_attributes(rbml_w, &item.attrs);
         encode_path(rbml_w, path.clone());
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         encode_visibility(rbml_w, vis);
         encode_repr_attrs(rbml_w, ecx, &item.attrs);
 
@@ -1167,6 +1197,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         }
         encode_path(rbml_w, path.clone());
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         rbml_w.end_tag();
 
         // Iterate down the trait items, emitting them. We rely on the
@@ -1236,6 +1267,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         encode_attributes(rbml_w, &item.attrs);
         encode_visibility(rbml_w, vis);
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         for &method_def_id in tcx.trait_item_def_ids(def_id).iter() {
             rbml_w.start_tag(tag_item_trait_item);
             match method_def_id {
@@ -1274,8 +1306,10 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
 
             encode_parent_item(rbml_w, def_id);
 
-            let stab = stability::lookup(tcx, item_def_id.def_id());
+            let stab = stability::lookup_stability(tcx, item_def_id.def_id());
+            let depr = stability::lookup_deprecation(tcx, item_def_id.def_id());
             encode_stability(rbml_w, stab);
+            encode_deprecation(rbml_w, depr);
 
             let trait_item_type =
                 tcx.impl_or_trait_item(item_def_id.def_id());
@@ -1407,8 +1441,10 @@ fn encode_info_for_foreign_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
             encode_inlined_item(ecx, rbml_w, InlinedItemRef::Foreign(nitem));
         }
         encode_attributes(rbml_w, &*nitem.attrs);
-        let stab = stability::lookup(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id));
+        let stab = stability::lookup_stability(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id));
+        let depr = stability::lookup_deprecation(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id));
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         encode_symbol(ecx, rbml_w, nitem.id);
         encode_method_argument_names(rbml_w, &*fndecl);
       }
@@ -1420,8 +1456,10 @@ fn encode_info_for_foreign_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
         }
         encode_bounds_and_type_for_item(rbml_w, ecx, index, nitem.id);
         encode_attributes(rbml_w, &*nitem.attrs);
-        let stab = stability::lookup(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id));
+        let stab = stability::lookup_stability(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id));
+        let depr = stability::lookup_deprecation(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id));
         encode_stability(rbml_w, stab);
+        encode_deprecation(rbml_w, depr);
         encode_symbol(ecx, rbml_w, nitem.id);
         encode_name(rbml_w, nitem.name);
       }
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 40e71466709..5c88576acb7 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -120,7 +120,7 @@ fn try_inline_def(cx: &DocContext, tcx: &ty::ctxt,
         attrs: load_attrs(cx, tcx, did),
         inner: inner,
         visibility: Some(hir::Public),
-        stability: stability::lookup(tcx, did).clean(cx),
+        stability: stability::lookup_stability(tcx, did).clean(cx),
         def_id: did,
     });
     Some(ret)
@@ -303,7 +303,7 @@ pub fn build_impl(cx: &DocContext,
             name: None,
             attrs: attrs,
             visibility: Some(hir::Inherited),
-            stability: stability::lookup(tcx, did).clean(cx),
+            stability: stability::lookup_stability(tcx, did).clean(cx),
             def_id: did,
         });
     }
@@ -333,7 +333,7 @@ pub fn build_impl(cx: &DocContext,
                     source: clean::Span::empty(),
                     attrs: vec![],
                     visibility: None,
-                    stability: stability::lookup(tcx, did).clean(cx),
+                    stability: stability::lookup_stability(tcx, did).clean(cx),
                     def_id: did
                 })
             }
@@ -381,7 +381,7 @@ pub fn build_impl(cx: &DocContext,
                     source: clean::Span::empty(),
                     attrs: vec![],
                     visibility: None,
-                    stability: stability::lookup(tcx, did).clean(cx),
+                    stability: stability::lookup_stability(tcx, did).clean(cx),
                     def_id: did
                 })
             }
@@ -414,7 +414,7 @@ pub fn build_impl(cx: &DocContext,
         name: None,
         attrs: attrs,
         visibility: Some(hir::Inherited),
-        stability: stability::lookup(tcx, did).clean(cx),
+        stability: stability::lookup_stability(tcx, did).clean(cx),
         def_id: did,
     });
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 1336acb6b03..942cc135aa8 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -62,7 +62,7 @@ mod simplify;
 
 // extract the stability index for a node from tcx, if possible
 fn get_stability(cx: &DocContext, def_id: DefId) -> Option<Stability> {
-    cx.tcx_opt().and_then(|tcx| stability::lookup(tcx, def_id)).clean(cx)
+    cx.tcx_opt().and_then(|tcx| stability::lookup_stability(tcx, def_id)).clean(cx)
 }
 
 pub trait Clean<T> {
@@ -2689,12 +2689,12 @@ impl Clean<Stability> for attr::Stability {
                 attr::Stable {ref since} => since.to_string(),
                 _ => "".to_string(),
             },
-            deprecated_since: match self.depr {
-                Some(attr::Deprecation {ref since, ..}) => since.to_string(),
+            deprecated_since: match self.rustc_depr {
+                Some(attr::RustcDeprecation {ref since, ..}) => since.to_string(),
                 _=> "".to_string(),
             },
             reason: {
-                if let Some(ref depr) = self.depr {
+                if let Some(ref depr) = self.rustc_depr {
                     depr.reason.to_string()
                 } else if let attr::Unstable {reason: Some(ref reason), ..} = self.level {
                     reason.to_string()
@@ -2782,7 +2782,7 @@ impl<'tcx> Clean<Item> for ty::AssociatedType<'tcx> {
             inner: AssociatedTypeItem(bounds, self.ty.clean(cx)),
             visibility: self.vis.clean(cx),
             def_id: self.def_id,
-            stability: stability::lookup(cx.tcx(), self.def_id).clean(cx),
+            stability: stability::lookup_stability(cx.tcx(), self.def_id).clean(cx),
         }
     }
 }
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 17291233e11..89527c54a38 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -64,7 +64,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
     fn stability(&self, id: ast::NodeId) -> Option<attr::Stability> {
         self.cx.tcx_opt().and_then(|tcx| {
             self.cx.map.opt_local_def_id(id)
-                       .and_then(|def_id| stability::lookup(tcx, def_id))
+                       .and_then(|def_id| stability::lookup_stability(tcx, def_id))
                        .cloned()
         })
     }
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index e828d8ae248..d511ce09a36 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -398,7 +398,7 @@ pub fn cfg_matches<T: CfgDiag>(cfgs: &[P<MetaItem>],
 pub struct Stability {
     pub level: StabilityLevel,
     pub feature: InternedString,
-    pub depr: Option<Deprecation>,
+    pub rustc_depr: Option<RustcDeprecation>,
 }
 
 /// The available stability levels.
@@ -410,11 +410,17 @@ pub enum StabilityLevel {
 }
 
 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
-pub struct Deprecation {
+pub struct RustcDeprecation {
     pub since: InternedString,
     pub reason: InternedString,
 }
 
+#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
+pub struct Deprecation {
+    pub since: Option<InternedString>,
+    pub note: Option<InternedString>,
+}
+
 impl StabilityLevel {
     pub fn is_unstable(&self) -> bool { if let Unstable {..} = *self { true } else { false }}
     pub fn is_stable(&self) -> bool { if let Stable {..} = *self { true } else { false }}
@@ -427,7 +433,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
     where I: Iterator<Item = &'a Attribute>
 {
     let mut stab: Option<Stability> = None;
-    let mut depr: Option<Deprecation> = None;
+    let mut rustc_depr: Option<RustcDeprecation> = None;
 
     'outer: for attr in attrs_iter {
         let tag = attr.name();
@@ -456,7 +462,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
 
             match tag {
                 "rustc_deprecated" => {
-                    if depr.is_some() {
+                    if rustc_depr.is_some() {
                         diagnostic.span_err(item_sp, "multiple rustc_deprecated attributes");
                         break
                     }
@@ -477,7 +483,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
 
                     match (since, reason) {
                         (Some(since), Some(reason)) => {
-                            depr = Some(Deprecation {
+                            rustc_depr = Some(RustcDeprecation {
                                 since: since,
                                 reason: reason,
                             })
@@ -529,7 +535,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
                                     }
                                 },
                                 feature: feature,
-                                depr: None,
+                                rustc_depr: None,
                             })
                         }
                         (None, _, _) => {
@@ -569,7 +575,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
                                     since: since,
                                 },
                                 feature: feature,
-                                depr: None,
+                                rustc_depr: None,
                             })
                         }
                         (None, _) => {
@@ -591,12 +597,12 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
     }
 
     // Merge the deprecation info into the stability info
-    if let Some(depr) = depr {
+    if let Some(rustc_depr) = rustc_depr {
         if let Some(ref mut stab) = stab {
             if let Unstable {reason: ref mut reason @ None, ..} = stab.level {
-                *reason = Some(depr.reason.clone())
+                *reason = Some(rustc_depr.reason.clone())
             }
-            stab.depr = Some(depr);
+            stab.rustc_depr = Some(rustc_depr);
         } else {
             diagnostic.span_err(item_sp, "rustc_deprecated attribute must be paired with \
                                           either stable or unstable attribute");
@@ -606,12 +612,77 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
     stab
 }
 
+fn find_deprecation_generic<'a, I>(diagnostic: &SpanHandler,
+                                 attrs_iter: I,
+                                 item_sp: Span)
+                                 -> Option<Deprecation>
+    where I: Iterator<Item = &'a Attribute>
+{
+    let mut depr: Option<Deprecation> = None;
+
+    'outer: for attr in attrs_iter {
+        if attr.name() != "deprecated" {
+            continue
+        }
+
+        mark_used(attr);
+
+        if depr.is_some() {
+            diagnostic.span_err(item_sp, "multiple deprecated attributes");
+            break
+        }
+
+        depr = if let Some(metas) = attr.meta_item_list() {
+            let get = |meta: &MetaItem, item: &mut Option<InternedString>| {
+                if item.is_some() {
+                    diagnostic.span_err(meta.span, &format!("multiple '{}' items",
+                                                             meta.name()));
+                    return false
+                }
+                if let Some(v) = meta.value_str() {
+                    *item = Some(v);
+                    true
+                } else {
+                    diagnostic.span_err(meta.span, "incorrect meta item");
+                    false
+                }
+            };
+
+            let mut since = None;
+            let mut note = None;
+            for meta in metas {
+                match &*meta.name() {
+                    "since" => if !get(meta, &mut since) { continue 'outer },
+                    "note" => if !get(meta, &mut note) { continue 'outer },
+                    _ => {
+                        diagnostic.span_err(meta.span, &format!("unknown meta item '{}'",
+                                                                meta.name()));
+                        continue 'outer
+                    }
+                }
+            }
+
+            Some(Deprecation {since: since, note: note})
+        } else {
+            Some(Deprecation{since: None, note: None})
+        }
+    }
+
+    depr
+}
+
 /// Find the first stability attribute. `None` if none exists.
 pub fn find_stability(diagnostic: &SpanHandler, attrs: &[Attribute],
                       item_sp: Span) -> Option<Stability> {
     find_stability_generic(diagnostic, attrs.iter(), item_sp)
 }
 
+/// Find the deprecation attribute. `None` if none exists.
+pub fn find_deprecation(diagnostic: &SpanHandler, attrs: &[Attribute],
+                      item_sp: Span) -> Option<Deprecation> {
+    find_deprecation_generic(diagnostic, attrs.iter(), item_sp)
+}
+
 pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[P<MetaItem>]) {
     let mut set = HashSet::new();
     for meta in metas {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index c456b7dc8b9..6b138b50f03 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -230,6 +230,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
 
     // Allow attributes on expressions and non-item statements
     ("stmt_expr_attributes", "1.6.0", Some(15701), Active),
+
+    // Allows `#[deprecated]` attribute
+    ("deprecated", "1.6.0", Some(29935), Active),
 ];
 // (changing above list without updating src/doc/reference.md makes @cmr sad)
 
@@ -377,6 +380,7 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
     ("must_use", Whitelisted, Ungated),
     ("stable", Whitelisted, Ungated),
     ("unstable", Whitelisted, Ungated),
+    ("deprecated", Whitelisted, Ungated),
 
     ("rustc_paren_sugar", Normal, Gated("unboxed_closures",
                                         "unboxed_closures are still evolving")),
@@ -539,6 +543,7 @@ pub struct Features {
     pub braced_empty_structs: bool,
     pub staged_api: bool,
     pub stmt_expr_attributes: bool,
+    pub deprecated: bool,
 }
 
 impl Features {
@@ -573,6 +578,7 @@ impl Features {
             braced_empty_structs: false,
             staged_api: false,
             stmt_expr_attributes: false,
+            deprecated: false,
         }
     }
 }
@@ -1151,6 +1157,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
         braced_empty_structs: cx.has_feature("braced_empty_structs"),
         staged_api: cx.has_feature("staged_api"),
         stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"),
+        deprecated: cx.has_feature("deprecated"),
     }
 }
 
diff --git a/src/test/auxiliary/deprecation-lint.rs b/src/test/auxiliary/deprecation-lint.rs
new file mode 100644
index 00000000000..61c91590b31
--- /dev/null
+++ b/src/test/auxiliary/deprecation-lint.rs
@@ -0,0 +1,87 @@
+// Copyright 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.
+
+#![feature(deprecated)]
+
+#[deprecated(since = "1.0.0", note = "text")]
+pub fn deprecated() {}
+#[deprecated(since = "1.0.0", note = "text")]
+pub fn deprecated_text() {}
+
+pub struct MethodTester;
+
+impl MethodTester {
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub fn method_deprecated(&self) {}
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub fn method_deprecated_text(&self) {}
+}
+
+pub trait Trait {
+    #[deprecated(since = "1.0.0", note = "text")]
+    fn trait_deprecated(&self) {}
+    #[deprecated(since = "1.0.0", note = "text")]
+    fn trait_deprecated_text(&self) {}
+}
+
+impl Trait for MethodTester {}
+
+#[deprecated(since = "1.0.0", note = "text")]
+pub struct DeprecatedStruct {
+    pub i: isize
+}
+
+#[deprecated(since = "1.0.0", note = "text")]
+pub struct DeprecatedUnitStruct;
+
+pub enum Enum {
+    #[deprecated(since = "1.0.0", note = "text")]
+    DeprecatedVariant,
+}
+
+#[deprecated(since = "1.0.0", note = "text")]
+pub struct DeprecatedTupleStruct(pub isize);
+
+pub struct Stable {
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub override2: u8,
+}
+
+pub struct Stable2(pub u8, pub u8, #[deprecated(since = "1.0.0", note = "text")] pub u8);
+
+#[deprecated(since = "1.0.0", note = "text")]
+pub struct Deprecated {
+    pub inherit: u8,
+}
+
+#[deprecated(since = "1.0.0", note = "text")]
+pub struct Deprecated2(pub u8,
+                       pub u8,
+                       pub u8);
+
+#[deprecated(since = "1.0.0", note = "text")]
+pub mod deprecated_mod {
+    pub fn deprecated() {}
+}
+
+#[macro_export]
+macro_rules! macro_test {
+    () => (deprecated());
+}
+
+#[macro_export]
+macro_rules! macro_test_arg {
+    ($func:expr) => ($func);
+}
+
+#[macro_export]
+macro_rules! macro_test_arg_nested {
+    ($func:ident) => (macro_test_arg!($func()));
+}
diff --git a/src/test/compile-fail/deprecation-in-staged-api.rs b/src/test/compile-fail/deprecation-in-staged-api.rs
new file mode 100644
index 00000000000..4f4aed21f99
--- /dev/null
+++ b/src/test/compile-fail/deprecation-in-staged-api.rs
@@ -0,0 +1,18 @@
+// Copyright 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.
+
+// #[deprecated] can't be used in staged api
+
+#![feature(deprecated, staged_api)]
+
+#![stable(feature = "test_feature", since = "1.0.0")]
+
+#[deprecated]
+fn main() { } //~ERROR `#[deprecated]` cannot be used in staged api
diff --git a/src/test/compile-fail/deprecation-lint-2.rs b/src/test/compile-fail/deprecation-lint-2.rs
new file mode 100644
index 00000000000..2817e06652a
--- /dev/null
+++ b/src/test/compile-fail/deprecation-lint-2.rs
@@ -0,0 +1,23 @@
+// Copyright 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.
+
+// aux-build:deprecation-lint.rs
+// error-pattern: use of deprecated item
+
+#![deny(deprecated)]
+
+#[macro_use]
+extern crate deprecation_lint;
+
+use deprecation_lint::*;
+
+fn main() {
+    macro_test!();
+}
diff --git a/src/test/compile-fail/deprecation-lint-3.rs b/src/test/compile-fail/deprecation-lint-3.rs
new file mode 100644
index 00000000000..7faaa181d39
--- /dev/null
+++ b/src/test/compile-fail/deprecation-lint-3.rs
@@ -0,0 +1,24 @@
+// Copyright 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.
+
+// aux-build:deprecation-lint.rs
+// error-pattern: use of deprecated item
+
+#![deny(deprecated)]
+#![allow(warnings)]
+
+#[macro_use]
+extern crate deprecation_lint;
+
+use deprecation_lint::*;
+
+fn main() {
+    macro_test_arg_nested!(deprecated_text);
+}
diff --git a/src/test/compile-fail/deprecation-lint.rs b/src/test/compile-fail/deprecation-lint.rs
new file mode 100644
index 00000000000..db6d5fd63e5
--- /dev/null
+++ b/src/test/compile-fail/deprecation-lint.rs
@@ -0,0 +1,384 @@
+// Copyright 2013-2014 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.
+
+// aux-build:deprecation-lint.rs
+
+#![feature(deprecated)]
+
+#![deny(deprecated)]
+#![allow(warnings)]
+
+#[macro_use]
+extern crate deprecation_lint;
+
+mod cross_crate {
+    use deprecation_lint::*;
+
+    fn test() {
+        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
+
+        let _ = DeprecatedStruct { //~ ERROR use of deprecated item
+            i: 0 //~ ERROR use of deprecated item
+        };
+
+        let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item
+
+        let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item
+
+        let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item
+
+        // At the moment, the lint checker only checks stability in
+        // in the arguments of macros.
+        // 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!(macro_test_arg!(deprecated_text())); //~ ERROR 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
+    }
+
+    fn test_method_object(foo: &Trait) {
+        foo.trait_deprecated(); //~ ERROR use of deprecated item
+        foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
+    }
+
+    pub fn foo() {
+        let x = Stable {
+            override2: 3,
+            //~^ ERROR use of deprecated item
+        };
+
+        let _ = x.override2;
+        //~^ ERROR use of deprecated item
+
+        let Stable {
+            override2: _
+            //~^ ERROR use of deprecated item
+        } = x;
+        // all fine
+        let Stable { .. } = x;
+
+        let x = Stable2(1, 2, 3);
+
+        let _ = x.2;
+        //~^ ERROR use of deprecated item
+
+        let Stable2(_,
+                   _,
+                   _)
+            //~^ ERROR use of deprecated item
+            = x;
+        // all fine
+        let Stable2(..) = x;
+
+        let x = Deprecated {
+            //~^ ERROR use of deprecated item
+            inherit: 1,
+            //~^ ERROR use of deprecated item
+        };
+
+        let _ = x.inherit;
+        //~^ ERROR use of deprecated item
+
+        let Deprecated {
+            //~^ ERROR use of deprecated item
+            inherit: _,
+            //~^ ERROR use of deprecated item
+        } = x;
+
+        let Deprecated
+            //~^ ERROR use of deprecated item
+            { .. } = x;
+
+        let x = Deprecated2(1, 2, 3);
+        //~^ ERROR use of deprecated item
+
+        let _ = x.0;
+        //~^ ERROR use of deprecated item
+        let _ = x.1;
+        //~^ ERROR use of deprecated item
+        let _ = x.2;
+        //~^ ERROR use of deprecated item
+
+        let Deprecated2
+        //~^ ERROR use of deprecated item
+            (_,
+             //~^ ERROR use of deprecated item
+             _,
+             //~^ ERROR use of deprecated item
+             _)
+             //~^ ERROR use of deprecated item
+            = x;
+        let Deprecated2
+        //~^ ERROR use of deprecated item
+            // the patterns are all fine:
+            (..) = x;
+    }
+}
+
+mod inheritance {
+    use deprecation_lint::*;
+
+    fn test_inheritance() {
+        deprecated_mod::deprecated(); //~ ERROR use of deprecated item
+    }
+}
+
+mod this_crate {
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub fn deprecated() {}
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub fn deprecated_text() {}
+
+    pub struct MethodTester;
+
+    impl MethodTester {
+        #[deprecated(since = "1.0.0", note = "text")]
+        pub fn method_deprecated(&self) {}
+        #[deprecated(since = "1.0.0", note = "text")]
+        pub fn method_deprecated_text(&self) {}
+    }
+
+    pub trait Trait {
+        #[deprecated(since = "1.0.0", note = "text")]
+        fn trait_deprecated(&self) {}
+        #[deprecated(since = "1.0.0", note = "text")]
+        fn trait_deprecated_text(&self) {}
+    }
+
+    impl Trait for MethodTester {}
+
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub struct DeprecatedStruct {
+        i: isize
+    }
+    pub struct UnstableStruct {
+        i: isize
+    }
+    pub struct StableStruct {
+        i: isize
+    }
+
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub struct DeprecatedUnitStruct;
+
+    pub enum Enum {
+        #[deprecated(since = "1.0.0", note = "text")]
+        DeprecatedVariant,
+    }
+
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub struct DeprecatedTupleStruct(isize);
+
+    fn test() {
+        // Only the deprecated cases of the following should generate
+        // errors, because other stability attributes now have meaning
+        // only *across* crates, not within a single 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
+
+        let _ = DeprecatedStruct {
+            //~^ ERROR use of deprecated item
+            i: 0 //~ ERROR use of deprecated item
+        };
+
+        let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item
+
+        let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item
+
+        let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item
+    }
+
+    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
+    }
+
+    fn test_method_object(foo: &Trait) {
+        foo.trait_deprecated(); //~ ERROR use of deprecated item
+        foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
+    }
+
+    #[deprecated(since = "1.0.0", note = "text")]
+    fn test_fn_body() {
+        fn fn_in_body() {}
+        fn_in_body(); //~ ERROR use of deprecated item: text
+    }
+
+    impl MethodTester {
+        #[deprecated(since = "1.0.0", note = "text")]
+        fn test_method_body(&self) {
+            fn fn_in_body() {}
+            fn_in_body(); //~ ERROR use of deprecated item: text
+        }
+    }
+
+    #[deprecated(since = "1.0.0", note = "text")]
+    pub trait DeprecatedTrait {
+        fn dummy(&self) { }
+    }
+
+    struct S;
+
+    impl DeprecatedTrait for S { } //~ ERROR use of deprecated item
+
+    trait LocalTrait : DeprecatedTrait { } //~ ERROR use of deprecated item
+}
+
+mod this_crate2 {
+    struct Stable {
+        #[deprecated(since = "1.0.0", note = "text")]
+        override2: u8,
+    }
+
+    struct Stable2(u8,
+                   u8,
+                   #[deprecated(since = "1.0.0", note = "text")] u8);
+
+    #[deprecated(since = "1.0.0", note = "text")]
+    struct Deprecated {
+        inherit: u8,
+    }
+
+    #[deprecated(since = "1.0.0", note = "text")]
+    struct Deprecated2(u8,
+                       u8,
+                       u8);
+
+    pub fn foo() {
+        let x = Stable {
+            override2: 3,
+            //~^ ERROR use of deprecated item
+        };
+
+        let _ = x.override2;
+        //~^ ERROR use of deprecated item
+
+        let Stable {
+            override2: _
+            //~^ ERROR use of deprecated item
+        } = x;
+        // all fine
+        let Stable { .. } = x;
+
+        let x = Stable2(1, 2, 3);
+
+        let _ = x.2;
+        //~^ ERROR use of deprecated item
+
+        let Stable2(_,
+                   _,
+                   _)
+            //~^ ERROR use of deprecated item
+            = x;
+        // all fine
+        let Stable2(..) = x;
+
+        let x = Deprecated {
+            //~^ ERROR use of deprecated item
+            inherit: 1,
+            //~^ ERROR use of deprecated item
+        };
+
+        let _ = x.inherit;
+        //~^ ERROR use of deprecated item
+
+        let Deprecated {
+            //~^ ERROR use of deprecated item
+            inherit: _,
+            //~^ ERROR use of deprecated item
+        } = x;
+
+        let Deprecated
+            //~^ ERROR use of deprecated item
+            // the patterns are all fine:
+            { .. } = x;
+
+        let x = Deprecated2(1, 2, 3);
+        //~^ ERROR use of deprecated item
+
+        let _ = x.0;
+        //~^ ERROR use of deprecated item
+        let _ = x.1;
+        //~^ ERROR use of deprecated item
+        let _ = x.2;
+        //~^ ERROR use of deprecated item
+
+        let Deprecated2
+        //~^ ERROR use of deprecated item
+            (_,
+             //~^ ERROR use of deprecated item
+             _,
+             //~^ ERROR use of deprecated item
+             _)
+            //~^ ERROR use of deprecated item
+            = x;
+        let Deprecated2
+        //~^ ERROR use of deprecated item
+            // the patterns are all fine:
+            (..) = x;
+    }
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/deprecation-sanity.rs b/src/test/compile-fail/deprecation-sanity.rs
new file mode 100644
index 00000000000..6ee5cd2c7e3
--- /dev/null
+++ b/src/test/compile-fail/deprecation-sanity.rs
@@ -0,0 +1,39 @@
+// Copyright 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.
+
+// Various checks that deprecation attributes are used correctly
+
+#![feature(deprecated)]
+
+mod bogus_attribute_types_1 {
+    #[deprecated(since = "a", note = "a", reason)] //~ ERROR unknown meta item 'reason'
+    fn f1() { }
+
+    #[deprecated(since = "a", note)] //~ ERROR incorrect meta item
+    fn f2() { }
+
+    #[deprecated(since, note = "a")] //~ ERROR incorrect meta item
+    fn f3() { }
+
+    #[deprecated(since = "a", note(b))] //~ ERROR incorrect meta item
+    fn f5() { }
+
+    #[deprecated(since(b), note = "a")] //~ ERROR incorrect meta item
+    fn f6() { }
+}
+
+#[deprecated(since = "a", note = "b")]
+#[deprecated(since = "a", note = "b")]
+fn multiple1() { } //~ ERROR multiple deprecated attributes
+
+#[deprecated(since = "a", since = "b", note = "c")] //~ ERROR multiple 'since' items
+fn f1() { }
+
+fn main() { }