about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-01-02 03:52:42 +0000
committerbors <bors@rust-lang.org>2020-01-02 03:52:42 +0000
commit68fdecd0ba798586d657af7a68c8128f5a67650b (patch)
treec9ba68fbc4da260e413150d112238a5b0db4c1f3 /src
parentaaca5f1d76cdb4d06dc0318e81493b9ae743cbc5 (diff)
parent36b6630771cb371ffc6aa737ae26c6c8806c7060 (diff)
downloadrust-68fdecd0ba798586d657af7a68c8128f5a67650b.tar.gz
rust-68fdecd0ba798586d657af7a68c8128f5a67650b.zip
Auto merge of #67700 - cjgillot:passes-stability, r=Zoxc
Move stability queries to librustc_passes.

Contains:
- check_mod_unstable_api_usage query;
- stability_index query;
- check_unused_or_stable features pass.

Split out of #67688

r? @Zoxc
Diffstat (limited to 'src')
-rw-r--r--src/librustc/middle/stability.rs613
-rw-r--r--src/librustc/ty/context.rs4
-rw-r--r--src/librustc_interface/passes.rs5
-rw-r--r--src/librustc_passes/lib.rs2
-rw-r--r--src/librustc_passes/stability.rs613
5 files changed, 628 insertions, 609 deletions
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index 7b186bc7a14..5ef4942ac3b 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -3,32 +3,25 @@
 
 pub use self::StabilityLevel::*;
 
-use crate::hir::def::{DefKind, Res};
-use crate::hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
-use crate::hir::intravisit::{self, NestedVisitorMap, Visitor};
-use crate::hir::{self, Generics, HirId, Item, StructField, Variant};
+use crate::hir::def::DefKind;
+use crate::hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX};
+use crate::hir::{self, HirId};
 use crate::lint::builtin::BuiltinLintDiagnostics;
 use crate::lint::{self, in_derive_expansion, Lint};
-use crate::middle::privacy::AccessLevels;
 use crate::session::{DiagnosticMessageId, Session};
-use crate::ty::query::Providers;
 use crate::ty::{self, TyCtxt};
 use crate::util::nodemap::{FxHashMap, FxHashSet};
 use errors::DiagnosticBuilder;
 use rustc_feature::GateIssue;
 use rustc_span::{MultiSpan, Span};
-use syntax::ast::{Attribute, CRATE_NODE_ID};
+use syntax::ast::CRATE_NODE_ID;
 use syntax::attr::{self, ConstStability, Deprecation, RustcDeprecation, Stability};
 use syntax::errors::Applicability;
-use syntax::feature_gate::{feature_err, feature_err_issue};
+use syntax::feature_gate::feature_err_issue;
 use syntax::symbol::{sym, Symbol};
 
-use std::cmp::Ordering;
-use std::mem::replace;
 use std::num::NonZeroU32;
 
-use rustc_error_codes::*;
-
 #[derive(PartialEq, Clone, Copy, Debug)]
 pub enum StabilityLevel {
     Unstable,
@@ -41,16 +34,6 @@ impl StabilityLevel {
     }
 }
 
-#[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,
-}
-
 /// An entry in the `depr_map`.
 #[derive(Clone, HashStable)]
 pub struct DeprecationEntry {
@@ -62,7 +45,7 @@ pub struct DeprecationEntry {
 }
 
 impl DeprecationEntry {
-    fn local(attr: Deprecation, id: HirId) -> DeprecationEntry {
+    pub fn local(attr: Deprecation, id: HirId) -> DeprecationEntry {
         DeprecationEntry { attr, origin: Some(id) }
     }
 
@@ -83,391 +66,18 @@ impl DeprecationEntry {
 pub struct Index<'tcx> {
     /// This is mostly a cache, except the stabilities of local items
     /// are filled by the annotator.
-    stab_map: FxHashMap<HirId, &'tcx Stability>,
-    const_stab_map: FxHashMap<HirId, &'tcx ConstStability>,
-    depr_map: FxHashMap<HirId, DeprecationEntry>,
+    pub stab_map: FxHashMap<HirId, &'tcx Stability>,
+    pub const_stab_map: FxHashMap<HirId, &'tcx ConstStability>,
+    pub depr_map: FxHashMap<HirId, DeprecationEntry>,
 
     /// Maps for each crate whether it is part of the staged API.
-    staged_api: FxHashMap<CrateNum, bool>,
+    pub staged_api: FxHashMap<CrateNum, bool>,
 
     /// Features enabled for this crate.
-    active_features: FxHashSet<Symbol>,
-}
-
-// A private tree-walker for producing an Index.
-struct Annotator<'a, 'tcx> {
-    tcx: TyCtxt<'tcx>,
-    index: &'a mut Index<'tcx>,
-    parent_stab: Option<&'tcx Stability>,
-    parent_depr: Option<DeprecationEntry>,
-    in_trait_impl: bool,
-}
-
-impl<'a, 'tcx> 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,
-        hir_id: HirId,
-        attrs: &[Attribute],
-        item_sp: Span,
-        kind: AnnotationKind,
-        visit_children: F,
-    ) where
-        F: FnOnce(&mut Self),
-    {
-        if self.tcx.features().staged_api {
-            // This crate explicitly wants staged API.
-            debug!("annotate(id = {:?}, attrs = {:?})", hir_id, attrs);
-            if let Some(..) = attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp) {
-                self.tcx.sess.span_err(
-                    item_sp,
-                    "`#[deprecated]` cannot be used in staged API; \
-                                                 use `#[rustc_deprecated]` instead",
-                );
-            }
-            let (stab, const_stab) =
-                attr::find_stability(&self.tcx.sess.parse_sess, attrs, item_sp);
-            if let Some(const_stab) = const_stab {
-                let const_stab = self.tcx.intern_const_stability(const_stab);
-                self.index.const_stab_map.insert(hir_id, const_stab);
-            }
-            if let Some(mut stab) = stab {
-                // 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.
-                if let (
-                    &Some(attr::RustcDeprecation { since: dep_since, .. }),
-                    &attr::Stable { since: 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.as_str().split('.').zip(stab_since.as_str().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,
-                            }
-                        } 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;
-                        }
-                    }
-                }
-
-                self.index.stab_map.insert(hir_id, 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);
-                if let Some(stab) = self.parent_stab {
-                    if stab.level.is_unstable() {
-                        self.index.stab_map.insert(hir_id, stab);
-                    }
-                }
-                visit_children(self);
-            }
-        } else {
-            // Emit errors for non-staged-api crates.
-            let unstable_attrs = [
-                sym::unstable,
-                sym::stable,
-                sym::rustc_deprecated,
-                sym::rustc_const_unstable,
-                sym::rustc_const_stable,
-            ];
-            for attr in attrs {
-                let name = attr.name_or_empty();
-                if unstable_attrs.contains(&name) {
-                    attr::mark_used(attr);
-                    struct_span_err!(
-                        self.tcx.sess,
-                        attr.span,
-                        E0734,
-                        "stability attributes may not be used outside of the standard library",
-                    )
-                    .emit();
-                }
-            }
-
-            // Propagate unstability.  This can happen even for non-staged-api crates in case
-            // -Zforce-unstable-if-unmarked is set.
-            if let Some(stab) = self.parent_stab {
-                if stab.level.is_unstable() {
-                    self.index.stab_map.insert(hir_id, stab);
-                }
-            }
-
-            if let Some(depr) = attr::find_deprecation(&self.tcx.sess.parse_sess, 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 depr_entry = DeprecationEntry::local(depr, hir_id);
-                self.index.depr_map.insert(hir_id, depr_entry.clone());
-
-                let orig_parent_depr = replace(&mut self.parent_depr, Some(depr_entry));
-                visit_children(self);
-                self.parent_depr = orig_parent_depr;
-            } else if let Some(parent_depr) = self.parent_depr.clone() {
-                self.index.depr_map.insert(hir_id, parent_depr);
-                visit_children(self);
-            } else {
-                visit_children(self);
-            }
-        }
-    }
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for Annotator<'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 nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
-        NestedVisitorMap::All(&self.tcx.hir())
-    }
-
-    fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
-        let orig_in_trait_impl = self.in_trait_impl;
-        let mut kind = AnnotationKind::Required;
-        match i.kind {
-            // 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::ItemKind::Impl(.., None, _, _) | hir::ItemKind::ForeignMod(..) => {
-                self.in_trait_impl = false;
-                kind = AnnotationKind::Container;
-            }
-            hir::ItemKind::Impl(.., Some(_), _, _) => {
-                self.in_trait_impl = true;
-            }
-            hir::ItemKind::Struct(ref sd, _) => {
-                if let Some(ctor_hir_id) = sd.ctor_hir_id() {
-                    self.annotate(ctor_hir_id, &i.attrs, i.span, AnnotationKind::Required, |_| {})
-                }
-            }
-            _ => {}
-        }
-
-        self.annotate(i.hir_id, &i.attrs, i.span, kind, |v| intravisit::walk_item(v, i));
-        self.in_trait_impl = orig_in_trait_impl;
-    }
-
-    fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
-        self.annotate(ti.hir_id, &ti.attrs, ti.span, AnnotationKind::Required, |v| {
-            intravisit::walk_trait_item(v, ti);
-        });
-    }
-
-    fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
-        let kind =
-            if self.in_trait_impl { AnnotationKind::Prohibited } else { AnnotationKind::Required };
-        self.annotate(ii.hir_id, &ii.attrs, ii.span, kind, |v| {
-            intravisit::walk_impl_item(v, ii);
-        });
-    }
-
-    fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) {
-        self.annotate(var.id, &var.attrs, var.span, AnnotationKind::Required, |v| {
-            if let Some(ctor_hir_id) = var.data.ctor_hir_id() {
-                v.annotate(ctor_hir_id, &var.attrs, var.span, AnnotationKind::Required, |_| {});
-            }
-
-            intravisit::walk_variant(v, var, g, item_id)
-        })
-    }
-
-    fn visit_struct_field(&mut self, s: &'tcx StructField<'tcx>) {
-        self.annotate(s.hir_id, &s.attrs, s.span, AnnotationKind::Required, |v| {
-            intravisit::walk_struct_field(v, s);
-        });
-    }
-
-    fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
-        self.annotate(i.hir_id, &i.attrs, i.span, AnnotationKind::Required, |v| {
-            intravisit::walk_foreign_item(v, i);
-        });
-    }
-
-    fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
-        self.annotate(md.hir_id, &md.attrs, md.span, AnnotationKind::Required, |_| {});
-    }
-}
-
-struct MissingStabilityAnnotations<'a, 'tcx> {
-    tcx: TyCtxt<'tcx>,
-    access_levels: &'a AccessLevels,
-}
-
-impl<'a, 'tcx> MissingStabilityAnnotations<'a, 'tcx> {
-    fn check_missing_stability(&self, hir_id: HirId, span: Span, name: &str) {
-        let stab = self.tcx.stability().local_stability(hir_id);
-        let is_error =
-            !self.tcx.sess.opts.test && stab.is_none() && self.access_levels.is_reachable(hir_id);
-        if is_error {
-            self.tcx.sess.span_err(span, &format!("{} has missing stability attribute", name));
-        }
-    }
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'a, 'tcx> {
-    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
-        NestedVisitorMap::OnlyBodies(&self.tcx.hir())
-    }
-
-    fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
-        match i.kind {
-            // 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::ItemKind::Impl(.., None, _, _) | hir::ItemKind::ForeignMod(..) => {}
-
-            _ => self.check_missing_stability(i.hir_id, i.span, i.kind.descriptive_variant()),
-        }
-
-        intravisit::walk_item(self, i)
-    }
-
-    fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
-        self.check_missing_stability(ti.hir_id, ti.span, "item");
-        intravisit::walk_trait_item(self, ti);
-    }
-
-    fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
-        let impl_def_id = self.tcx.hir().local_def_id(self.tcx.hir().get_parent_item(ii.hir_id));
-        if self.tcx.impl_trait_ref(impl_def_id).is_none() {
-            self.check_missing_stability(ii.hir_id, ii.span, "item");
-        }
-        intravisit::walk_impl_item(self, ii);
-    }
-
-    fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) {
-        self.check_missing_stability(var.id, var.span, "variant");
-        intravisit::walk_variant(self, var, g, item_id);
-    }
-
-    fn visit_struct_field(&mut self, s: &'tcx StructField<'tcx>) {
-        self.check_missing_stability(s.hir_id, s.span, "field");
-        intravisit::walk_struct_field(self, s);
-    }
-
-    fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
-        self.check_missing_stability(i.hir_id, i.span, i.kind.descriptive_variant());
-        intravisit::walk_foreign_item(self, i);
-    }
-
-    fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
-        self.check_missing_stability(md.hir_id, md.span, "macro");
-    }
+    pub active_features: FxHashSet<Symbol>,
 }
 
 impl<'tcx> Index<'tcx> {
-    pub fn new(tcx: TyCtxt<'tcx>) -> Index<'tcx> {
-        let is_staged_api =
-            tcx.sess.opts.debugging_opts.force_unstable_if_unmarked || tcx.features().staged_api;
-        let mut staged_api = FxHashMap::default();
-        staged_api.insert(LOCAL_CRATE, is_staged_api);
-        let mut index = Index {
-            staged_api,
-            stab_map: Default::default(),
-            const_stab_map: Default::default(),
-            depr_map: Default::default(),
-            active_features: Default::default(),
-        };
-
-        let active_lib_features = &tcx.features().declared_lib_features;
-        let active_lang_features = &tcx.features().declared_lang_features;
-
-        // Put the active features into a map for quick lookup.
-        index.active_features = active_lib_features
-            .iter()
-            .map(|&(ref s, ..)| s.clone())
-            .chain(active_lang_features.iter().map(|&(ref s, ..)| s.clone()))
-            .collect();
-
-        {
-            let krate = tcx.hir().krate();
-            let mut annotator = Annotator {
-                tcx,
-                index: &mut index,
-                parent_stab: None,
-                parent_depr: None,
-                in_trait_impl: false,
-            };
-
-            // If the `-Z force-unstable-if-unmarked` flag is passed then we provide
-            // a parent stability annotation which indicates that this is private
-            // with the `rustc_private` feature. This is intended for use when
-            // compiling librustc crates themselves so we can leverage crates.io
-            // while maintaining the invariant that all sysroot crates are unstable
-            // by default and are unable to be used.
-            if tcx.sess.opts.debugging_opts.force_unstable_if_unmarked {
-                let reason = "this crate is being loaded from the sysroot, an \
-                              unstable location; did you mean to load this crate \
-                              from crates.io via `Cargo.toml` instead?";
-                let stability = tcx.intern_stability(Stability {
-                    level: attr::StabilityLevel::Unstable {
-                        reason: Some(Symbol::intern(reason)),
-                        issue: NonZeroU32::new(27812),
-                        is_soft: false,
-                    },
-                    feature: sym::rustc_private,
-                    rustc_depr: None,
-                });
-                annotator.parent_stab = Some(stability);
-            }
-
-            annotator.annotate(
-                hir::CRATE_HIR_ID,
-                &krate.attrs,
-                krate.span,
-                AnnotationKind::Required,
-                |v| intravisit::walk_crate(v, krate),
-            );
-        }
-        return index;
-    }
-
     pub fn local_stability(&self, id: HirId) -> Option<&'tcx Stability> {
         self.stab_map.get(&id).cloned()
     }
@@ -481,16 +91,6 @@ impl<'tcx> Index<'tcx> {
     }
 }
 
-/// Cross-references the feature names of unstable APIs with enabled
-/// features and possibly prints errors.
-fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: DefId) {
-    tcx.hir().visit_item_likes_in_module(module_def_id, &mut Checker { tcx }.as_deep_visitor());
-}
-
-pub fn provide(providers: &mut Providers<'_>) {
-    *providers = Providers { check_mod_unstable_api_usage, ..*providers };
-}
-
 pub fn report_unstable(
     sess: &Session,
     feature: Symbol,
@@ -632,10 +232,6 @@ fn late_report_deprecation(
     }
 }
 
-struct Checker<'tcx> {
-    tcx: TyCtxt<'tcx>,
-}
-
 /// Result of `TyCtxt::eval_stability`.
 pub enum EvalResult {
     /// We can use the item because it is stable or we provided the
@@ -807,195 +403,8 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 }
 
-impl Visitor<'tcx> for Checker<'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 nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
-        NestedVisitorMap::OnlyBodies(&self.tcx.hir())
-    }
-
-    fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
-        match item.kind {
-            hir::ItemKind::ExternCrate(_) => {
-                // compiler-generated `extern crate` items have a dummy span.
-                if item.span.is_dummy() {
-                    return;
-                }
-
-                let def_id = self.tcx.hir().local_def_id(item.hir_id);
-                let cnum = match self.tcx.extern_mod_stmt_cnum(def_id) {
-                    Some(cnum) => cnum,
-                    None => return,
-                };
-                let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX };
-                self.tcx.check_stability(def_id, Some(item.hir_id), item.span);
-            }
-
-            // For implementations of traits, check the stability of each item
-            // individually as it's possible to have a stable trait with unstable
-            // items.
-            hir::ItemKind::Impl(.., Some(ref t), _, impl_item_refs) => {
-                if let Res::Def(DefKind::Trait, trait_did) = t.path.res {
-                    for impl_item_ref in impl_item_refs {
-                        let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
-                        let trait_item_def_id = self
-                            .tcx
-                            .associated_items(trait_did)
-                            .find(|item| item.ident.name == impl_item.ident.name)
-                            .map(|item| item.def_id);
-                        if let Some(def_id) = trait_item_def_id {
-                            // Pass `None` to skip deprecation warnings.
-                            self.tcx.check_stability(def_id, None, impl_item.span);
-                        }
-                    }
-                }
-            }
-
-            // There's no good place to insert stability check for non-Copy unions,
-            // so semi-randomly perform it here in stability.rs
-            hir::ItemKind::Union(..) if !self.tcx.features().untagged_unions => {
-                let def_id = self.tcx.hir().local_def_id(item.hir_id);
-                let adt_def = self.tcx.adt_def(def_id);
-                let ty = self.tcx.type_of(def_id);
-
-                if adt_def.has_dtor(self.tcx) {
-                    feature_err(
-                        &self.tcx.sess.parse_sess,
-                        sym::untagged_unions,
-                        item.span,
-                        "unions with `Drop` implementations are unstable",
-                    )
-                    .emit();
-                } else {
-                    let param_env = self.tcx.param_env(def_id);
-                    if !param_env.can_type_implement_copy(self.tcx, ty).is_ok() {
-                        feature_err(
-                            &self.tcx.sess.parse_sess,
-                            sym::untagged_unions,
-                            item.span,
-                            "unions with non-`Copy` fields are unstable",
-                        )
-                        .emit();
-                    }
-                }
-            }
-
-            _ => (/* pass */),
-        }
-        intravisit::walk_item(self, item);
-    }
-
-    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, id: hir::HirId) {
-        if let Some(def_id) = path.res.opt_def_id() {
-            self.tcx.check_stability(def_id, Some(id), path.span)
-        }
-        intravisit::walk_path(self, path)
-    }
-}
-
 impl<'tcx> TyCtxt<'tcx> {
     pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
         self.lookup_deprecation_entry(id).map(|depr| depr.attr)
     }
 }
-
-/// Given the list of enabled features that were not language features (i.e., that
-/// were expected to be library features), and the list of features used from
-/// libraries, identify activated features that don't exist and error about them.
-pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
-    let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE);
-
-    if tcx.stability().staged_api[&LOCAL_CRATE] {
-        let krate = tcx.hir().krate();
-        let mut missing = MissingStabilityAnnotations { tcx, access_levels };
-        missing.check_missing_stability(hir::CRATE_HIR_ID, krate.span, "crate");
-        intravisit::walk_crate(&mut missing, krate);
-        krate.visit_all_item_likes(&mut missing.as_deep_visitor());
-    }
-
-    let declared_lang_features = &tcx.features().declared_lang_features;
-    let mut lang_features = FxHashSet::default();
-    for &(feature, span, since) in declared_lang_features {
-        if let Some(since) = since {
-            // Warn if the user has enabled an already-stable lang feature.
-            unnecessary_stable_feature_lint(tcx, span, feature, since);
-        }
-        if !lang_features.insert(feature) {
-            // Warn if the user enables a lang feature multiple times.
-            duplicate_feature_err(tcx.sess, span, feature);
-        }
-    }
-
-    let declared_lib_features = &tcx.features().declared_lib_features;
-    let mut remaining_lib_features = FxHashMap::default();
-    for (feature, span) in declared_lib_features {
-        if remaining_lib_features.contains_key(&feature) {
-            // Warn if the user enables a lib feature multiple times.
-            duplicate_feature_err(tcx.sess, *span, *feature);
-        }
-        remaining_lib_features.insert(feature, span.clone());
-    }
-    // `stdbuild` has special handling for `libc`, so we need to
-    // recognise the feature when building std.
-    // Likewise, libtest is handled specially, so `test` isn't
-    // available as we'd like it to be.
-    // FIXME: only remove `libc` when `stdbuild` is active.
-    // FIXME: remove special casing for `test`.
-    remaining_lib_features.remove(&Symbol::intern("libc"));
-    remaining_lib_features.remove(&sym::test);
-
-    let check_features = |remaining_lib_features: &mut FxHashMap<_, _>, defined_features: &[_]| {
-        for &(feature, since) in defined_features {
-            if let Some(since) = since {
-                if let Some(span) = remaining_lib_features.get(&feature) {
-                    // Warn if the user has enabled an already-stable lib feature.
-                    unnecessary_stable_feature_lint(tcx, *span, feature, since);
-                }
-            }
-            remaining_lib_features.remove(&feature);
-            if remaining_lib_features.is_empty() {
-                break;
-            }
-        }
-    };
-
-    // We always collect the lib features declared in the current crate, even if there are
-    // no unknown features, because the collection also does feature attribute validation.
-    let local_defined_features = tcx.lib_features().to_vec();
-    if !remaining_lib_features.is_empty() {
-        check_features(&mut remaining_lib_features, &local_defined_features);
-
-        for &cnum in &*tcx.crates() {
-            if remaining_lib_features.is_empty() {
-                break;
-            }
-            check_features(&mut remaining_lib_features, tcx.defined_lib_features(cnum));
-        }
-    }
-
-    for (feature, span) in remaining_lib_features {
-        struct_span_err!(tcx.sess, span, E0635, "unknown feature `{}`", feature).emit();
-    }
-
-    // FIXME(#44232): the `used_features` table no longer exists, so we
-    // don't lint about unused features. We should reenable this one day!
-}
-
-fn unnecessary_stable_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol, since: Symbol) {
-    tcx.lint_hir(
-        lint::builtin::STABLE_FEATURES,
-        hir::CRATE_HIR_ID,
-        span,
-        &format!(
-            "the feature `{}` has been stable since {} and no longer requires \
-                  an attribute to enable",
-            feature, since
-        ),
-    );
-}
-
-fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) {
-    struct_span_err!(sess, span, E0636, "the feature `{}` has already been declared", feature)
-        .emit();
-}
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index daa5ca37a98..d83a720170b 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -2765,10 +2765,6 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
         Lrc::new(tcx.glob_map.get(&id).cloned().unwrap_or_default())
     };
 
-    providers.stability_index = |tcx, cnum| {
-        assert_eq!(cnum, LOCAL_CRATE);
-        tcx.arena.alloc(stability::Index::new(tcx))
-    };
     providers.lookup_stability = |tcx, id| {
         assert_eq!(id.krate, LOCAL_CRATE);
         let id = tcx.hir().definitions().def_index_to_hir_id(id.index);
diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs
index 4d15424112e..670ca30faf2 100644
--- a/src/librustc_interface/passes.rs
+++ b/src/librustc_interface/passes.rs
@@ -8,8 +8,8 @@ use rustc::dep_graph::DepGraph;
 use rustc::hir;
 use rustc::hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc::lint;
+use rustc::middle;
 use rustc::middle::cstore::{CrateStore, MetadataLoader, MetadataLoaderDyn};
-use rustc::middle::{self, stability};
 use rustc::session::config::{self, CrateType, Input, OutputFilenames, OutputType};
 use rustc::session::config::{PpMode, PpSourceMode};
 use rustc::session::search_paths::PathKind;
@@ -687,7 +687,6 @@ pub fn default_provide(providers: &mut ty::query::Providers<'_>) {
     typeck::provide(providers);
     ty::provide(providers);
     traits::provide(providers);
-    stability::provide(providers);
     rustc_passes::provide(providers);
     rustc_resolve::provide(providers);
     rustc_traits::provide(providers);
@@ -881,7 +880,7 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> {
                     },
                     {
                         time(sess, "unused lib feature checking", || {
-                            stability::check_unused_or_stable_features(tcx)
+                            rustc_passes::stability::check_unused_or_stable_features(tcx)
                         });
                     },
                     {
diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs
index 8a10c8fe89d..a692c45ced4 100644
--- a/src/librustc_passes/lib.rs
+++ b/src/librustc_passes/lib.rs
@@ -32,6 +32,7 @@ mod liveness;
 pub mod loops;
 mod reachable;
 mod region;
+pub mod stability;
 
 pub fn provide(providers: &mut Providers<'_>) {
     check_const::provide(providers);
@@ -43,4 +44,5 @@ pub fn provide(providers: &mut Providers<'_>) {
     intrinsicck::provide(providers);
     reachable::provide(providers);
     region::provide(providers);
+    stability::provide(providers);
 }
diff --git a/src/librustc_passes/stability.rs b/src/librustc_passes/stability.rs
new file mode 100644
index 00000000000..fcbc742b9a0
--- /dev/null
+++ b/src/librustc_passes/stability.rs
@@ -0,0 +1,613 @@
+//! A pass that annotates every item and method with its stability level,
+//! propagating default levels lexically from parent to children ast nodes.
+
+use rustc::hir::def::{DefKind, Res};
+use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
+use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc::hir::{self, Generics, HirId, Item, StructField, Variant};
+use rustc::lint;
+use rustc::middle::privacy::AccessLevels;
+use rustc::middle::stability::{DeprecationEntry, Index};
+use rustc::session::Session;
+use rustc::ty::query::Providers;
+use rustc::ty::TyCtxt;
+use rustc::util::nodemap::{FxHashMap, FxHashSet};
+use rustc_span::Span;
+use syntax::ast::Attribute;
+use syntax::attr::{self, Stability};
+use syntax::feature_gate::feature_err;
+use syntax::symbol::{sym, Symbol};
+
+use std::cmp::Ordering;
+use std::mem::replace;
+use std::num::NonZeroU32;
+
+use rustc_error_codes::*;
+
+#[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 private tree-walker for producing an Index.
+struct Annotator<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    index: &'a mut Index<'tcx>,
+    parent_stab: Option<&'tcx Stability>,
+    parent_depr: Option<DeprecationEntry>,
+    in_trait_impl: bool,
+}
+
+impl<'a, 'tcx> 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,
+        hir_id: HirId,
+        attrs: &[Attribute],
+        item_sp: Span,
+        kind: AnnotationKind,
+        visit_children: F,
+    ) where
+        F: FnOnce(&mut Self),
+    {
+        if self.tcx.features().staged_api {
+            // This crate explicitly wants staged API.
+            debug!("annotate(id = {:?}, attrs = {:?})", hir_id, attrs);
+            if let Some(..) = attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp) {
+                self.tcx.sess.span_err(
+                    item_sp,
+                    "`#[deprecated]` cannot be used in staged API; \
+                                                 use `#[rustc_deprecated]` instead",
+                );
+            }
+            let (stab, const_stab) =
+                attr::find_stability(&self.tcx.sess.parse_sess, attrs, item_sp);
+            if let Some(const_stab) = const_stab {
+                let const_stab = self.tcx.intern_const_stability(const_stab);
+                self.index.const_stab_map.insert(hir_id, const_stab);
+            }
+            if let Some(mut stab) = stab {
+                // 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.
+                if let (
+                    &Some(attr::RustcDeprecation { since: dep_since, .. }),
+                    &attr::Stable { since: 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.as_str().split('.').zip(stab_since.as_str().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,
+                            }
+                        } 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;
+                        }
+                    }
+                }
+
+                self.index.stab_map.insert(hir_id, 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);
+                if let Some(stab) = self.parent_stab {
+                    if stab.level.is_unstable() {
+                        self.index.stab_map.insert(hir_id, stab);
+                    }
+                }
+                visit_children(self);
+            }
+        } else {
+            // Emit errors for non-staged-api crates.
+            let unstable_attrs = [
+                sym::unstable,
+                sym::stable,
+                sym::rustc_deprecated,
+                sym::rustc_const_unstable,
+                sym::rustc_const_stable,
+            ];
+            for attr in attrs {
+                let name = attr.name_or_empty();
+                if unstable_attrs.contains(&name) {
+                    attr::mark_used(attr);
+                    struct_span_err!(
+                        self.tcx.sess,
+                        attr.span,
+                        E0734,
+                        "stability attributes may not be used outside of the standard library",
+                    )
+                    .emit();
+                }
+            }
+
+            // Propagate unstability.  This can happen even for non-staged-api crates in case
+            // -Zforce-unstable-if-unmarked is set.
+            if let Some(stab) = self.parent_stab {
+                if stab.level.is_unstable() {
+                    self.index.stab_map.insert(hir_id, stab);
+                }
+            }
+
+            if let Some(depr) = attr::find_deprecation(&self.tcx.sess.parse_sess, 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 depr_entry = DeprecationEntry::local(depr, hir_id);
+                self.index.depr_map.insert(hir_id, depr_entry.clone());
+
+                let orig_parent_depr = replace(&mut self.parent_depr, Some(depr_entry));
+                visit_children(self);
+                self.parent_depr = orig_parent_depr;
+            } else if let Some(parent_depr) = self.parent_depr.clone() {
+                self.index.depr_map.insert(hir_id, parent_depr);
+                visit_children(self);
+            } else {
+                visit_children(self);
+            }
+        }
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for Annotator<'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 nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+        NestedVisitorMap::All(&self.tcx.hir())
+    }
+
+    fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
+        let orig_in_trait_impl = self.in_trait_impl;
+        let mut kind = AnnotationKind::Required;
+        match i.kind {
+            // 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::ItemKind::Impl(.., None, _, _) | hir::ItemKind::ForeignMod(..) => {
+                self.in_trait_impl = false;
+                kind = AnnotationKind::Container;
+            }
+            hir::ItemKind::Impl(.., Some(_), _, _) => {
+                self.in_trait_impl = true;
+            }
+            hir::ItemKind::Struct(ref sd, _) => {
+                if let Some(ctor_hir_id) = sd.ctor_hir_id() {
+                    self.annotate(ctor_hir_id, &i.attrs, i.span, AnnotationKind::Required, |_| {})
+                }
+            }
+            _ => {}
+        }
+
+        self.annotate(i.hir_id, &i.attrs, i.span, kind, |v| intravisit::walk_item(v, i));
+        self.in_trait_impl = orig_in_trait_impl;
+    }
+
+    fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
+        self.annotate(ti.hir_id, &ti.attrs, ti.span, AnnotationKind::Required, |v| {
+            intravisit::walk_trait_item(v, ti);
+        });
+    }
+
+    fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
+        let kind =
+            if self.in_trait_impl { AnnotationKind::Prohibited } else { AnnotationKind::Required };
+        self.annotate(ii.hir_id, &ii.attrs, ii.span, kind, |v| {
+            intravisit::walk_impl_item(v, ii);
+        });
+    }
+
+    fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) {
+        self.annotate(var.id, &var.attrs, var.span, AnnotationKind::Required, |v| {
+            if let Some(ctor_hir_id) = var.data.ctor_hir_id() {
+                v.annotate(ctor_hir_id, &var.attrs, var.span, AnnotationKind::Required, |_| {});
+            }
+
+            intravisit::walk_variant(v, var, g, item_id)
+        })
+    }
+
+    fn visit_struct_field(&mut self, s: &'tcx StructField<'tcx>) {
+        self.annotate(s.hir_id, &s.attrs, s.span, AnnotationKind::Required, |v| {
+            intravisit::walk_struct_field(v, s);
+        });
+    }
+
+    fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
+        self.annotate(i.hir_id, &i.attrs, i.span, AnnotationKind::Required, |v| {
+            intravisit::walk_foreign_item(v, i);
+        });
+    }
+
+    fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
+        self.annotate(md.hir_id, &md.attrs, md.span, AnnotationKind::Required, |_| {});
+    }
+}
+
+struct MissingStabilityAnnotations<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    access_levels: &'a AccessLevels,
+}
+
+impl<'a, 'tcx> MissingStabilityAnnotations<'a, 'tcx> {
+    fn check_missing_stability(&self, hir_id: HirId, span: Span, name: &str) {
+        let stab = self.tcx.stability().local_stability(hir_id);
+        let is_error =
+            !self.tcx.sess.opts.test && stab.is_none() && self.access_levels.is_reachable(hir_id);
+        if is_error {
+            self.tcx.sess.span_err(span, &format!("{} has missing stability attribute", name));
+        }
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'a, 'tcx> {
+    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+        NestedVisitorMap::OnlyBodies(&self.tcx.hir())
+    }
+
+    fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
+        match i.kind {
+            // 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::ItemKind::Impl(.., None, _, _) | hir::ItemKind::ForeignMod(..) => {}
+
+            _ => self.check_missing_stability(i.hir_id, i.span, i.kind.descriptive_variant()),
+        }
+
+        intravisit::walk_item(self, i)
+    }
+
+    fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
+        self.check_missing_stability(ti.hir_id, ti.span, "item");
+        intravisit::walk_trait_item(self, ti);
+    }
+
+    fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
+        let impl_def_id = self.tcx.hir().local_def_id(self.tcx.hir().get_parent_item(ii.hir_id));
+        if self.tcx.impl_trait_ref(impl_def_id).is_none() {
+            self.check_missing_stability(ii.hir_id, ii.span, "item");
+        }
+        intravisit::walk_impl_item(self, ii);
+    }
+
+    fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) {
+        self.check_missing_stability(var.id, var.span, "variant");
+        intravisit::walk_variant(self, var, g, item_id);
+    }
+
+    fn visit_struct_field(&mut self, s: &'tcx StructField<'tcx>) {
+        self.check_missing_stability(s.hir_id, s.span, "field");
+        intravisit::walk_struct_field(self, s);
+    }
+
+    fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
+        self.check_missing_stability(i.hir_id, i.span, i.kind.descriptive_variant());
+        intravisit::walk_foreign_item(self, i);
+    }
+
+    fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
+        self.check_missing_stability(md.hir_id, md.span, "macro");
+    }
+}
+
+fn new_index(tcx: TyCtxt<'tcx>) -> Index<'tcx> {
+    let is_staged_api =
+        tcx.sess.opts.debugging_opts.force_unstable_if_unmarked || tcx.features().staged_api;
+    let mut staged_api = FxHashMap::default();
+    staged_api.insert(LOCAL_CRATE, is_staged_api);
+    let mut index = Index {
+        staged_api,
+        stab_map: Default::default(),
+        const_stab_map: Default::default(),
+        depr_map: Default::default(),
+        active_features: Default::default(),
+    };
+
+    let active_lib_features = &tcx.features().declared_lib_features;
+    let active_lang_features = &tcx.features().declared_lang_features;
+
+    // Put the active features into a map for quick lookup.
+    index.active_features = active_lib_features
+        .iter()
+        .map(|&(ref s, ..)| s.clone())
+        .chain(active_lang_features.iter().map(|&(ref s, ..)| s.clone()))
+        .collect();
+
+    {
+        let krate = tcx.hir().krate();
+        let mut annotator = Annotator {
+            tcx,
+            index: &mut index,
+            parent_stab: None,
+            parent_depr: None,
+            in_trait_impl: false,
+        };
+
+        // If the `-Z force-unstable-if-unmarked` flag is passed then we provide
+        // a parent stability annotation which indicates that this is private
+        // with the `rustc_private` feature. This is intended for use when
+        // compiling librustc crates themselves so we can leverage crates.io
+        // while maintaining the invariant that all sysroot crates are unstable
+        // by default and are unable to be used.
+        if tcx.sess.opts.debugging_opts.force_unstable_if_unmarked {
+            let reason = "this crate is being loaded from the sysroot, an \
+                          unstable location; did you mean to load this crate \
+                          from crates.io via `Cargo.toml` instead?";
+            let stability = tcx.intern_stability(Stability {
+                level: attr::StabilityLevel::Unstable {
+                    reason: Some(Symbol::intern(reason)),
+                    issue: NonZeroU32::new(27812),
+                    is_soft: false,
+                },
+                feature: sym::rustc_private,
+                rustc_depr: None,
+            });
+            annotator.parent_stab = Some(stability);
+        }
+
+        annotator.annotate(
+            hir::CRATE_HIR_ID,
+            &krate.attrs,
+            krate.span,
+            AnnotationKind::Required,
+            |v| intravisit::walk_crate(v, krate),
+        );
+    }
+    return index;
+}
+
+/// Cross-references the feature names of unstable APIs with enabled
+/// features and possibly prints errors.
+fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: DefId) {
+    tcx.hir().visit_item_likes_in_module(module_def_id, &mut Checker { tcx }.as_deep_visitor());
+}
+
+pub(crate) fn provide(providers: &mut Providers<'_>) {
+    *providers = Providers { check_mod_unstable_api_usage, ..*providers };
+    providers.stability_index = |tcx, cnum| {
+        assert_eq!(cnum, LOCAL_CRATE);
+        tcx.arena.alloc(new_index(tcx))
+    };
+}
+
+struct Checker<'tcx> {
+    tcx: TyCtxt<'tcx>,
+}
+
+impl Visitor<'tcx> for Checker<'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 nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+        NestedVisitorMap::OnlyBodies(&self.tcx.hir())
+    }
+
+    fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
+        match item.kind {
+            hir::ItemKind::ExternCrate(_) => {
+                // compiler-generated `extern crate` items have a dummy span.
+                if item.span.is_dummy() {
+                    return;
+                }
+
+                let def_id = self.tcx.hir().local_def_id(item.hir_id);
+                let cnum = match self.tcx.extern_mod_stmt_cnum(def_id) {
+                    Some(cnum) => cnum,
+                    None => return,
+                };
+                let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX };
+                self.tcx.check_stability(def_id, Some(item.hir_id), item.span);
+            }
+
+            // For implementations of traits, check the stability of each item
+            // individually as it's possible to have a stable trait with unstable
+            // items.
+            hir::ItemKind::Impl(.., Some(ref t), _, impl_item_refs) => {
+                if let Res::Def(DefKind::Trait, trait_did) = t.path.res {
+                    for impl_item_ref in impl_item_refs {
+                        let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
+                        let trait_item_def_id = self
+                            .tcx
+                            .associated_items(trait_did)
+                            .find(|item| item.ident.name == impl_item.ident.name)
+                            .map(|item| item.def_id);
+                        if let Some(def_id) = trait_item_def_id {
+                            // Pass `None` to skip deprecation warnings.
+                            self.tcx.check_stability(def_id, None, impl_item.span);
+                        }
+                    }
+                }
+            }
+
+            // There's no good place to insert stability check for non-Copy unions,
+            // so semi-randomly perform it here in stability.rs
+            hir::ItemKind::Union(..) if !self.tcx.features().untagged_unions => {
+                let def_id = self.tcx.hir().local_def_id(item.hir_id);
+                let adt_def = self.tcx.adt_def(def_id);
+                let ty = self.tcx.type_of(def_id);
+
+                if adt_def.has_dtor(self.tcx) {
+                    feature_err(
+                        &self.tcx.sess.parse_sess,
+                        sym::untagged_unions,
+                        item.span,
+                        "unions with `Drop` implementations are unstable",
+                    )
+                    .emit();
+                } else {
+                    let param_env = self.tcx.param_env(def_id);
+                    if !param_env.can_type_implement_copy(self.tcx, ty).is_ok() {
+                        feature_err(
+                            &self.tcx.sess.parse_sess,
+                            sym::untagged_unions,
+                            item.span,
+                            "unions with non-`Copy` fields are unstable",
+                        )
+                        .emit();
+                    }
+                }
+            }
+
+            _ => (/* pass */),
+        }
+        intravisit::walk_item(self, item);
+    }
+
+    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, id: hir::HirId) {
+        if let Some(def_id) = path.res.opt_def_id() {
+            self.tcx.check_stability(def_id, Some(id), path.span)
+        }
+        intravisit::walk_path(self, path)
+    }
+}
+
+/// Given the list of enabled features that were not language features (i.e., that
+/// were expected to be library features), and the list of features used from
+/// libraries, identify activated features that don't exist and error about them.
+pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
+    let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE);
+
+    if tcx.stability().staged_api[&LOCAL_CRATE] {
+        let krate = tcx.hir().krate();
+        let mut missing = MissingStabilityAnnotations { tcx, access_levels };
+        missing.check_missing_stability(hir::CRATE_HIR_ID, krate.span, "crate");
+        intravisit::walk_crate(&mut missing, krate);
+        krate.visit_all_item_likes(&mut missing.as_deep_visitor());
+    }
+
+    let declared_lang_features = &tcx.features().declared_lang_features;
+    let mut lang_features = FxHashSet::default();
+    for &(feature, span, since) in declared_lang_features {
+        if let Some(since) = since {
+            // Warn if the user has enabled an already-stable lang feature.
+            unnecessary_stable_feature_lint(tcx, span, feature, since);
+        }
+        if !lang_features.insert(feature) {
+            // Warn if the user enables a lang feature multiple times.
+            duplicate_feature_err(tcx.sess, span, feature);
+        }
+    }
+
+    let declared_lib_features = &tcx.features().declared_lib_features;
+    let mut remaining_lib_features = FxHashMap::default();
+    for (feature, span) in declared_lib_features {
+        if remaining_lib_features.contains_key(&feature) {
+            // Warn if the user enables a lib feature multiple times.
+            duplicate_feature_err(tcx.sess, *span, *feature);
+        }
+        remaining_lib_features.insert(feature, span.clone());
+    }
+    // `stdbuild` has special handling for `libc`, so we need to
+    // recognise the feature when building std.
+    // Likewise, libtest is handled specially, so `test` isn't
+    // available as we'd like it to be.
+    // FIXME: only remove `libc` when `stdbuild` is active.
+    // FIXME: remove special casing for `test`.
+    remaining_lib_features.remove(&Symbol::intern("libc"));
+    remaining_lib_features.remove(&sym::test);
+
+    let check_features = |remaining_lib_features: &mut FxHashMap<_, _>, defined_features: &[_]| {
+        for &(feature, since) in defined_features {
+            if let Some(since) = since {
+                if let Some(span) = remaining_lib_features.get(&feature) {
+                    // Warn if the user has enabled an already-stable lib feature.
+                    unnecessary_stable_feature_lint(tcx, *span, feature, since);
+                }
+            }
+            remaining_lib_features.remove(&feature);
+            if remaining_lib_features.is_empty() {
+                break;
+            }
+        }
+    };
+
+    // We always collect the lib features declared in the current crate, even if there are
+    // no unknown features, because the collection also does feature attribute validation.
+    let local_defined_features = tcx.lib_features().to_vec();
+    if !remaining_lib_features.is_empty() {
+        check_features(&mut remaining_lib_features, &local_defined_features);
+
+        for &cnum in &*tcx.crates() {
+            if remaining_lib_features.is_empty() {
+                break;
+            }
+            check_features(&mut remaining_lib_features, tcx.defined_lib_features(cnum));
+        }
+    }
+
+    for (feature, span) in remaining_lib_features {
+        struct_span_err!(tcx.sess, span, E0635, "unknown feature `{}`", feature).emit();
+    }
+
+    // FIXME(#44232): the `used_features` table no longer exists, so we
+    // don't lint about unused features. We should reenable this one day!
+}
+
+fn unnecessary_stable_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol, since: Symbol) {
+    tcx.lint_hir(
+        lint::builtin::STABLE_FEATURES,
+        hir::CRATE_HIR_ID,
+        span,
+        &format!(
+            "the feature `{}` has been stable since {} and no longer requires \
+                  an attribute to enable",
+            feature, since
+        ),
+    );
+}
+
+fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) {
+    struct_span_err!(sess, span, E0636, "the feature `{}` has already been declared", feature)
+        .emit();
+}