]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc/middle/stability.rs
Auto merge of #30341 - pnkfelix:call-site-scope, r=nikomatsakis
[rust.git] / src / librustc / middle / stability.rs
index 4cc374431f4c76557c02947011bb35f5c06aae53..31158901775f35dc911a066aec9a29ebd05e82fa 100644 (file)
 
 use session::Session;
 use lint;
-use metadata::cstore::LOCAL_CRATE;
+use middle::cstore::{CrateStore, LOCAL_CRATE};
 use middle::def;
 use middle::def_id::{CRATE_DEF_INDEX, DefId};
 use middle::ty;
-use middle::privacy::PublicItems;
-use metadata::csearch;
+use middle::privacy::AccessLevels;
 use syntax::parse::token::InternedString;
 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;
-use rustc_front::hir::{FnDecl, Block, Crate, Item, Generics, StructField, Variant};
-use rustc_front::visit::{self, FnKind, Visitor};
+use rustc_front::hir::{Block, Crate, Item, Generics, StructField, Variant};
+use rustc_front::intravisit::{self, Visitor};
 
 use std::mem::replace;
 use std::cmp::Ordering;
@@ -48,11 +47,22 @@ pub fn from_attr_level(level: &attr::StabilityLevel) -> Self {
     }
 }
 
+#[derive(PartialEq)]
+enum AnnotationKind {
+    // Annotation is required if not inherited from unstable parents
+    Required,
+    // Annotation is useless, reject it
+    Prohibited,
+    // Annotation itself is useless, but it can be propagated to children
+    Container,
+}
+
 /// A stability index, giving the stability level for items and methods.
 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>
@@ -62,210 +72,253 @@ pub struct Index<'tcx> {
 struct Annotator<'a, 'tcx: 'a> {
     tcx: &'a ty::ctxt<'tcx>,
     index: &'a mut Index<'tcx>,
-    parent: Option<&'tcx Stability>,
-    export_map: &'a PublicItems,
+    parent_stab: Option<&'tcx Stability>,
+    parent_depr: Option<Deprecation>,
+    access_levels: &'a AccessLevels,
+    in_trait_impl: bool,
+    in_enum: bool,
 }
 
 impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
     // Determine the stability for a node based on its attributes and inherited
     // stability. The stability is recorded in the index and used as the parent.
-    fn annotate<F>(&mut self, id: NodeId, use_parent: bool,
-                   attrs: &Vec<Attribute>, item_sp: Span, f: F, required: bool) where
-        F: FnOnce(&mut Annotator),
+    fn annotate<F>(&mut self, id: NodeId, attrs: &Vec<Attribute>,
+                   item_sp: Span, kind: AnnotationKind, visit_children: F)
+        where F: FnOnce(&mut Annotator)
     {
-        if self.index.staged_api[&LOCAL_CRATE] {
+        if self.index.staged_api[&LOCAL_CRATE] && self.tcx.sess.features.borrow().staged_api {
             debug!("annotate(id = {:?}, attrs = {:?})", id, attrs);
-            match attr::find_stability(self.tcx.sess.diagnostic(), attrs, item_sp) {
-                Some(mut stab) => {
-                    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(..) = 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.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_stab {
+                    if parent_stab.rustc_depr.is_some() && stab.rustc_depr.is_none() {
+                        stab.rustc_depr = parent_stab.rustc_depr.clone()
                     }
+                }
 
-                    let stab = self.tcx.intern_stability(stab);
-
-                    // Check if deprecated_since < stable_since. If it is,
-                    // this is *almost surely* an accident.
-                    let deprecated_predates_stable = match (&stab.depr, &stab.level) {
-                        (&Some(attr::Deprecation {since: ref dep_since, ..}),
-                               &attr::Stable {since: ref stab_since}) => {
-                            // explicit version of iter::order::lt to handle parse errors properly
-                            let mut is_less = false;
-                            for (dep_v, stab_v) in dep_since.split(".").zip(stab_since.split(".")) {
-                                match (dep_v.parse::<u64>(), stab_v.parse::<u64>()) {
-                                    (Ok(dep_v), Ok(stab_v)) => match dep_v.cmp(&stab_v) {
-                                        Ordering::Less => {
-                                            is_less = true;
-                                            break;
-                                        }
-                                        Ordering::Equal => { continue; }
-                                        Ordering::Greater => { break; }
-                                    },
-                                    _ => {
-                                        self.tcx.sess.span_err(item_sp,
-                                            "Invalid stability or deprecation version found");
-                                        // act like it isn't less because the question is now
-                                        // nonsensical, and this makes us not do anything else
-                                        // interesting.
-                                        break;
-                                    }
+                let stab = self.tcx.intern_stability(stab);
+
+                // Check if deprecated_since < stable_since. If it is,
+                // this is *almost surely* an accident.
+                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()) {
+                            match dep_v.cmp(&stab_v) {
+                                Ordering::Less => {
+                                    self.tcx.sess.span_err(item_sp, "An API can't be stabilized \
+                                                                     after it is deprecated");
+                                    break
                                 }
+                                Ordering::Equal => continue,
+                                Ordering::Greater => break,
                             }
-                            is_less
-                        },
-                        _ => false,
-                    };
-
-                    if deprecated_predates_stable {
-                        self.tcx.sess.span_err(item_sp,
-                            "An API can't be stabilized after it is deprecated");
+                        } else {
+                            // Act like it isn't less because the question is now nonsensical,
+                            // and this makes us not do anything else interesting.
+                            self.tcx.sess.span_err(item_sp, "Invalid stability or deprecation \
+                                                             version found");
+                            break
+                        }
                     }
+                }
 
-                    let def_id = self.tcx.map.local_def_id(id);
-                    self.index.map.insert(def_id, Some(stab));
-
-                    // Don't inherit #[stable(feature = "rust1", since = "1.0.0")]
-                    if !stab.level.is_stable() {
-                        let parent = replace(&mut self.parent, Some(stab));
-                        f(self);
-                        self.parent = parent;
-                    } else {
-                        f(self);
+                let def_id = self.tcx.map.local_def_id(id);
+                self.index.stab_map.insert(def_id, Some(stab));
+
+                let orig_parent_stab = replace(&mut self.parent_stab, Some(stab));
+                visit_children(self);
+                self.parent_stab = orig_parent_stab;
+            } else {
+                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_stab {
+                    if stab.level.is_unstable() {
+                        let def_id = self.tcx.map.local_def_id(id);
+                        self.index.stab_map.insert(def_id, Some(stab));
+                        is_error = false;
                     }
                 }
-                None => {
-                    debug!("annotate: not found, use_parent = {:?}, parent = {:?}",
-                           use_parent, self.parent);
-                    if use_parent {
-                        if let Some(stab) = self.parent {
-                            let def_id = self.tcx.map.local_def_id(id);
-                            self.index.map.insert(def_id, Some(stab));
-                        } else if self.index.staged_api[&LOCAL_CRATE] && required
-                            && self.export_map.contains(&id)
-                            && !self.tcx.sess.opts.test {
-                                self.tcx.sess.span_err(item_sp,
-                                                       "This node does not \
-                                                        have a stability attribute");
-                            }
-                    }
-                    f(self);
+                if is_error {
+                    self.tcx.sess.span_err(item_sp, "This node does not have \
+                                                     a stability attribute");
                 }
+                visit_children(self);
             }
         } else {
-            // Emit warnings for non-staged-api crates. These should be errors.
+            // Emit errors for non-staged-api crates.
             for attr in attrs {
                 let tag = attr.name();
-                if tag == "unstable" || tag == "stable" || tag == "deprecated" {
+                if tag == "unstable" || tag == "stable" || tag == "rustc_deprecated" {
                     attr::mark_used(attr);
-                    self.tcx.sess.span_err(attr.span(),
-                                       "stability attributes may not be used outside \
-                                        of the standard library");
+                    self.tcx.sess.span_err(attr.span(), "stability attributes may not be used \
+                                                         outside of the standard library");
                 }
             }
-            f(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);
+            }
         }
     }
 }
 
 impl<'a, 'tcx, 'v> Visitor<'v> for Annotator<'a, 'tcx> {
-    fn visit_item(&mut self, i: &Item) {
-        // FIXME (#18969): the following is a hack around the fact
-        // that we cannot currently annotate the stability of
-        // `deriving`.  Basically, we do *not* allow stability
-        // inheritance on trait implementations, so that derived
-        // implementations appear to be unannotated. This then allows
-        // derived implementations to be automatically tagged with the
-        // stability of the trait. This is WRONG, but expedient to get
-        // libstd stabilized for the 1.0 release.
-        let use_parent = match i.node {
-            hir::ItemImpl(_, _, _, Some(_), _, _) => false,
-            _ => true,
-        };
-
-        // In case of a `pub use <mod>;`, we should not error since the stability
-        // is inherited from the module itself
-        let required = match i.node {
-            hir::ItemUse(_) => i.vis != hir::Public,
-            _ => true
-        };
-
-        self.annotate(i.id, use_parent, &i.attrs, i.span,
-                      |v| visit::walk_item(v, i), required);
+    /// Because stability levels are scoped lexically, we want to walk
+    /// nested items in the context of the outer item, so enable
+    /// deep-walking.
+    fn visit_nested_item(&mut self, item: hir::ItemId) {
+        self.visit_item(self.tcx.map.expect_item(item.id))
+    }
 
-        if let hir::ItemStruct(ref sd, _) = i.node {
-            if !sd.is_struct() {
-                self.annotate(sd.id(), true, &i.attrs, i.span, |_| {}, true)
+    fn visit_item(&mut self, i: &Item) {
+        let orig_in_trait_impl = self.in_trait_impl;
+        let orig_in_enum = self.in_enum;
+        let mut kind = AnnotationKind::Required;
+        match i.node {
+            // Inherent impls and foreign modules serve only as containers for other items,
+            // they don't have their own stability. They still can be annotated as unstable
+            // and propagate this unstability to children, but this annotation is completely
+            // optional. They inherit stability from their parents when unannotated.
+            hir::ItemImpl(_, _, _, None, _, _) | hir::ItemForeignMod(..) => {
+                self.in_trait_impl = false;
+                kind = AnnotationKind::Container;
             }
+            hir::ItemImpl(_, _, _, Some(_), _, _) => {
+                self.in_trait_impl = true;
+            }
+            hir::ItemStruct(ref sd, _) => {
+                self.in_enum = false;
+                if !sd.is_struct() {
+                    self.annotate(sd.id(), &i.attrs, i.span, AnnotationKind::Required, |_| {})
+                }
+            }
+            hir::ItemEnum(..) => {
+                self.in_enum = true;
+            }
+            _ => {}
         }
-    }
 
-    fn visit_fn(&mut self, _: FnKind<'v>, _: &'v FnDecl,
-                _: &'v Block, _: Span, _: NodeId) {
-        // Items defined in a function body have no reason to have
-        // a stability attribute, so we don't recurse.
+        self.annotate(i.id, &i.attrs, i.span, kind, |v| {
+            intravisit::walk_item(v, i)
+        });
+        self.in_trait_impl = orig_in_trait_impl;
+        self.in_enum = orig_in_enum;
     }
 
     fn visit_trait_item(&mut self, ti: &hir::TraitItem) {
-        self.annotate(ti.id, true, &ti.attrs, ti.span,
-                      |v| visit::walk_trait_item(v, ti), true);
+        self.annotate(ti.id, &ti.attrs, ti.span, AnnotationKind::Required, |v| {
+            intravisit::walk_trait_item(v, ti);
+        });
     }
 
     fn visit_impl_item(&mut self, ii: &hir::ImplItem) {
-        self.annotate(ii.id, true, &ii.attrs, ii.span,
-                      |v| visit::walk_impl_item(v, ii), false);
+        let kind = if self.in_trait_impl {
+            AnnotationKind::Prohibited
+        } else {
+            AnnotationKind::Required
+        };
+        self.annotate(ii.id, &ii.attrs, ii.span, kind, |v| {
+            intravisit::walk_impl_item(v, ii);
+        });
     }
 
     fn visit_variant(&mut self, var: &Variant, g: &'v Generics, item_id: NodeId) {
-        self.annotate(var.node.data.id(), true, &var.node.attrs, var.span,
-                      |v| visit::walk_variant(v, var, g, item_id), true)
+        self.annotate(var.node.data.id(), &var.node.attrs, var.span, AnnotationKind::Required, |v| {
+            intravisit::walk_variant(v, var, g, item_id);
+        })
     }
 
     fn visit_struct_field(&mut self, s: &StructField) {
-        self.annotate(s.node.id, true, &s.node.attrs, s.span,
-                      |v| visit::walk_struct_field(v, s), !s.node.kind.is_unnamed());
+        // FIXME: This is temporary, can't use attributes with tuple variant fields until snapshot
+        let kind = if self.in_enum && s.node.kind.is_unnamed() {
+            AnnotationKind::Prohibited
+        } else {
+            AnnotationKind::Required
+        };
+        self.annotate(s.node.id, &s.node.attrs, s.span, kind, |v| {
+            intravisit::walk_struct_field(v, s);
+        });
     }
 
     fn visit_foreign_item(&mut self, i: &hir::ForeignItem) {
-        self.annotate(i.id, true, &i.attrs, i.span, |_| {}, true);
+        self.annotate(i.id, &i.attrs, i.span, AnnotationKind::Required, |v| {
+            intravisit::walk_foreign_item(v, i);
+        });
+    }
+
+    fn visit_macro_def(&mut self, md: &'v hir::MacroDef) {
+        if md.imported_from.is_none() {
+            self.annotate(md.id, &md.attrs, md.span, AnnotationKind::Required, |_| {});
+        }
     }
 }
 
 impl<'tcx> Index<'tcx> {
     /// Construct the stability index for a crate being compiled.
-    pub fn build(&mut self, tcx: &ty::ctxt<'tcx>, krate: &Crate, export_map: &PublicItems) {
+    pub fn build(&mut self, tcx: &ty::ctxt<'tcx>, krate: &Crate, access_levels: &AccessLevels) {
         let mut annotator = Annotator {
             tcx: tcx,
             index: self,
-            parent: None,
-            export_map: export_map,
+            parent_stab: None,
+            parent_depr: None,
+            access_levels: access_levels,
+            in_trait_impl: false,
+            in_enum: false,
         };
-        annotator.annotate(ast::CRATE_NODE_ID, true, &krate.attrs, krate.span,
-                           |v| visit::walk_crate(v, krate), true);
+        annotator.annotate(ast::CRATE_NODE_ID, &krate.attrs, krate.span, AnnotationKind::Required,
+                           |v| intravisit::walk_crate(v, krate));
     }
 
-    pub fn new(krate: &Crate) -> Index {
+    pub fn new(krate: &Crate) -> Index<'tcx> {
         let mut is_staged_api = false;
         for attr in &krate.attrs {
-            if &attr.name()[..] == "staged_api" {
-                match attr.node.value.node {
-                    ast::MetaWord(_) => {
-                        attr::mark_used(attr);
-                        is_staged_api = true;
-                    }
-                    _ => (/*pass*/)
-                }
+            if attr.name() == "stable" || attr.name() == "unstable" {
+                is_staged_api = true;
+                break
             }
         }
+
         let mut staged_api = FnvHashMap();
         staged_api.insert(LOCAL_CRATE, is_staged_api);
         Index {
             staged_api: staged_api,
-            map: DefIdMap(),
+            stab_map: DefIdMap(),
+            depr_map: DefIdMap(),
         }
     }
 }
@@ -286,9 +339,7 @@ pub fn check_unstable_api_usage(tcx: &ty::ctxt)
         used_features: FnvHashMap(),
         in_skip_block: 0,
     };
-
-    let krate = tcx.map.krate();
-    visit::walk_crate(&mut checker, krate);
+    intravisit::walk_crate(&mut checker, tcx.map.krate());
 
     let used_features = checker.used_features;
     return used_features;
@@ -303,7 +354,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 {
@@ -357,6 +412,13 @@ fn check(&mut self, id: DefId, span: Span, stab: &Option<&Stability>) {
 }
 
 impl<'a, 'v, 'tcx> Visitor<'v> for Checker<'a, 'tcx> {
+    /// Because stability levels are scoped lexically, we want to walk
+    /// nested items in the context of the outer item, so enable
+    /// deep-walking.
+    fn visit_nested_item(&mut self, item: hir::ItemId) {
+        self.visit_item(self.tcx.map.expect_item(item.id))
+    }
+
     fn visit_item(&mut self, item: &hir::Item) {
         // When compiling with --test we don't enforce stability on the
         // compiler-generated test module, demarcated with `DUMMY_SP` plus the
@@ -364,32 +426,32 @@ fn visit_item(&mut self, item: &hir::Item) {
         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));
-        visit::walk_item(self, item);
+                   &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));
-        visit::walk_expr(self, ex);
+                   &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));
-        visit::walk_path(self, path)
+                   &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));
-        visit::walk_path_list_item(self, prefix, item)
+                   &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));
-        visit::walk_pat(self, pat)
+                  &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
+        intravisit::walk_pat(self, pat)
     }
 
     fn visit_block(&mut self, b: &hir::Block) {
@@ -403,20 +465,20 @@ fn visit_block(&mut self, b: &hir::Block) {
             }
             _ => {}
         }
-        visit::walk_block(self, b);
+        intravisit::walk_block(self, b);
         self.in_skip_block = old_skip_count;
     }
 }
 
 /// 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.
             if item.span == DUMMY_SP { return }
 
-            let cnum = match tcx.sess.cstore.find_extern_mod_stmt_cnum(item.id) {
+            let cnum = match tcx.sess.cstore.extern_mod_stmt_cnum(item.id) {
                 Some(cnum) => cnum,
                 None => return,
             };
@@ -447,7 +509,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, _, _) => {
@@ -508,7 +570,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(..)) => {}
@@ -520,7 +582,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) => {
@@ -531,7 +593,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; }
 
@@ -560,21 +622,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 {
@@ -589,54 +651,49 @@ fn is_staged_api(tcx: &ty::ctxt, id: DefId) -> bool {
             }
         _ => {
             *tcx.stability.borrow_mut().staged_api.entry(id.krate).or_insert_with(
-                || csearch::is_staged_api(&tcx.sess.cstore, id.krate))
+                || tcx.sess.cstore.is_staged_api(id.krate))
         }
     }
 }
 
 /// 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> {
-    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)
-        }
-        _ => {}
+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 item_stab = if id.is_local() {
+    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);
+    if id.is_local() {
         None // The stability cache is filled partially lazily
     } else {
-        csearch::get_stability(&tcx.sess.cstore, id).map(|st| tcx.intern_stability(st))
-    };
-
-    item_stab.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.
+        tcx.sess.cstore.stability(id).map(|st| tcx.intern_stability(st))
+    }
+}
 
-                debug!("lookup: trait_id={:?}", trait_id);
-                return lookup(tcx, trait_id);
-            }
-        }
-        None
-    })
+fn lookup_deprecation_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<Deprecation> {
+    debug!("lookup(id={:?})", id);
+    if id.is_local() {
+        None // The stability cache is filled partially lazily
+    } else {
+        tcx.sess.cstore.deprecation(id)
+    }
 }
 
 /// Given the list of enabled features that were not language features (i.e. that