about summary refs log tree commit diff
diff options
context:
space:
mode:
authorCamille GILLOT <gillot.camille@gmail.com>2019-12-29 11:20:20 +0100
committerCamille GILLOT <gillot.camille@gmail.com>2020-01-01 14:15:02 +0100
commit36b6630771cb371ffc6aa737ae26c6c8806c7060 (patch)
treede6d040a74b92004bd13571570c89f312c07e2c8
parenteca7797d6b96b67039017b64129bd2bc1898ccd8 (diff)
downloadrust-36b6630771cb371ffc6aa737ae26c6c8806c7060.tar.gz
rust-36b6630771cb371ffc6aa737ae26c6c8806c7060.zip
Move stability queries to librustc_passes.
Contains:
- check_mod_unstable_api_usage query;
- stability_index query;
- check_unused_or_stable features pass.
-rw-r--r--src/librustc/middle/stability.rs410
-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.rs542
5 files changed, 491 insertions, 472 deletions
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
new file mode 100644
index 00000000000..5ef4942ac3b
--- /dev/null
+++ b/src/librustc/middle/stability.rs
@@ -0,0 +1,410 @@
+//! A pass that annotates every item and method with its stability level,
+//! propagating default levels lexically from parent to children ast nodes.
+
+pub use self::StabilityLevel::*;
+
+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::session::{DiagnosticMessageId, Session};
+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::CRATE_NODE_ID;
+use syntax::attr::{self, ConstStability, Deprecation, RustcDeprecation, Stability};
+use syntax::errors::Applicability;
+use syntax::feature_gate::feature_err_issue;
+use syntax::symbol::{sym, Symbol};
+
+use std::num::NonZeroU32;
+
+#[derive(PartialEq, Clone, Copy, Debug)]
+pub enum StabilityLevel {
+    Unstable,
+    Stable,
+}
+
+impl StabilityLevel {
+    pub fn from_attr_level(level: &attr::StabilityLevel) -> Self {
+        if level.is_stable() { Stable } else { Unstable }
+    }
+}
+
+/// An entry in the `depr_map`.
+#[derive(Clone, HashStable)]
+pub struct DeprecationEntry {
+    /// The metadata of the attribute associated with this entry.
+    pub attr: Deprecation,
+    /// The `DefId` where the attr was originally attached. `None` for non-local
+    /// `DefId`'s.
+    origin: Option<HirId>,
+}
+
+impl DeprecationEntry {
+    pub fn local(attr: Deprecation, id: HirId) -> DeprecationEntry {
+        DeprecationEntry { attr, origin: Some(id) }
+    }
+
+    pub fn external(attr: Deprecation) -> DeprecationEntry {
+        DeprecationEntry { attr, origin: None }
+    }
+
+    pub fn same_origin(&self, other: &DeprecationEntry) -> bool {
+        match (self.origin, other.origin) {
+            (Some(o1), Some(o2)) => o1 == o2,
+            _ => false,
+        }
+    }
+}
+
+/// A stability index, giving the stability level for items and methods.
+#[derive(HashStable)]
+pub struct Index<'tcx> {
+    /// This is mostly a cache, except the stabilities of local items
+    /// are filled by the annotator.
+    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.
+    pub staged_api: FxHashMap<CrateNum, bool>,
+
+    /// Features enabled for this crate.
+    pub active_features: FxHashSet<Symbol>,
+}
+
+impl<'tcx> Index<'tcx> {
+    pub fn local_stability(&self, id: HirId) -> Option<&'tcx Stability> {
+        self.stab_map.get(&id).cloned()
+    }
+
+    pub fn local_const_stability(&self, id: HirId) -> Option<&'tcx ConstStability> {
+        self.const_stab_map.get(&id).cloned()
+    }
+
+    pub fn local_deprecation_entry(&self, id: HirId) -> Option<DeprecationEntry> {
+        self.depr_map.get(&id).cloned()
+    }
+}
+
+pub fn report_unstable(
+    sess: &Session,
+    feature: Symbol,
+    reason: Option<Symbol>,
+    issue: Option<NonZeroU32>,
+    is_soft: bool,
+    span: Span,
+    soft_handler: impl FnOnce(&'static lint::Lint, Span, &str),
+) {
+    let msg = match reason {
+        Some(r) => format!("use of unstable library feature '{}': {}", feature, r),
+        None => format!("use of unstable library feature '{}'", &feature),
+    };
+
+    let msp: MultiSpan = span.into();
+    let cm = &sess.parse_sess.source_map();
+    let span_key = msp.primary_span().and_then(|sp: Span| {
+        if !sp.is_dummy() {
+            let file = cm.lookup_char_pos(sp.lo()).file;
+            if file.name.is_macros() { None } else { Some(span) }
+        } else {
+            None
+        }
+    });
+
+    let error_id = (DiagnosticMessageId::StabilityId(issue), span_key, msg.clone());
+    let fresh = sess.one_time_diagnostics.borrow_mut().insert(error_id);
+    if fresh {
+        if is_soft {
+            soft_handler(lint::builtin::SOFT_UNSTABLE, span, &msg)
+        } else {
+            feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg)
+                .emit();
+        }
+    }
+}
+
+/// Checks whether an item marked with `deprecated(since="X")` is currently
+/// deprecated (i.e., whether X is not greater than the current rustc version).
+pub fn deprecation_in_effect(since: &str) -> bool {
+    fn parse_version(ver: &str) -> Vec<u32> {
+        // We ignore non-integer components of the version (e.g., "nightly").
+        ver.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect()
+    }
+
+    if let Some(rustc) = option_env!("CFG_RELEASE") {
+        let since: Vec<u32> = parse_version(since);
+        let rustc: Vec<u32> = parse_version(rustc);
+        // We simply treat invalid `since` attributes as relating to a previous
+        // Rust version, thus always displaying the warning.
+        if since.len() != 3 {
+            return true;
+        }
+        since <= rustc
+    } else {
+        // By default, a deprecation warning applies to
+        // the current version of the compiler.
+        true
+    }
+}
+
+pub fn deprecation_suggestion(
+    diag: &mut DiagnosticBuilder<'_>,
+    suggestion: Option<Symbol>,
+    span: Span,
+) {
+    if let Some(suggestion) = suggestion {
+        diag.span_suggestion(
+            span,
+            "replace the use of the deprecated item",
+            suggestion.to_string(),
+            Applicability::MachineApplicable,
+        );
+    }
+}
+
+fn deprecation_message_common(message: String, reason: Option<Symbol>) -> String {
+    match reason {
+        Some(reason) => format!("{}: {}", message, reason),
+        None => message,
+    }
+}
+
+pub fn deprecation_message(depr: &Deprecation, path: &str) -> (String, &'static Lint) {
+    let message = format!("use of deprecated item '{}'", path);
+    (deprecation_message_common(message, depr.note), lint::builtin::DEPRECATED)
+}
+
+pub fn rustc_deprecation_message(depr: &RustcDeprecation, path: &str) -> (String, &'static Lint) {
+    let (message, lint) = if deprecation_in_effect(&depr.since.as_str()) {
+        (format!("use of deprecated item '{}'", path), lint::builtin::DEPRECATED)
+    } else {
+        (
+            format!(
+                "use of item '{}' that will be deprecated in future version {}",
+                path, depr.since
+            ),
+            lint::builtin::DEPRECATED_IN_FUTURE,
+        )
+    };
+    (deprecation_message_common(message, Some(depr.reason)), lint)
+}
+
+pub fn early_report_deprecation(
+    lint_buffer: &'a mut lint::LintBuffer,
+    message: &str,
+    suggestion: Option<Symbol>,
+    lint: &'static Lint,
+    span: Span,
+) {
+    if in_derive_expansion(span) {
+        return;
+    }
+
+    let diag = BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span);
+    lint_buffer.buffer_lint_with_diagnostic(lint, CRATE_NODE_ID, span, message, diag);
+}
+
+fn late_report_deprecation(
+    tcx: TyCtxt<'_>,
+    message: &str,
+    suggestion: Option<Symbol>,
+    lint: &'static Lint,
+    span: Span,
+    def_id: DefId,
+    hir_id: HirId,
+) {
+    if in_derive_expansion(span) {
+        return;
+    }
+
+    let mut diag = tcx.struct_span_lint_hir(lint, hir_id, span, message);
+    if let hir::Node::Expr(_) = tcx.hir().get(hir_id) {
+        deprecation_suggestion(&mut diag, suggestion, span);
+    }
+    diag.emit();
+    if hir_id == hir::DUMMY_HIR_ID {
+        span_bug!(span, "emitted a {} lint with dummy HIR id: {:?}", lint.name, def_id);
+    }
+}
+
+/// Result of `TyCtxt::eval_stability`.
+pub enum EvalResult {
+    /// We can use the item because it is stable or we provided the
+    /// corresponding feature gate.
+    Allow,
+    /// We cannot use the item because it is unstable and we did not provide the
+    /// corresponding feature gate.
+    Deny { feature: Symbol, reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool },
+    /// The item does not have the `#[stable]` or `#[unstable]` marker assigned.
+    Unmarked,
+}
+
+impl<'tcx> TyCtxt<'tcx> {
+    // See issue #38412.
+    fn skip_stability_check_due_to_privacy(self, mut def_id: DefId) -> bool {
+        // Check if `def_id` is a trait method.
+        match self.def_kind(def_id) {
+            Some(DefKind::Method) | Some(DefKind::AssocTy) | Some(DefKind::AssocConst) => {
+                if let ty::TraitContainer(trait_def_id) = self.associated_item(def_id).container {
+                    // Trait methods do not declare visibility (even
+                    // for visibility info in cstore). Use containing
+                    // trait instead, so methods of `pub` traits are
+                    // themselves considered `pub`.
+                    def_id = trait_def_id;
+                }
+            }
+            _ => {}
+        }
+
+        let visibility = self.visibility(def_id);
+
+        match visibility {
+            // Must check stability for `pub` items.
+            ty::Visibility::Public => false,
+
+            // These are not visible outside crate; therefore
+            // stability markers are irrelevant, if even present.
+            ty::Visibility::Restricted(..) | ty::Visibility::Invisible => true,
+        }
+    }
+
+    /// Evaluates the stability of an item.
+    ///
+    /// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding
+    /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending
+    /// unstable feature otherwise.
+    ///
+    /// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been
+    /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to
+    /// `id`.
+    pub fn eval_stability(self, def_id: DefId, id: Option<HirId>, span: Span) -> EvalResult {
+        // Deprecated attributes apply in-crate and cross-crate.
+        if let Some(id) = id {
+            if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) {
+                let parent_def_id = self.hir().local_def_id(self.hir().get_parent_item(id));
+                let skip = self
+                    .lookup_deprecation_entry(parent_def_id)
+                    .map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry));
+
+                if !skip {
+                    let (message, lint) =
+                        deprecation_message(&depr_entry.attr, &self.def_path_str(def_id));
+                    late_report_deprecation(self, &message, None, lint, span, def_id, id);
+                }
+            };
+        }
+
+        let is_staged_api =
+            self.lookup_stability(DefId { index: CRATE_DEF_INDEX, ..def_id }).is_some();
+        if !is_staged_api {
+            return EvalResult::Allow;
+        }
+
+        let stability = self.lookup_stability(def_id);
+        debug!(
+            "stability: \
+                inspecting def_id={:?} span={:?} of stability={:?}",
+            def_id, span, stability
+        );
+
+        if let Some(id) = id {
+            if let Some(stability) = stability {
+                if let Some(depr) = &stability.rustc_depr {
+                    let (message, lint) =
+                        rustc_deprecation_message(depr, &self.def_path_str(def_id));
+                    late_report_deprecation(
+                        self,
+                        &message,
+                        depr.suggestion,
+                        lint,
+                        span,
+                        def_id,
+                        id,
+                    );
+                }
+            }
+        }
+
+        // Only the cross-crate scenario matters when checking unstable APIs
+        let cross_crate = !def_id.is_local();
+        if !cross_crate {
+            return EvalResult::Allow;
+        }
+
+        // Issue #38412: private items lack stability markers.
+        if self.skip_stability_check_due_to_privacy(def_id) {
+            return EvalResult::Allow;
+        }
+
+        match stability {
+            Some(&Stability {
+                level: attr::Unstable { reason, issue, is_soft }, feature, ..
+            }) => {
+                if span.allows_unstable(feature) {
+                    debug!("stability: skipping span={:?} since it is internal", span);
+                    return EvalResult::Allow;
+                }
+                if self.stability().active_features.contains(&feature) {
+                    return EvalResult::Allow;
+                }
+
+                // When we're compiling the compiler itself we may pull in
+                // crates from crates.io, but those crates may depend on other
+                // crates also pulled in from crates.io. We want to ideally be
+                // able to compile everything without requiring upstream
+                // modifications, so in the case that this looks like a
+                // `rustc_private` crate (e.g., a compiler crate) and we also have
+                // the `-Z force-unstable-if-unmarked` flag present (we're
+                // compiling a compiler crate), then let this missing feature
+                // annotation slide.
+                if feature == sym::rustc_private && issue == NonZeroU32::new(27812) {
+                    if self.sess.opts.debugging_opts.force_unstable_if_unmarked {
+                        return EvalResult::Allow;
+                    }
+                }
+
+                EvalResult::Deny { feature, reason, issue, is_soft }
+            }
+            Some(_) => {
+                // Stable APIs are always ok to call and deprecated APIs are
+                // handled by the lint emitting logic above.
+                EvalResult::Allow
+            }
+            None => EvalResult::Unmarked,
+        }
+    }
+
+    /// Checks if an item is stable or error out.
+    ///
+    /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not
+    /// exist, emits an error.
+    ///
+    /// Additionally, this function will also check if the item is deprecated. If so, and `id` is
+    /// not `None`, a deprecated lint attached to `id` will be emitted.
+    pub fn check_stability(self, def_id: DefId, id: Option<HirId>, span: Span) {
+        let soft_handler =
+            |lint, span, msg: &_| self.lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, msg);
+        match self.eval_stability(def_id, id, span) {
+            EvalResult::Allow => {}
+            EvalResult::Deny { feature, reason, issue, is_soft } => {
+                report_unstable(self.sess, feature, reason, issue, is_soft, span, soft_handler)
+            }
+            EvalResult::Unmarked => {
+                // The API could be uncallable for other reasons, for example when a private module
+                // was referenced.
+                self.sess.delay_span_bug(span, &format!("encountered unmarked API: {:?}", def_id));
+            }
+        }
+    }
+}
+
+impl<'tcx> TyCtxt<'tcx> {
+    pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
+        self.lookup_deprecation_entry(id).map(|depr| depr.attr)
+    }
+}
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
index 7b186bc7a14..fcbc742b9a0 100644
--- a/src/librustc_passes/stability.rs
+++ b/src/librustc_passes/stability.rs
@@ -1,26 +1,21 @@
 //! A pass that annotates every item and method with its stability level,
 //! propagating default levels lexically from parent to children ast nodes.
 
-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::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::attr::{self, ConstStability, Deprecation, RustcDeprecation, Stability};
-use syntax::errors::Applicability;
-use syntax::feature_gate::{feature_err, feature_err_issue};
+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;
@@ -29,18 +24,6 @@ use std::num::NonZeroU32;
 
 use rustc_error_codes::*;
 
-#[derive(PartialEq, Clone, Copy, Debug)]
-pub enum StabilityLevel {
-    Unstable,
-    Stable,
-}
-
-impl StabilityLevel {
-    pub fn from_attr_level(level: &attr::StabilityLevel) -> Self {
-        if level.is_stable() { Stable } else { Unstable }
-    }
-}
-
 #[derive(PartialEq)]
 enum AnnotationKind {
     // Annotation is required if not inherited from unstable parents
@@ -51,49 +34,6 @@ enum AnnotationKind {
     Container,
 }
 
-/// An entry in the `depr_map`.
-#[derive(Clone, HashStable)]
-pub struct DeprecationEntry {
-    /// The metadata of the attribute associated with this entry.
-    pub attr: Deprecation,
-    /// The `DefId` where the attr was originally attached. `None` for non-local
-    /// `DefId`'s.
-    origin: Option<HirId>,
-}
-
-impl DeprecationEntry {
-    fn local(attr: Deprecation, id: HirId) -> DeprecationEntry {
-        DeprecationEntry { attr, origin: Some(id) }
-    }
-
-    pub fn external(attr: Deprecation) -> DeprecationEntry {
-        DeprecationEntry { attr, origin: None }
-    }
-
-    pub fn same_origin(&self, other: &DeprecationEntry) -> bool {
-        match (self.origin, other.origin) {
-            (Some(o1), Some(o2)) => o1 == o2,
-            _ => false,
-        }
-    }
-}
-
-/// A stability index, giving the stability level for items and methods.
-#[derive(HashStable)]
-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>,
-
-    /// Maps for each crate whether it is part of the staged API.
-    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>,
@@ -401,84 +341,70 @@ impl<'a, 'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'a, 'tcx> {
     }
 }
 
-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(),
-        };
+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);
-            }
+    let active_lib_features = &tcx.features().declared_lib_features;
+    let active_lang_features = &tcx.features().declared_lang_features;
 
-            annotator.annotate(
-                hir::CRATE_HIR_ID,
-                &krate.attrs,
-                krate.span,
-                AnnotationKind::Required,
-                |v| intravisit::walk_crate(v, krate),
-            );
-        }
-        return index;
-    }
+    // 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();
 
-    pub fn local_stability(&self, id: HirId) -> Option<&'tcx Stability> {
-        self.stab_map.get(&id).cloned()
-    }
+    {
+        let krate = tcx.hir().krate();
+        let mut annotator = Annotator {
+            tcx,
+            index: &mut index,
+            parent_stab: None,
+            parent_depr: None,
+            in_trait_impl: false,
+        };
 
-    pub fn local_const_stability(&self, id: HirId) -> Option<&'tcx ConstStability> {
-        self.const_stab_map.get(&id).cloned()
-    }
+        // 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);
+        }
 
-    pub fn local_deprecation_entry(&self, id: HirId) -> Option<DeprecationEntry> {
-        self.depr_map.get(&id).cloned()
+        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
@@ -487,326 +413,18 @@ 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<'_>) {
+pub(crate) fn provide(providers: &mut Providers<'_>) {
     *providers = Providers { check_mod_unstable_api_usage, ..*providers };
-}
-
-pub fn report_unstable(
-    sess: &Session,
-    feature: Symbol,
-    reason: Option<Symbol>,
-    issue: Option<NonZeroU32>,
-    is_soft: bool,
-    span: Span,
-    soft_handler: impl FnOnce(&'static lint::Lint, Span, &str),
-) {
-    let msg = match reason {
-        Some(r) => format!("use of unstable library feature '{}': {}", feature, r),
-        None => format!("use of unstable library feature '{}'", &feature),
-    };
-
-    let msp: MultiSpan = span.into();
-    let cm = &sess.parse_sess.source_map();
-    let span_key = msp.primary_span().and_then(|sp: Span| {
-        if !sp.is_dummy() {
-            let file = cm.lookup_char_pos(sp.lo()).file;
-            if file.name.is_macros() { None } else { Some(span) }
-        } else {
-            None
-        }
-    });
-
-    let error_id = (DiagnosticMessageId::StabilityId(issue), span_key, msg.clone());
-    let fresh = sess.one_time_diagnostics.borrow_mut().insert(error_id);
-    if fresh {
-        if is_soft {
-            soft_handler(lint::builtin::SOFT_UNSTABLE, span, &msg)
-        } else {
-            feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), &msg)
-                .emit();
-        }
-    }
-}
-
-/// Checks whether an item marked with `deprecated(since="X")` is currently
-/// deprecated (i.e., whether X is not greater than the current rustc version).
-pub fn deprecation_in_effect(since: &str) -> bool {
-    fn parse_version(ver: &str) -> Vec<u32> {
-        // We ignore non-integer components of the version (e.g., "nightly").
-        ver.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect()
-    }
-
-    if let Some(rustc) = option_env!("CFG_RELEASE") {
-        let since: Vec<u32> = parse_version(since);
-        let rustc: Vec<u32> = parse_version(rustc);
-        // We simply treat invalid `since` attributes as relating to a previous
-        // Rust version, thus always displaying the warning.
-        if since.len() != 3 {
-            return true;
-        }
-        since <= rustc
-    } else {
-        // By default, a deprecation warning applies to
-        // the current version of the compiler.
-        true
-    }
-}
-
-pub fn deprecation_suggestion(
-    diag: &mut DiagnosticBuilder<'_>,
-    suggestion: Option<Symbol>,
-    span: Span,
-) {
-    if let Some(suggestion) = suggestion {
-        diag.span_suggestion(
-            span,
-            "replace the use of the deprecated item",
-            suggestion.to_string(),
-            Applicability::MachineApplicable,
-        );
-    }
-}
-
-fn deprecation_message_common(message: String, reason: Option<Symbol>) -> String {
-    match reason {
-        Some(reason) => format!("{}: {}", message, reason),
-        None => message,
-    }
-}
-
-pub fn deprecation_message(depr: &Deprecation, path: &str) -> (String, &'static Lint) {
-    let message = format!("use of deprecated item '{}'", path);
-    (deprecation_message_common(message, depr.note), lint::builtin::DEPRECATED)
-}
-
-pub fn rustc_deprecation_message(depr: &RustcDeprecation, path: &str) -> (String, &'static Lint) {
-    let (message, lint) = if deprecation_in_effect(&depr.since.as_str()) {
-        (format!("use of deprecated item '{}'", path), lint::builtin::DEPRECATED)
-    } else {
-        (
-            format!(
-                "use of item '{}' that will be deprecated in future version {}",
-                path, depr.since
-            ),
-            lint::builtin::DEPRECATED_IN_FUTURE,
-        )
+    providers.stability_index = |tcx, cnum| {
+        assert_eq!(cnum, LOCAL_CRATE);
+        tcx.arena.alloc(new_index(tcx))
     };
-    (deprecation_message_common(message, Some(depr.reason)), lint)
-}
-
-pub fn early_report_deprecation(
-    lint_buffer: &'a mut lint::LintBuffer,
-    message: &str,
-    suggestion: Option<Symbol>,
-    lint: &'static Lint,
-    span: Span,
-) {
-    if in_derive_expansion(span) {
-        return;
-    }
-
-    let diag = BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span);
-    lint_buffer.buffer_lint_with_diagnostic(lint, CRATE_NODE_ID, span, message, diag);
-}
-
-fn late_report_deprecation(
-    tcx: TyCtxt<'_>,
-    message: &str,
-    suggestion: Option<Symbol>,
-    lint: &'static Lint,
-    span: Span,
-    def_id: DefId,
-    hir_id: HirId,
-) {
-    if in_derive_expansion(span) {
-        return;
-    }
-
-    let mut diag = tcx.struct_span_lint_hir(lint, hir_id, span, message);
-    if let hir::Node::Expr(_) = tcx.hir().get(hir_id) {
-        deprecation_suggestion(&mut diag, suggestion, span);
-    }
-    diag.emit();
-    if hir_id == hir::DUMMY_HIR_ID {
-        span_bug!(span, "emitted a {} lint with dummy HIR id: {:?}", lint.name, def_id);
-    }
 }
 
 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
-    /// corresponding feature gate.
-    Allow,
-    /// We cannot use the item because it is unstable and we did not provide the
-    /// corresponding feature gate.
-    Deny { feature: Symbol, reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool },
-    /// The item does not have the `#[stable]` or `#[unstable]` marker assigned.
-    Unmarked,
-}
-
-impl<'tcx> TyCtxt<'tcx> {
-    // See issue #38412.
-    fn skip_stability_check_due_to_privacy(self, mut def_id: DefId) -> bool {
-        // Check if `def_id` is a trait method.
-        match self.def_kind(def_id) {
-            Some(DefKind::Method) | Some(DefKind::AssocTy) | Some(DefKind::AssocConst) => {
-                if let ty::TraitContainer(trait_def_id) = self.associated_item(def_id).container {
-                    // Trait methods do not declare visibility (even
-                    // for visibility info in cstore). Use containing
-                    // trait instead, so methods of `pub` traits are
-                    // themselves considered `pub`.
-                    def_id = trait_def_id;
-                }
-            }
-            _ => {}
-        }
-
-        let visibility = self.visibility(def_id);
-
-        match visibility {
-            // Must check stability for `pub` items.
-            ty::Visibility::Public => false,
-
-            // These are not visible outside crate; therefore
-            // stability markers are irrelevant, if even present.
-            ty::Visibility::Restricted(..) | ty::Visibility::Invisible => true,
-        }
-    }
-
-    /// Evaluates the stability of an item.
-    ///
-    /// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding
-    /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending
-    /// unstable feature otherwise.
-    ///
-    /// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been
-    /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to
-    /// `id`.
-    pub fn eval_stability(self, def_id: DefId, id: Option<HirId>, span: Span) -> EvalResult {
-        // Deprecated attributes apply in-crate and cross-crate.
-        if let Some(id) = id {
-            if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) {
-                let parent_def_id = self.hir().local_def_id(self.hir().get_parent_item(id));
-                let skip = self
-                    .lookup_deprecation_entry(parent_def_id)
-                    .map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry));
-
-                if !skip {
-                    let (message, lint) =
-                        deprecation_message(&depr_entry.attr, &self.def_path_str(def_id));
-                    late_report_deprecation(self, &message, None, lint, span, def_id, id);
-                }
-            };
-        }
-
-        let is_staged_api =
-            self.lookup_stability(DefId { index: CRATE_DEF_INDEX, ..def_id }).is_some();
-        if !is_staged_api {
-            return EvalResult::Allow;
-        }
-
-        let stability = self.lookup_stability(def_id);
-        debug!(
-            "stability: \
-                inspecting def_id={:?} span={:?} of stability={:?}",
-            def_id, span, stability
-        );
-
-        if let Some(id) = id {
-            if let Some(stability) = stability {
-                if let Some(depr) = &stability.rustc_depr {
-                    let (message, lint) =
-                        rustc_deprecation_message(depr, &self.def_path_str(def_id));
-                    late_report_deprecation(
-                        self,
-                        &message,
-                        depr.suggestion,
-                        lint,
-                        span,
-                        def_id,
-                        id,
-                    );
-                }
-            }
-        }
-
-        // Only the cross-crate scenario matters when checking unstable APIs
-        let cross_crate = !def_id.is_local();
-        if !cross_crate {
-            return EvalResult::Allow;
-        }
-
-        // Issue #38412: private items lack stability markers.
-        if self.skip_stability_check_due_to_privacy(def_id) {
-            return EvalResult::Allow;
-        }
-
-        match stability {
-            Some(&Stability {
-                level: attr::Unstable { reason, issue, is_soft }, feature, ..
-            }) => {
-                if span.allows_unstable(feature) {
-                    debug!("stability: skipping span={:?} since it is internal", span);
-                    return EvalResult::Allow;
-                }
-                if self.stability().active_features.contains(&feature) {
-                    return EvalResult::Allow;
-                }
-
-                // When we're compiling the compiler itself we may pull in
-                // crates from crates.io, but those crates may depend on other
-                // crates also pulled in from crates.io. We want to ideally be
-                // able to compile everything without requiring upstream
-                // modifications, so in the case that this looks like a
-                // `rustc_private` crate (e.g., a compiler crate) and we also have
-                // the `-Z force-unstable-if-unmarked` flag present (we're
-                // compiling a compiler crate), then let this missing feature
-                // annotation slide.
-                if feature == sym::rustc_private && issue == NonZeroU32::new(27812) {
-                    if self.sess.opts.debugging_opts.force_unstable_if_unmarked {
-                        return EvalResult::Allow;
-                    }
-                }
-
-                EvalResult::Deny { feature, reason, issue, is_soft }
-            }
-            Some(_) => {
-                // Stable APIs are always ok to call and deprecated APIs are
-                // handled by the lint emitting logic above.
-                EvalResult::Allow
-            }
-            None => EvalResult::Unmarked,
-        }
-    }
-
-    /// Checks if an item is stable or error out.
-    ///
-    /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not
-    /// exist, emits an error.
-    ///
-    /// Additionally, this function will also check if the item is deprecated. If so, and `id` is
-    /// not `None`, a deprecated lint attached to `id` will be emitted.
-    pub fn check_stability(self, def_id: DefId, id: Option<HirId>, span: Span) {
-        let soft_handler =
-            |lint, span, msg: &_| self.lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, msg);
-        match self.eval_stability(def_id, id, span) {
-            EvalResult::Allow => {}
-            EvalResult::Deny { feature, reason, issue, is_soft } => {
-                report_unstable(self.sess, feature, reason, issue, is_soft, span, soft_handler)
-            }
-            EvalResult::Unmarked => {
-                // The API could be uncallable for other reasons, for example when a private module
-                // was referenced.
-                self.sess.delay_span_bug(span, &format!("encountered unmarked API: {:?}", def_id));
-            }
-        }
-    }
-}
-
 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
@@ -894,12 +512,6 @@ impl Visitor<'tcx> for Checker<'tcx> {
     }
 }
 
-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.