Add stability inheritance
This commit makes several changes to the stability index infrastructure:
* Stability levels are now inherited lexically, i.e., each item's
stability level becomes the default for any nested items.
* The computed stability level for an item is stored as part of the
metadata. When using an item from an external crate, this data is
looked up and cached.
* The stability lint works from the computed stability level, rather
than manual stability attribute annotations. However, the lint still
checks only a limited set of item uses (e.g., it does not check every
component of a path on import). This will be addressed in a later PR,
as part of issue #8962.
* The stability lint only applies to items originating from external
crates, since the stability index is intended as a promise to
downstream crates.
* The "experimental" lint is now _allow_ by default. This is because
almost all existing crates have been marked "experimental", pending
library stabilization. With inheritance in place, this would generate
a massive explosion of warnings for every Rust program.
The lint should be changed back to deny-by-default after library
stabilization is complete.
* The "deprecated" lint still warns by default.
The net result: we can begin tracking stability index for the standard
libraries as we stabilize, without impacting most clients.
Closes #13540.
2014-06-11 17:23:11 -07:00
|
|
|
//! A pass that annotates every item and method with its stability level,
|
|
|
|
|
//! propagating default levels lexically from parent to children ast nodes.
|
|
|
|
|
|
2023-02-05 02:49:50 +01:00
|
|
|
use std::num::NonZero;
|
2024-07-29 08:13:50 +10:00
|
|
|
|
2025-02-05 11:18:18 -08:00
|
|
|
use rustc_ast_lowering::stability::extern_abi_stability;
|
2025-05-09 23:16:55 +02:00
|
|
|
use rustc_attr_data_structures::{
|
2023-01-30 20:47:11 +00:00
|
|
|
self as attrs, AttributeKind, ConstStability, DefaultBodyStability, DeprecatedSince, Stability,
|
|
|
|
|
StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER, find_attr,
|
2022-04-27 18:14:19 +04:00
|
|
|
};
|
2023-12-21 10:52:27 +01:00
|
|
|
use rustc_data_structures::fx::FxIndexMap;
|
|
|
|
|
use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
|
2025-07-13 11:30:30 +00:00
|
|
|
use rustc_feature::{EnabledLangFeature, EnabledLibFeature};
|
2020-01-05 02:37:57 +01:00
|
|
|
use rustc_hir::def::{DefKind, Res};
|
2023-11-17 23:40:04 +00:00
|
|
|
use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId};
|
2025-01-11 19:12:36 +00:00
|
|
|
use rustc_hir::intravisit::{self, Visitor, VisitorExt};
|
|
|
|
|
use rustc_hir::{self as hir, AmbigArg, FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant};
|
2021-11-03 18:03:12 -05:00
|
|
|
use rustc_middle::hir::nested_filter;
|
2023-09-23 05:38:49 +00:00
|
|
|
use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures};
|
2022-09-22 16:19:53 +03:00
|
|
|
use rustc_middle::middle::privacy::EffectiveVisibilities;
|
2025-07-12 10:58:33 +00:00
|
|
|
use rustc_middle::middle::stability::{AllowUnstable, Deprecated, DeprecationEntry, EvalResult};
|
|
|
|
|
use rustc_middle::query::{LocalCrate, Providers};
|
2023-05-15 06:24:45 +02:00
|
|
|
use rustc_middle::ty::TyCtxt;
|
2025-01-01 19:09:01 +01:00
|
|
|
use rustc_middle::ty::print::with_no_trimmed_paths;
|
2020-03-11 12:49:08 +01:00
|
|
|
use rustc_session::lint;
|
2025-07-12 14:24:30 +00:00
|
|
|
use rustc_session::lint::builtin::{DEPRECATED, INEFFECTIVE_UNSTABLE_TRAIT_IMPL};
|
2024-12-13 10:29:23 +11:00
|
|
|
use rustc_span::{Span, Symbol, sym};
|
2025-07-16 22:32:05 +00:00
|
|
|
use tracing::instrument;
|
2014-09-12 13:10:30 +03:00
|
|
|
|
2023-05-15 06:24:45 +02:00
|
|
|
use crate::errors;
|
2024-07-29 08:13:50 +10:00
|
|
|
|
2015-11-16 19:53:41 +03:00
|
|
|
#[derive(PartialEq)]
|
|
|
|
|
enum AnnotationKind {
|
2022-07-13 10:09:37 +01:00
|
|
|
/// Annotation is required if not inherited from unstable parents.
|
2015-11-16 21:01:06 +03:00
|
|
|
Required,
|
2022-07-13 10:09:37 +01:00
|
|
|
/// Annotation is useless, reject it.
|
2015-11-16 21:01:06 +03:00
|
|
|
Prohibited,
|
2022-07-13 10:09:37 +01:00
|
|
|
/// Deprecation annotation is useless, reject it. (Stability attribute is still required.)
|
2020-10-31 15:34:10 +01:00
|
|
|
DeprecationProhibited,
|
2022-07-13 10:09:37 +01:00
|
|
|
/// Annotation itself is useless, but it can be propagated to children.
|
2015-11-16 21:01:06 +03:00
|
|
|
Container,
|
2015-11-16 19:53:41 +03:00
|
|
|
}
|
|
|
|
|
|
2025-07-16 22:34:00 +00:00
|
|
|
fn inherit_deprecation(def_kind: DefKind) -> bool {
|
2022-01-18 21:01:07 +01:00
|
|
|
match def_kind {
|
2025-07-16 22:34:00 +00:00
|
|
|
DefKind::LifetimeParam | DefKind::TyParam | DefKind::ConstParam => false,
|
|
|
|
|
_ => true,
|
2022-01-18 21:01:07 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-16 22:34:00 +00:00
|
|
|
fn inherit_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
2025-07-12 10:41:16 +00:00
|
|
|
let def_kind = tcx.def_kind(def_id);
|
|
|
|
|
match def_kind {
|
|
|
|
|
DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {
|
|
|
|
|
match tcx.def_kind(tcx.local_parent(def_id)) {
|
2025-07-16 22:34:00 +00:00
|
|
|
DefKind::Impl { of_trait: true } => true,
|
|
|
|
|
_ => false,
|
2025-07-12 10:41:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
2025-07-16 22:34:00 +00:00
|
|
|
_ => false,
|
2025-07-12 10:41:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-12 14:33:04 +00:00
|
|
|
fn annotation_kind(tcx: TyCtxt<'_>, def_id: LocalDefId) -> AnnotationKind {
|
|
|
|
|
let def_kind = tcx.def_kind(def_id);
|
|
|
|
|
match def_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.
|
|
|
|
|
DefKind::Impl { of_trait: false } | DefKind::ForeignMod => AnnotationKind::Container,
|
|
|
|
|
DefKind::Impl { of_trait: true } => AnnotationKind::DeprecationProhibited,
|
|
|
|
|
|
|
|
|
|
// Allow stability attributes on default generic arguments.
|
|
|
|
|
DefKind::TyParam | DefKind::ConstParam => {
|
|
|
|
|
match &tcx.hir_node_by_def_id(def_id).expect_generic_param().kind {
|
|
|
|
|
hir::GenericParamKind::Type { default: Some(_), .. }
|
|
|
|
|
| hir::GenericParamKind::Const { default: Some(_), .. } => {
|
|
|
|
|
AnnotationKind::Container
|
|
|
|
|
}
|
|
|
|
|
_ => AnnotationKind::Prohibited,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Impl items in trait impls cannot have stability.
|
|
|
|
|
DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst => {
|
|
|
|
|
match tcx.def_kind(tcx.local_parent(def_id)) {
|
|
|
|
|
DefKind::Impl { of_trait: true } => AnnotationKind::Prohibited,
|
|
|
|
|
_ => AnnotationKind::Required,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_ => AnnotationKind::Required,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-18 21:01:07 +01:00
|
|
|
fn lookup_deprecation_entry(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DeprecationEntry> {
|
|
|
|
|
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
|
|
|
|
|
let depr = attrs::find_attr!(attrs,
|
|
|
|
|
AttributeKind::Deprecation { deprecation, span: _ } => *deprecation
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let Some(depr) = depr else {
|
2025-07-16 22:34:00 +00:00
|
|
|
if inherit_deprecation(tcx.def_kind(def_id)) {
|
2022-01-18 21:01:07 +01:00
|
|
|
let parent_id = tcx.opt_local_parent(def_id)?;
|
|
|
|
|
let parent_depr = tcx.lookup_deprecation_entry(parent_id)?;
|
|
|
|
|
return Some(parent_depr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return None;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// `Deprecation` is just two pointers, no need to intern it
|
|
|
|
|
Some(DeprecationEntry::local(depr, def_id))
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-16 22:34:00 +00:00
|
|
|
fn inherit_stability(def_kind: DefKind) -> bool {
|
2025-07-12 10:15:25 +00:00
|
|
|
match def_kind {
|
2025-07-16 22:34:00 +00:00
|
|
|
DefKind::Field | DefKind::Variant | DefKind::Ctor(..) => true,
|
|
|
|
|
_ => false,
|
2025-07-12 10:15:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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
|
2025-07-16 22:42:55 +00:00
|
|
|
/// compiling library and `rustc_*` crates themselves so we can leverage crates.io
|
2025-07-12 10:15:25 +00:00
|
|
|
/// while maintaining the invariant that all sysroot crates are unstable
|
|
|
|
|
/// by default and are unable to be used.
|
|
|
|
|
const FORCE_UNSTABLE: Stability = Stability {
|
|
|
|
|
level: attrs::StabilityLevel::Unstable {
|
|
|
|
|
reason: UnstableReason::Default,
|
|
|
|
|
issue: NonZero::new(27812),
|
|
|
|
|
is_soft: false,
|
|
|
|
|
implied_by: None,
|
|
|
|
|
old_name: None,
|
|
|
|
|
},
|
|
|
|
|
feature: sym::rustc_private,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#[instrument(level = "debug", skip(tcx))]
|
|
|
|
|
fn lookup_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<Stability> {
|
|
|
|
|
// Propagate unstability. This can happen even for non-staged-api crates in case
|
|
|
|
|
// -Zforce-unstable-if-unmarked is set.
|
|
|
|
|
if !tcx.features().staged_api() {
|
|
|
|
|
if !tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let Some(parent) = tcx.opt_local_parent(def_id) else { return Some(FORCE_UNSTABLE) };
|
|
|
|
|
|
2025-07-16 22:34:00 +00:00
|
|
|
if inherit_deprecation(tcx.def_kind(def_id)) {
|
2025-07-12 10:15:25 +00:00
|
|
|
let parent = tcx.lookup_stability(parent)?;
|
|
|
|
|
if parent.is_unstable() {
|
|
|
|
|
return Some(parent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// # Regular stability
|
|
|
|
|
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
|
|
|
|
|
let stab =
|
|
|
|
|
attrs::find_attr!(attrs, AttributeKind::Stability { stability, span: _ } => *stability);
|
|
|
|
|
|
|
|
|
|
if let Some(stab) = stab {
|
|
|
|
|
return Some(stab);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-16 22:34:00 +00:00
|
|
|
if inherit_deprecation(tcx.def_kind(def_id)) {
|
2025-07-12 10:15:25 +00:00
|
|
|
let Some(parent) = tcx.opt_local_parent(def_id) else {
|
|
|
|
|
return tcx
|
|
|
|
|
.sess
|
|
|
|
|
.opts
|
|
|
|
|
.unstable_opts
|
|
|
|
|
.force_unstable_if_unmarked
|
|
|
|
|
.then_some(FORCE_UNSTABLE);
|
|
|
|
|
};
|
|
|
|
|
let parent = tcx.lookup_stability(parent)?;
|
2025-07-16 22:34:00 +00:00
|
|
|
if parent.is_unstable() || inherit_stability(tcx.def_kind(def_id)) {
|
2025-07-12 10:15:25 +00:00
|
|
|
return Some(parent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-30 20:47:11 +00:00
|
|
|
#[instrument(level = "debug", skip(tcx))]
|
|
|
|
|
fn lookup_default_body_stability(
|
|
|
|
|
tcx: TyCtxt<'_>,
|
|
|
|
|
def_id: LocalDefId,
|
|
|
|
|
) -> Option<DefaultBodyStability> {
|
|
|
|
|
if !tcx.features().staged_api() {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
|
|
|
|
|
// FIXME: check that this item can have body stability
|
|
|
|
|
attrs::find_attr!(attrs, AttributeKind::BodyStability { stability, .. } => *stability)
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-12 10:41:16 +00:00
|
|
|
#[instrument(level = "debug", skip(tcx))]
|
|
|
|
|
fn lookup_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ConstStability> {
|
|
|
|
|
if !tcx.features().staged_api() {
|
|
|
|
|
// Propagate unstability. This can happen even for non-staged-api crates in case
|
|
|
|
|
// -Zforce-unstable-if-unmarked is set.
|
2025-07-16 22:34:00 +00:00
|
|
|
if inherit_deprecation(tcx.def_kind(def_id)) {
|
2025-07-12 10:41:16 +00:00
|
|
|
let parent = tcx.opt_local_parent(def_id)?;
|
|
|
|
|
let parent_stab = tcx.lookup_stability(parent)?;
|
|
|
|
|
if parent_stab.is_unstable()
|
|
|
|
|
&& let Some(fn_sig) = tcx.hir_node_by_def_id(def_id).fn_sig()
|
|
|
|
|
&& fn_sig.header.is_const()
|
|
|
|
|
{
|
|
|
|
|
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
|
|
|
|
|
let const_stability_indirect =
|
|
|
|
|
find_attr!(attrs, AttributeKind::ConstStabilityIndirect);
|
|
|
|
|
return Some(ConstStability::unmarked(const_stability_indirect, parent_stab));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
|
|
|
|
|
let const_stability_indirect = find_attr!(attrs, AttributeKind::ConstStabilityIndirect);
|
|
|
|
|
let const_stab = attrs::find_attr!(attrs, AttributeKind::ConstStability { stability, span: _ } => *stability);
|
|
|
|
|
|
|
|
|
|
// After checking the immediate attributes, get rid of the span and compute implied
|
|
|
|
|
// const stability: inherit feature gate from regular stability.
|
|
|
|
|
let mut const_stab = const_stab
|
|
|
|
|
.map(|const_stab| ConstStability::from_partial(const_stab, const_stability_indirect));
|
|
|
|
|
|
2025-07-16 22:43:26 +00:00
|
|
|
// If this is a const fn but not annotated with stability markers, see if we can inherit
|
|
|
|
|
// regular stability.
|
2025-07-12 10:41:16 +00:00
|
|
|
if let Some(fn_sig) = tcx.hir_node_by_def_id(def_id).fn_sig()
|
2025-07-16 22:43:26 +00:00
|
|
|
&& fn_sig.header.is_const()
|
|
|
|
|
&& const_stab.is_none()
|
|
|
|
|
// We only ever inherit unstable features.
|
|
|
|
|
&& let Some(inherit_regular_stab) = tcx.lookup_stability(def_id)
|
|
|
|
|
&& inherit_regular_stab.is_unstable()
|
2025-07-12 10:41:16 +00:00
|
|
|
{
|
|
|
|
|
const_stab = Some(ConstStability {
|
|
|
|
|
// We subject these implicitly-const functions to recursive const stability.
|
|
|
|
|
const_stable_indirect: true,
|
|
|
|
|
promotable: false,
|
|
|
|
|
level: inherit_regular_stab.level,
|
|
|
|
|
feature: inherit_regular_stab.feature,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(const_stab) = const_stab {
|
|
|
|
|
return Some(const_stab);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-16 22:42:55 +00:00
|
|
|
// `impl const Trait for Type` items forward their const stability to their immediate children.
|
2025-07-12 10:41:16 +00:00
|
|
|
// FIXME(const_trait_impl): how is this supposed to interact with `#[rustc_const_stable_indirect]`?
|
|
|
|
|
// Currently, once that is set, we do not inherit anything from the parent any more.
|
2025-07-16 22:34:00 +00:00
|
|
|
if inherit_const_stability(tcx, def_id) {
|
2025-07-12 10:41:16 +00:00
|
|
|
let parent = tcx.opt_local_parent(def_id)?;
|
|
|
|
|
let parent = tcx.lookup_const_stability(parent)?;
|
|
|
|
|
if parent.is_const_unstable() {
|
|
|
|
|
return Some(parent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-13 10:09:37 +01:00
|
|
|
/// A private tree-walker for producing an `Index`.
|
2025-07-12 10:58:33 +00:00
|
|
|
struct Annotator<'tcx> {
|
2019-06-14 00:48:52 +03:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2025-07-12 10:58:33 +00:00
|
|
|
implications: UnordMap<Symbol, Symbol>,
|
Add stability inheritance
This commit makes several changes to the stability index infrastructure:
* Stability levels are now inherited lexically, i.e., each item's
stability level becomes the default for any nested items.
* The computed stability level for an item is stored as part of the
metadata. When using an item from an external crate, this data is
looked up and cached.
* The stability lint works from the computed stability level, rather
than manual stability attribute annotations. However, the lint still
checks only a limited set of item uses (e.g., it does not check every
component of a path on import). This will be addressed in a later PR,
as part of issue #8962.
* The stability lint only applies to items originating from external
crates, since the stability index is intended as a promise to
downstream crates.
* The "experimental" lint is now _allow_ by default. This is because
almost all existing crates have been marked "experimental", pending
library stabilization. With inheritance in place, this would generate
a massive explosion of warnings for every Rust program.
The lint should be changed back to deny-by-default after library
stabilization is complete.
* The "deprecated" lint still warns by default.
The net result: we can begin tracking stability index for the standard
libraries as we stabilize, without impacting most clients.
Closes #13540.
2014-06-11 17:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
2025-07-12 10:58:33 +00:00
|
|
|
impl<'tcx> Annotator<'tcx> {
|
2022-07-13 10:09:37 +01:00
|
|
|
/// 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. If the node is a function,
|
|
|
|
|
/// `fn_sig` is its signature.
|
2025-07-12 10:57:57 +00:00
|
|
|
#[instrument(level = "trace", skip(self))]
|
|
|
|
|
fn annotate(&mut self, def_id: LocalDefId) {
|
2024-10-09 09:01:57 +02:00
|
|
|
if !self.tcx.features().staged_api() {
|
2020-07-20 10:44:43 -04:00
|
|
|
return;
|
2020-02-07 09:56:56 -08:00
|
|
|
}
|
2020-02-07 10:24:22 -08:00
|
|
|
|
2025-07-12 10:57:57 +00:00
|
|
|
if let Some(stability) = self.tcx.lookup_stability(def_id)
|
|
|
|
|
&& let StabilityLevel::Unstable { implied_by: Some(implied_by), .. } = stability.level
|
2025-07-12 10:15:25 +00:00
|
|
|
{
|
2025-07-12 10:58:33 +00:00
|
|
|
self.implications.insert(implied_by, stability.feature);
|
2020-02-07 10:24:22 -08:00
|
|
|
}
|
|
|
|
|
|
2025-07-12 10:57:57 +00:00
|
|
|
if let Some(stability) = self.tcx.lookup_const_stability(def_id)
|
|
|
|
|
&& let StabilityLevel::Unstable { implied_by: Some(implied_by), .. } = stability.level
|
2024-11-02 21:19:21 +01:00
|
|
|
{
|
2025-07-12 10:58:33 +00:00
|
|
|
self.implications.insert(implied_by, stability.feature);
|
2024-11-02 21:19:21 +01:00
|
|
|
}
|
2020-02-07 09:56:56 -08:00
|
|
|
}
|
Add stability inheritance
This commit makes several changes to the stability index infrastructure:
* Stability levels are now inherited lexically, i.e., each item's
stability level becomes the default for any nested items.
* The computed stability level for an item is stored as part of the
metadata. When using an item from an external crate, this data is
looked up and cached.
* The stability lint works from the computed stability level, rather
than manual stability attribute annotations. However, the lint still
checks only a limited set of item uses (e.g., it does not check every
component of a path on import). This will be addressed in a later PR,
as part of issue #8962.
* The stability lint only applies to items originating from external
crates, since the stability index is intended as a promise to
downstream crates.
* The "experimental" lint is now _allow_ by default. This is because
almost all existing crates have been marked "experimental", pending
library stabilization. With inheritance in place, this would generate
a massive explosion of warnings for every Rust program.
The lint should be changed back to deny-by-default after library
stabilization is complete.
* The "deprecated" lint still warns by default.
The net result: we can begin tracking stability index for the standard
libraries as we stabilize, without impacting most clients.
Closes #13540.
2014-06-11 17:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
2025-07-12 10:58:33 +00:00
|
|
|
impl<'tcx> Visitor<'tcx> for Annotator<'tcx> {
|
2015-11-17 18:56:13 -05:00
|
|
|
/// Because stability levels are scoped lexically, we want to walk
|
|
|
|
|
/// nested items in the context of the outer item, so enable
|
|
|
|
|
/// deep-walking.
|
2021-11-03 18:03:12 -05:00
|
|
|
type NestedFilter = nested_filter::All;
|
2020-01-07 17:25:33 +01:00
|
|
|
|
2025-02-03 14:42:01 +11:00
|
|
|
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
|
|
|
|
|
self.tcx
|
2016-11-04 18:20:15 -04:00
|
|
|
}
|
|
|
|
|
|
2019-11-28 19:28:50 +01:00
|
|
|
fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
|
2019-09-26 17:51:36 +01:00
|
|
|
match i.kind {
|
2025-05-28 15:18:48 +10:00
|
|
|
hir::ItemKind::Struct(_, _, ref sd) => {
|
2022-11-06 19:46:55 +00:00
|
|
|
if let Some(ctor_def_id) = sd.ctor_def_id() {
|
2025-07-12 10:57:57 +00:00
|
|
|
self.annotate(ctor_def_id);
|
2015-11-16 19:53:41 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
2014-11-11 12:46:47 -08:00
|
|
|
}
|
Add stability inheritance
This commit makes several changes to the stability index infrastructure:
* Stability levels are now inherited lexically, i.e., each item's
stability level becomes the default for any nested items.
* The computed stability level for an item is stored as part of the
metadata. When using an item from an external crate, this data is
looked up and cached.
* The stability lint works from the computed stability level, rather
than manual stability attribute annotations. However, the lint still
checks only a limited set of item uses (e.g., it does not check every
component of a path on import). This will be addressed in a later PR,
as part of issue #8962.
* The stability lint only applies to items originating from external
crates, since the stability index is intended as a promise to
downstream crates.
* The "experimental" lint is now _allow_ by default. This is because
almost all existing crates have been marked "experimental", pending
library stabilization. With inheritance in place, this would generate
a massive explosion of warnings for every Rust program.
The lint should be changed back to deny-by-default after library
stabilization is complete.
* The "deprecated" lint still warns by default.
The net result: we can begin tracking stability index for the standard
libraries as we stabilize, without impacting most clients.
Closes #13540.
2014-06-11 17:23:11 -07:00
|
|
|
|
2025-07-12 10:57:57 +00:00
|
|
|
self.annotate(i.owner_id.def_id);
|
|
|
|
|
intravisit::walk_item(self, i)
|
Add stability inheritance
This commit makes several changes to the stability index infrastructure:
* Stability levels are now inherited lexically, i.e., each item's
stability level becomes the default for any nested items.
* The computed stability level for an item is stored as part of the
metadata. When using an item from an external crate, this data is
looked up and cached.
* The stability lint works from the computed stability level, rather
than manual stability attribute annotations. However, the lint still
checks only a limited set of item uses (e.g., it does not check every
component of a path on import). This will be addressed in a later PR,
as part of issue #8962.
* The stability lint only applies to items originating from external
crates, since the stability index is intended as a promise to
downstream crates.
* The "experimental" lint is now _allow_ by default. This is because
almost all existing crates have been marked "experimental", pending
library stabilization. With inheritance in place, this would generate
a massive explosion of warnings for every Rust program.
The lint should be changed back to deny-by-default after library
stabilization is complete.
* The "deprecated" lint still warns by default.
The net result: we can begin tracking stability index for the standard
libraries as we stabilize, without impacting most clients.
Closes #13540.
2014-06-11 17:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
2019-11-28 21:47:10 +01:00
|
|
|
fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
|
2025-07-12 10:57:57 +00:00
|
|
|
self.annotate(ti.owner_id.def_id);
|
|
|
|
|
intravisit::walk_trait_item(self, ti);
|
2015-03-10 12:28:44 +02:00
|
|
|
}
|
2014-08-05 19:44:21 -07:00
|
|
|
|
2019-11-28 22:16:44 +01:00
|
|
|
fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
|
2025-07-12 10:57:57 +00:00
|
|
|
self.annotate(ii.owner_id.def_id);
|
|
|
|
|
intravisit::walk_impl_item(self, ii);
|
Add stability inheritance
This commit makes several changes to the stability index infrastructure:
* Stability levels are now inherited lexically, i.e., each item's
stability level becomes the default for any nested items.
* The computed stability level for an item is stored as part of the
metadata. When using an item from an external crate, this data is
looked up and cached.
* The stability lint works from the computed stability level, rather
than manual stability attribute annotations. However, the lint still
checks only a limited set of item uses (e.g., it does not check every
component of a path on import). This will be addressed in a later PR,
as part of issue #8962.
* The stability lint only applies to items originating from external
crates, since the stability index is intended as a promise to
downstream crates.
* The "experimental" lint is now _allow_ by default. This is because
almost all existing crates have been marked "experimental", pending
library stabilization. With inheritance in place, this would generate
a massive explosion of warnings for every Rust program.
The lint should be changed back to deny-by-default after library
stabilization is complete.
* The "deprecated" lint still warns by default.
The net result: we can begin tracking stability index for the standard
libraries as we stabilize, without impacting most clients.
Closes #13540.
2014-06-11 17:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
2022-08-10 11:22:01 +10:00
|
|
|
fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) {
|
2025-07-12 10:57:57 +00:00
|
|
|
self.annotate(var.def_id);
|
|
|
|
|
if let Some(ctor_def_id) = var.data.ctor_def_id() {
|
|
|
|
|
self.annotate(ctor_def_id);
|
|
|
|
|
}
|
2019-03-21 23:38:50 +01:00
|
|
|
|
2025-07-12 10:57:57 +00:00
|
|
|
intravisit::walk_variant(self, var)
|
Add stability inheritance
This commit makes several changes to the stability index infrastructure:
* Stability levels are now inherited lexically, i.e., each item's
stability level becomes the default for any nested items.
* The computed stability level for an item is stored as part of the
metadata. When using an item from an external crate, this data is
looked up and cached.
* The stability lint works from the computed stability level, rather
than manual stability attribute annotations. However, the lint still
checks only a limited set of item uses (e.g., it does not check every
component of a path on import). This will be addressed in a later PR,
as part of issue #8962.
* The stability lint only applies to items originating from external
crates, since the stability index is intended as a promise to
downstream crates.
* The "experimental" lint is now _allow_ by default. This is because
almost all existing crates have been marked "experimental", pending
library stabilization. With inheritance in place, this would generate
a massive explosion of warnings for every Rust program.
The lint should be changed back to deny-by-default after library
stabilization is complete.
* The "deprecated" lint still warns by default.
The net result: we can begin tracking stability index for the standard
libraries as we stabilize, without impacting most clients.
Closes #13540.
2014-06-11 17:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
2021-03-16 00:36:07 +03:00
|
|
|
fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) {
|
2025-07-12 10:57:57 +00:00
|
|
|
self.annotate(s.def_id);
|
|
|
|
|
intravisit::walk_field_def(self, s);
|
2014-07-10 11:17:40 -07:00
|
|
|
}
|
2014-12-20 10:08:16 -08:00
|
|
|
|
2019-11-28 20:18:29 +01:00
|
|
|
fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
|
2025-07-12 10:57:57 +00:00
|
|
|
self.annotate(i.owner_id.def_id);
|
|
|
|
|
intravisit::walk_foreign_item(self, i);
|
2015-11-16 19:53:41 +03:00
|
|
|
}
|
|
|
|
|
|
2020-05-17 23:00:19 -04:00
|
|
|
fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
|
2025-07-12 10:57:57 +00:00
|
|
|
self.annotate(p.def_id);
|
|
|
|
|
intravisit::walk_generic_param(self, p);
|
2014-12-20 10:08:16 -08:00
|
|
|
}
|
Add stability inheritance
This commit makes several changes to the stability index infrastructure:
* Stability levels are now inherited lexically, i.e., each item's
stability level becomes the default for any nested items.
* The computed stability level for an item is stored as part of the
metadata. When using an item from an external crate, this data is
looked up and cached.
* The stability lint works from the computed stability level, rather
than manual stability attribute annotations. However, the lint still
checks only a limited set of item uses (e.g., it does not check every
component of a path on import). This will be addressed in a later PR,
as part of issue #8962.
* The stability lint only applies to items originating from external
crates, since the stability index is intended as a promise to
downstream crates.
* The "experimental" lint is now _allow_ by default. This is because
almost all existing crates have been marked "experimental", pending
library stabilization. With inheritance in place, this would generate
a massive explosion of warnings for every Rust program.
The lint should be changed back to deny-by-default after library
stabilization is complete.
* The "deprecated" lint still warns by default.
The net result: we can begin tracking stability index for the standard
libraries as we stabilize, without impacting most clients.
Closes #13540.
2014-06-11 17:23:11 -07:00
|
|
|
}
|
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
struct MissingStabilityAnnotations<'tcx> {
|
2019-06-14 00:48:52 +03:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2022-09-22 16:19:53 +03:00
|
|
|
effective_visibilities: &'tcx EffectiveVisibilities,
|
2016-11-10 19:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> MissingStabilityAnnotations<'tcx> {
|
2025-07-13 11:30:30 +00:00
|
|
|
/// Verify that deprecation and stability attributes make sense with one another.
|
2025-07-12 14:33:04 +00:00
|
|
|
#[instrument(level = "trace", skip(self))]
|
2022-01-30 15:41:35 +01:00
|
|
|
fn check_compatible_stability(&self, def_id: LocalDefId) {
|
2025-07-12 14:33:04 +00:00
|
|
|
if !self.tcx.features().staged_api() {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let depr = self.tcx.lookup_deprecation_entry(def_id);
|
|
|
|
|
let stab = self.tcx.lookup_stability(def_id);
|
|
|
|
|
let const_stab = self.tcx.lookup_const_stability(def_id);
|
|
|
|
|
|
|
|
|
|
macro_rules! find_attr_span {
|
|
|
|
|
($name:ident) => {{
|
|
|
|
|
let attrs = self.tcx.hir_attrs(self.tcx.local_def_id_to_hir_id(def_id));
|
|
|
|
|
attrs::find_attr!(attrs, AttributeKind::$name { span, .. } => *span)
|
|
|
|
|
}}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if stab.is_none()
|
|
|
|
|
&& depr.map_or(false, |d| d.attr.is_since_rustc_version())
|
|
|
|
|
&& let Some(span) = find_attr_span!(Deprecation)
|
|
|
|
|
{
|
|
|
|
|
self.tcx.dcx().emit_err(errors::DeprecatedAttribute { span });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(stab) = stab {
|
|
|
|
|
// Error if prohibited, or can't inherit anything from a container.
|
|
|
|
|
let kind = annotation_kind(self.tcx, def_id);
|
|
|
|
|
if kind == AnnotationKind::Prohibited
|
|
|
|
|
|| (kind == AnnotationKind::Container && stab.level.is_stable() && depr.is_some())
|
|
|
|
|
{
|
|
|
|
|
if let Some(span) = find_attr_span!(Stability) {
|
2022-01-30 15:41:35 +01:00
|
|
|
let item_sp = self.tcx.def_span(def_id);
|
2025-07-12 14:33:04 +00:00
|
|
|
self.tcx.dcx().emit_err(errors::UselessStability { span, item_sp });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if deprecated_since < stable_since. If it is,
|
|
|
|
|
// this is *almost surely* an accident.
|
|
|
|
|
if let Some(depr) = depr
|
|
|
|
|
&& let DeprecatedSince::RustcVersion(dep_since) = depr.attr.since
|
|
|
|
|
&& let attrs::StabilityLevel::Stable { since: stab_since, .. } = stab.level
|
|
|
|
|
&& let Some(span) = find_attr_span!(Stability)
|
|
|
|
|
{
|
2022-01-30 15:41:35 +01:00
|
|
|
let item_sp = self.tcx.def_span(def_id);
|
2025-07-12 14:33:04 +00:00
|
|
|
match stab_since {
|
|
|
|
|
StableSince::Current => {
|
|
|
|
|
self.tcx
|
|
|
|
|
.dcx()
|
|
|
|
|
.emit_err(errors::CannotStabilizeDeprecated { span, item_sp });
|
|
|
|
|
}
|
|
|
|
|
StableSince::Version(stab_since) => {
|
|
|
|
|
if dep_since < stab_since {
|
|
|
|
|
self.tcx
|
|
|
|
|
.dcx()
|
|
|
|
|
.emit_err(errors::CannotStabilizeDeprecated { span, item_sp });
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-16 22:39:04 +00:00
|
|
|
StableSince::Err(_) => {
|
2025-07-12 14:33:04 +00:00
|
|
|
// An error already reported. Assume the unparseable stabilization
|
|
|
|
|
// version is older than the deprecation version.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the current node is a function with const stability attributes (directly given or
|
|
|
|
|
// implied), check if the function/method is const or the parent impl block is const.
|
|
|
|
|
let fn_sig = self.tcx.hir_node_by_def_id(def_id).fn_sig();
|
|
|
|
|
if let Some(fn_sig) = fn_sig
|
|
|
|
|
&& !fn_sig.header.is_const()
|
|
|
|
|
&& const_stab.is_some()
|
|
|
|
|
&& find_attr_span!(ConstStability).is_some()
|
|
|
|
|
{
|
|
|
|
|
self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If this is marked const *stable*, it must also be regular-stable.
|
|
|
|
|
if let Some(const_stab) = const_stab
|
|
|
|
|
&& let Some(fn_sig) = fn_sig
|
|
|
|
|
&& const_stab.is_const_stable()
|
|
|
|
|
&& !stab.is_some_and(|s| s.is_stable())
|
|
|
|
|
&& let Some(const_span) = find_attr_span!(ConstStability)
|
|
|
|
|
{
|
|
|
|
|
self.tcx
|
|
|
|
|
.dcx()
|
|
|
|
|
.emit_err(errors::ConstStableNotStable { fn_sig_span: fn_sig.span, const_span });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(stab) = &const_stab
|
|
|
|
|
&& stab.is_const_stable()
|
|
|
|
|
&& stab.const_stable_indirect
|
|
|
|
|
&& let Some(span) = find_attr_span!(ConstStability)
|
|
|
|
|
{
|
|
|
|
|
self.tcx.dcx().emit_err(errors::RustcConstStableIndirectPairing { span });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-12 10:41:16 +00:00
|
|
|
#[instrument(level = "debug", skip(self))]
|
2022-01-30 15:41:35 +01:00
|
|
|
fn check_missing_stability(&self, def_id: LocalDefId) {
|
2025-07-12 10:15:25 +00:00
|
|
|
let stab = self.tcx.lookup_stability(def_id);
|
2025-07-12 10:41:16 +00:00
|
|
|
self.tcx.ensure_ok().lookup_const_stability(def_id);
|
2023-04-09 21:37:31 +02:00
|
|
|
if !self.tcx.sess.is_test_crate()
|
2022-09-22 16:19:53 +03:00
|
|
|
&& stab.is_none()
|
|
|
|
|
&& self.effective_visibilities.is_reachable(def_id)
|
|
|
|
|
{
|
2023-02-21 14:05:32 -07:00
|
|
|
let descr = self.tcx.def_descr(def_id.to_def_id());
|
2022-01-30 15:41:35 +01:00
|
|
|
let span = self.tcx.def_span(def_id);
|
2023-12-18 22:21:37 +11:00
|
|
|
self.tcx.dcx().emit_err(errors::MissingStabilityAttr { span, descr });
|
2016-11-10 19:08:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-09-25 13:48:48 -07:00
|
|
|
|
2022-01-30 15:41:35 +01:00
|
|
|
fn check_missing_const_stability(&self, def_id: LocalDefId) {
|
2025-01-12 20:52:30 +00:00
|
|
|
let is_const = self.tcx.is_const_fn(def_id.to_def_id())
|
|
|
|
|
|| (self.tcx.def_kind(def_id.to_def_id()) == DefKind::Trait
|
|
|
|
|
&& self.tcx.is_const_trait(def_id.to_def_id()));
|
2021-11-17 21:06:17 -05:00
|
|
|
|
2025-01-12 20:52:30 +00:00
|
|
|
// Reachable const fn/trait must have a stability attribute.
|
2024-11-02 21:19:21 +01:00
|
|
|
if is_const
|
|
|
|
|
&& self.effective_visibilities.is_reachable(def_id)
|
|
|
|
|
&& self.tcx.lookup_const_stability(def_id).is_none()
|
|
|
|
|
{
|
2022-01-30 15:41:35 +01:00
|
|
|
let span = self.tcx.def_span(def_id);
|
2023-02-21 14:05:32 -07:00
|
|
|
let descr = self.tcx.def_descr(def_id.to_def_id());
|
2023-12-18 22:21:37 +11:00
|
|
|
self.tcx.dcx().emit_err(errors::MissingConstStabAttr { span, descr });
|
2020-09-25 13:48:48 -07:00
|
|
|
}
|
|
|
|
|
}
|
2016-11-10 19:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-25 23:41:36 +03:00
|
|
|
impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
|
2021-11-03 18:03:12 -05:00
|
|
|
type NestedFilter = nested_filter::OnlyBodies;
|
2020-01-07 17:25:33 +01:00
|
|
|
|
2025-02-03 14:42:01 +11:00
|
|
|
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
|
|
|
|
|
self.tcx
|
2016-11-28 18:10:37 +01:00
|
|
|
}
|
|
|
|
|
|
2019-11-28 19:28:50 +01:00
|
|
|
fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
|
2022-01-30 15:41:35 +01:00
|
|
|
self.check_compatible_stability(i.owner_id.def_id);
|
2025-07-12 14:33:04 +00:00
|
|
|
|
2020-09-25 13:48:48 -07:00
|
|
|
// 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
|
2022-03-30 15:14:15 -04:00
|
|
|
// and propagate this instability to children, but this annotation is completely
|
2020-09-25 13:48:48 -07:00
|
|
|
// optional. They inherit stability from their parents when unannotated.
|
|
|
|
|
if !matches!(
|
|
|
|
|
i.kind,
|
2021-01-09 12:00:45 -05:00
|
|
|
hir::ItemKind::Impl(hir::Impl { of_trait: None, .. })
|
|
|
|
|
| hir::ItemKind::ForeignMod { .. }
|
2020-09-25 13:48:48 -07:00
|
|
|
) {
|
2022-01-30 15:41:35 +01:00
|
|
|
self.check_missing_stability(i.owner_id.def_id);
|
2020-09-25 13:48:48 -07:00
|
|
|
}
|
2016-11-10 19:08:21 +02:00
|
|
|
|
2021-11-17 21:06:17 -05:00
|
|
|
// Ensure stable `const fn` have a const stability attribute.
|
2022-01-30 15:41:35 +01:00
|
|
|
self.check_missing_const_stability(i.owner_id.def_id);
|
2016-11-10 19:08:21 +02:00
|
|
|
|
|
|
|
|
intravisit::walk_item(self, i)
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-28 21:47:10 +01:00
|
|
|
fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
|
2022-01-30 15:41:35 +01:00
|
|
|
self.check_compatible_stability(ti.owner_id.def_id);
|
|
|
|
|
self.check_missing_stability(ti.owner_id.def_id);
|
2016-11-10 19:08:21 +02:00
|
|
|
intravisit::walk_trait_item(self, ti);
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-28 22:16:44 +01:00
|
|
|
fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
|
2022-01-30 15:41:35 +01:00
|
|
|
self.check_compatible_stability(ii.owner_id.def_id);
|
2025-02-21 07:54:35 +11:00
|
|
|
let impl_def_id = self.tcx.hir_get_parent_item(ii.hir_id());
|
2016-11-10 19:08:21 +02:00
|
|
|
if self.tcx.impl_trait_ref(impl_def_id).is_none() {
|
2022-01-30 15:41:35 +01:00
|
|
|
self.check_missing_stability(ii.owner_id.def_id);
|
|
|
|
|
self.check_missing_const_stability(ii.owner_id.def_id);
|
2016-11-10 19:08:21 +02:00
|
|
|
}
|
|
|
|
|
intravisit::walk_impl_item(self, ii);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-10 11:22:01 +10:00
|
|
|
fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) {
|
2022-01-30 15:41:35 +01:00
|
|
|
self.check_compatible_stability(var.def_id);
|
|
|
|
|
self.check_missing_stability(var.def_id);
|
2022-11-06 19:46:55 +00:00
|
|
|
if let Some(ctor_def_id) = var.data.ctor_def_id() {
|
2022-01-30 15:41:35 +01:00
|
|
|
self.check_missing_stability(ctor_def_id);
|
2022-08-11 22:25:16 +00:00
|
|
|
}
|
2022-08-10 11:22:01 +10:00
|
|
|
intravisit::walk_variant(self, var);
|
2016-11-10 19:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2021-03-16 00:36:07 +03:00
|
|
|
fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) {
|
2022-01-30 15:41:35 +01:00
|
|
|
self.check_compatible_stability(s.def_id);
|
|
|
|
|
self.check_missing_stability(s.def_id);
|
2021-03-16 00:36:07 +03:00
|
|
|
intravisit::walk_field_def(self, s);
|
2016-11-10 19:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2019-11-28 20:18:29 +01:00
|
|
|
fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
|
2022-01-30 15:41:35 +01:00
|
|
|
self.check_compatible_stability(i.owner_id.def_id);
|
|
|
|
|
self.check_missing_stability(i.owner_id.def_id);
|
2016-11-10 19:08:21 +02:00
|
|
|
intravisit::walk_foreign_item(self, i);
|
|
|
|
|
}
|
2025-07-12 14:33:04 +00:00
|
|
|
|
|
|
|
|
fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
|
2022-01-30 15:41:35 +01:00
|
|
|
self.check_compatible_stability(p.def_id);
|
2025-07-12 14:33:04 +00:00
|
|
|
// Note that we don't need to `check_missing_stability` for default generic parameters,
|
|
|
|
|
// as we assume that any default generic parameters without attributes are automatically
|
|
|
|
|
// stable (assuming they have not inherited instability from their parent).
|
|
|
|
|
intravisit::walk_generic_param(self, p);
|
|
|
|
|
}
|
2016-11-10 19:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-12 10:58:33 +00:00
|
|
|
fn stability_implications(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> UnordMap<Symbol, Symbol> {
|
|
|
|
|
let mut annotator = Annotator { tcx, implications: Default::default() };
|
2025-07-12 10:57:57 +00:00
|
|
|
annotator.annotate(CRATE_DEF_ID);
|
|
|
|
|
tcx.hir_walk_toplevel_module(&mut annotator);
|
2025-07-12 10:58:33 +00:00
|
|
|
annotator.implications
|
2014-06-26 11:37:39 -07:00
|
|
|
}
|
Add stability inheritance
This commit makes several changes to the stability index infrastructure:
* Stability levels are now inherited lexically, i.e., each item's
stability level becomes the default for any nested items.
* The computed stability level for an item is stored as part of the
metadata. When using an item from an external crate, this data is
looked up and cached.
* The stability lint works from the computed stability level, rather
than manual stability attribute annotations. However, the lint still
checks only a limited set of item uses (e.g., it does not check every
component of a path on import). This will be addressed in a later PR,
as part of issue #8962.
* The stability lint only applies to items originating from external
crates, since the stability index is intended as a promise to
downstream crates.
* The "experimental" lint is now _allow_ by default. This is because
almost all existing crates have been marked "experimental", pending
library stabilization. With inheritance in place, this would generate
a massive explosion of warnings for every Rust program.
The lint should be changed back to deny-by-default after library
stabilization is complete.
* The "deprecated" lint still warns by default.
The net result: we can begin tracking stability index for the standard
libraries as we stabilize, without impacting most clients.
Closes #13540.
2014-06-11 17:23:11 -07:00
|
|
|
|
2015-01-14 15:20:14 -08:00
|
|
|
/// Cross-references the feature names of unstable APIs with enabled
|
2016-11-10 19:08:21 +02:00
|
|
|
/// features and possibly prints errors.
|
2023-04-26 20:53:51 +02:00
|
|
|
fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
|
2025-02-17 14:17:57 +11:00
|
|
|
tcx.hir_visit_item_likes_in_module(module_def_id, &mut Checker { tcx });
|
2022-01-30 15:41:35 +01:00
|
|
|
|
|
|
|
|
let is_staged_api =
|
|
|
|
|
tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api();
|
|
|
|
|
if is_staged_api {
|
|
|
|
|
let effective_visibilities = &tcx.effective_visibilities(());
|
|
|
|
|
let mut missing = MissingStabilityAnnotations { tcx, effective_visibilities };
|
|
|
|
|
if module_def_id.is_top_level_module() {
|
|
|
|
|
missing.check_missing_stability(CRATE_DEF_ID);
|
|
|
|
|
}
|
|
|
|
|
tcx.hir_visit_item_likes_in_module(module_def_id, &mut missing);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if module_def_id.is_top_level_module() {
|
|
|
|
|
check_unused_or_stable_features(tcx)
|
|
|
|
|
}
|
2018-06-06 22:13:52 +02:00
|
|
|
}
|
|
|
|
|
|
2020-07-05 23:00:14 +03:00
|
|
|
pub(crate) fn provide(providers: &mut Providers) {
|
2022-01-16 22:26:46 +01:00
|
|
|
*providers = Providers {
|
|
|
|
|
check_mod_unstable_api_usage,
|
2025-07-12 10:58:33 +00:00
|
|
|
stability_implications,
|
2025-07-12 10:15:25 +00:00
|
|
|
lookup_stability,
|
2025-07-12 10:41:16 +00:00
|
|
|
lookup_const_stability,
|
2023-01-30 20:47:11 +00:00
|
|
|
lookup_default_body_stability,
|
2022-01-18 21:01:07 +01:00
|
|
|
lookup_deprecation_entry,
|
2022-01-16 22:26:46 +01:00
|
|
|
..*providers
|
|
|
|
|
};
|
2019-06-22 02:44:45 +03:00
|
|
|
}
|
|
|
|
|
|
2019-06-11 22:03:44 +03:00
|
|
|
struct Checker<'tcx> {
|
2019-06-14 00:48:52 +03:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2015-01-14 15:20:14 -08:00
|
|
|
}
|
|
|
|
|
|
2021-12-13 22:16:36 -07:00
|
|
|
impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
|
2021-11-03 18:03:12 -05:00
|
|
|
type NestedFilter = nested_filter::OnlyBodies;
|
2020-01-07 17:25:33 +01:00
|
|
|
|
2016-11-28 14:00:26 -05:00
|
|
|
/// Because stability levels are scoped lexically, we want to walk
|
|
|
|
|
/// nested items in the context of the outer item, so enable
|
|
|
|
|
/// deep-walking.
|
2025-02-03 14:42:01 +11:00
|
|
|
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
|
|
|
|
|
self.tcx
|
2016-10-28 22:58:32 +02:00
|
|
|
}
|
|
|
|
|
|
2019-11-28 19:28:50 +01:00
|
|
|
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
|
2019-09-26 17:51:36 +01:00
|
|
|
match item.kind {
|
Move `hir::Item::ident` into `hir::ItemKind`.
`hir::Item` has an `ident` field.
- It's always non-empty for these item kinds: `ExternCrate`, `Static`,
`Const`, `Fn`, `Macro`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`,
Trait`, TraitAalis`.
- It's always empty for these item kinds: `ForeignMod`, `GlobalAsm`,
`Impl`.
- For `Use`, it is non-empty for `UseKind::Single` and empty for
`UseKind::{Glob,ListStem}`.
All of this is quite non-obvious; the only documentation is a single
comment saying "The name might be a dummy name in case of anonymous
items". Some sites that handle items check for an empty ident, some
don't. This is a very C-like way of doing things, but this is Rust, we
have sum types, we can do this properly and never forget to check for
the exceptional case and never YOLO possibly empty identifiers (or
possibly dummy spans) around and hope that things will work out.
The commit is large but it's mostly obvious plumbing work. Some notable
things.
- A similar transformation makes sense for `ast::Item`, but this is
already a big change. That can be done later.
- Lots of assertions are added to item lowering to ensure that
identifiers are empty/non-empty as expected. These will be removable
when `ast::Item` is done later.
- `ItemKind::Use` doesn't get an `Ident`, but `UseKind::Single` does.
- `lower_use_tree` is significantly simpler. No more confusing `&mut
Ident` to deal with.
- `ItemKind::ident` is a new method, it returns an `Option<Ident>`. It's
used with `unwrap` in a few places; sometimes it's hard to tell
exactly which item kinds might occur. None of these unwraps fail on
the test suite. It's conceivable that some might fail on alternative
input. We can deal with those if/when they happen.
- In `trait_path` the `find_map`/`if let` is replaced with a loop, and
things end up much clearer that way.
- `named_span` no longer checks for an empty name; instead the call site
now checks for a missing identifier if necessary.
- `maybe_inline_local` doesn't need the `glob` argument, it can be
computed in-function from the `renamed` argument.
- `arbitrary_source_item_ordering::check_mod` had a big `if` statement
that was just getting the ident from the item kinds that had one. It
could be mostly replaced by a single call to the new `ItemKind::ident`
method.
- `ItemKind` grows from 56 to 64 bytes, but `Item` stays the same size,
and that's what matters, because `ItemKind` only occurs within `Item`.
2025-03-06 19:07:36 +11:00
|
|
|
hir::ItemKind::ExternCrate(_, ident) => {
|
2016-11-10 19:08:21 +02:00
|
|
|
// compiler-generated `extern crate` items have a dummy span.
|
2020-05-31 18:09:25 -07:00
|
|
|
// `std` is still checked for the `restricted-std` feature.
|
Move `hir::Item::ident` into `hir::ItemKind`.
`hir::Item` has an `ident` field.
- It's always non-empty for these item kinds: `ExternCrate`, `Static`,
`Const`, `Fn`, `Macro`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`,
Trait`, TraitAalis`.
- It's always empty for these item kinds: `ForeignMod`, `GlobalAsm`,
`Impl`.
- For `Use`, it is non-empty for `UseKind::Single` and empty for
`UseKind::{Glob,ListStem}`.
All of this is quite non-obvious; the only documentation is a single
comment saying "The name might be a dummy name in case of anonymous
items". Some sites that handle items check for an empty ident, some
don't. This is a very C-like way of doing things, but this is Rust, we
have sum types, we can do this properly and never forget to check for
the exceptional case and never YOLO possibly empty identifiers (or
possibly dummy spans) around and hope that things will work out.
The commit is large but it's mostly obvious plumbing work. Some notable
things.
- A similar transformation makes sense for `ast::Item`, but this is
already a big change. That can be done later.
- Lots of assertions are added to item lowering to ensure that
identifiers are empty/non-empty as expected. These will be removable
when `ast::Item` is done later.
- `ItemKind::Use` doesn't get an `Ident`, but `UseKind::Single` does.
- `lower_use_tree` is significantly simpler. No more confusing `&mut
Ident` to deal with.
- `ItemKind::ident` is a new method, it returns an `Option<Ident>`. It's
used with `unwrap` in a few places; sometimes it's hard to tell
exactly which item kinds might occur. None of these unwraps fail on
the test suite. It's conceivable that some might fail on alternative
input. We can deal with those if/when they happen.
- In `trait_path` the `find_map`/`if let` is replaced with a loop, and
things end up much clearer that way.
- `named_span` no longer checks for an empty name; instead the call site
now checks for a missing identifier if necessary.
- `maybe_inline_local` doesn't need the `glob` argument, it can be
computed in-function from the `renamed` argument.
- `arbitrary_source_item_ordering::check_mod` had a big `if` statement
that was just getting the ident from the item kinds that had one. It
could be mostly replaced by a single call to the new `ItemKind::ident`
method.
- `ItemKind` grows from 56 to 64 bytes, but `Item` stays the same size,
and that's what matters, because `ItemKind` only occurs within `Item`.
2025-03-06 19:07:36 +11:00
|
|
|
if item.span.is_dummy() && ident.name != sym::std {
|
2018-06-25 01:00:21 +03:00
|
|
|
return;
|
|
|
|
|
}
|
2016-11-10 19:08:21 +02:00
|
|
|
|
2022-10-27 14:02:18 +11:00
|
|
|
let Some(cnum) = self.tcx.extern_mod_stmt_cnum(item.owner_id.def_id) else {
|
2022-02-19 00:48:49 +01:00
|
|
|
return;
|
2016-11-10 19:08:21 +02:00
|
|
|
};
|
2022-04-15 19:27:53 +02:00
|
|
|
let def_id = cnum.as_def_id();
|
2021-05-07 10:41:04 +08:00
|
|
|
self.tcx.check_stability(def_id, Some(item.hir_id()), item.span, None);
|
2015-02-17 13:56:06 -08:00
|
|
|
}
|
|
|
|
|
|
2016-11-10 19:08:21 +02:00
|
|
|
// For implementations of traits, check the stability of each item
|
|
|
|
|
// individually as it's possible to have a stable trait with unstable
|
|
|
|
|
// items.
|
2025-01-12 20:52:30 +00:00
|
|
|
hir::ItemKind::Impl(hir::Impl {
|
2025-02-20 18:28:48 +00:00
|
|
|
of_trait: Some(t), self_ty, items, constness, ..
|
2025-01-12 20:52:30 +00:00
|
|
|
}) => {
|
2022-02-22 14:54:47 -05:00
|
|
|
let features = self.tcx.features();
|
2024-10-09 09:01:57 +02:00
|
|
|
if features.staged_api() {
|
2025-02-21 18:33:05 +11:00
|
|
|
let attrs = self.tcx.hir_attrs(item.hir_id());
|
2025-05-09 23:16:55 +02:00
|
|
|
let stab = attrs::find_attr!(attrs, AttributeKind::Stability{stability, span} => (*stability, *span));
|
2025-02-09 22:49:33 +01:00
|
|
|
|
|
|
|
|
// FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem
|
2025-05-09 23:16:55 +02:00
|
|
|
let const_stab = attrs::find_attr!(attrs, AttributeKind::ConstStability{stability, ..} => *stability);
|
2022-02-22 14:54:47 -05:00
|
|
|
|
2025-07-14 13:36:19 +00:00
|
|
|
let unstable_feature_stab =
|
|
|
|
|
find_attr!(attrs, AttributeKind::UnstableFeatureBound(i) => i)
|
|
|
|
|
.map(|i| i.as_slice())
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
2020-09-10 20:43:13 +02:00
|
|
|
// If this impl block has an #[unstable] attribute, give an
|
|
|
|
|
// error if all involved types and traits are stable, because
|
|
|
|
|
// it will have no effect.
|
|
|
|
|
// See: https://github.com/rust-lang/rust/issues/55436
|
2025-07-14 13:36:19 +00:00
|
|
|
//
|
|
|
|
|
// The exception is when there are both #[unstable_feature_bound(..)] and
|
|
|
|
|
// #![unstable(feature = "..", issue = "..")] that have the same symbol because
|
|
|
|
|
// that can effectively mark an impl as unstable.
|
|
|
|
|
//
|
|
|
|
|
// For example:
|
|
|
|
|
// ```
|
|
|
|
|
// #[unstable_feature_bound(feat_foo)]
|
|
|
|
|
// #[unstable(feature = "feat_foo", issue = "none")]
|
|
|
|
|
// impl Foo for Bar {}
|
|
|
|
|
// ```
|
2024-12-07 15:27:17 +01:00
|
|
|
if let Some((
|
2025-07-14 13:36:19 +00:00
|
|
|
Stability { level: attrs::StabilityLevel::Unstable { .. }, feature },
|
2024-12-07 15:27:17 +01:00
|
|
|
span,
|
|
|
|
|
)) = stab
|
|
|
|
|
{
|
2020-09-10 20:43:13 +02:00
|
|
|
let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true };
|
2025-01-18 22:45:41 +00:00
|
|
|
c.visit_ty_unambig(self_ty);
|
2020-09-10 20:43:13 +02:00
|
|
|
c.visit_trait_ref(t);
|
2023-02-25 08:39:05 +00:00
|
|
|
|
2025-07-14 13:36:19 +00:00
|
|
|
// Skip the lint if the impl is marked as unstable using
|
|
|
|
|
// #[unstable_feature_bound(..)]
|
|
|
|
|
let mut unstable_feature_bound_in_effect = false;
|
|
|
|
|
for (unstable_bound_feat_name, _) in unstable_feature_stab {
|
|
|
|
|
if *unstable_bound_feat_name == feature {
|
|
|
|
|
unstable_feature_bound_in_effect = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-25 08:39:05 +00:00
|
|
|
// do not lint when the trait isn't resolved, since resolution error should
|
|
|
|
|
// be fixed first
|
2025-07-14 13:36:19 +00:00
|
|
|
if t.path.res != Res::Err
|
|
|
|
|
&& c.fully_stable
|
|
|
|
|
&& !unstable_feature_bound_in_effect
|
|
|
|
|
{
|
2024-01-16 16:27:02 +11:00
|
|
|
self.tcx.emit_node_span_lint(
|
2020-09-10 20:43:13 +02:00
|
|
|
INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
|
2021-01-30 17:47:51 +01:00
|
|
|
item.hir_id(),
|
2020-09-10 20:43:13 +02:00
|
|
|
span,
|
2023-04-25 18:42:42 +01:00
|
|
|
errors::IneffectiveUnstableImpl,
|
2020-09-10 20:43:13 +02:00
|
|
|
);
|
|
|
|
|
}
|
2020-09-09 22:16:13 +02:00
|
|
|
}
|
2022-02-22 14:54:47 -05:00
|
|
|
|
2024-10-09 09:01:57 +02:00
|
|
|
if features.const_trait_impl()
|
2025-02-07 21:58:08 +08:00
|
|
|
&& let hir::Constness::Const = constness
|
2022-02-22 14:54:47 -05:00
|
|
|
{
|
2025-02-07 21:58:08 +08:00
|
|
|
let stable_or_implied_stable = match const_stab {
|
|
|
|
|
None => true,
|
|
|
|
|
Some(stab) if stab.is_const_stable() => {
|
|
|
|
|
// `#![feature(const_trait_impl)]` is unstable, so any impl declared stable
|
|
|
|
|
// needs to have an error emitted.
|
|
|
|
|
// Note: Remove this error once `const_trait_impl` is stabilized
|
|
|
|
|
self.tcx
|
|
|
|
|
.dcx()
|
|
|
|
|
.emit_err(errors::TraitImplConstStable { span: item.span });
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
Some(_) => false,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if let Some(trait_id) = t.trait_def_id()
|
|
|
|
|
&& let Some(const_stab) = self.tcx.lookup_const_stability(trait_id)
|
|
|
|
|
{
|
|
|
|
|
// the const stability of a trait impl must match the const stability on the trait.
|
|
|
|
|
if const_stab.is_const_stable() != stable_or_implied_stable {
|
|
|
|
|
let trait_span = self.tcx.def_ident_span(trait_id).unwrap();
|
|
|
|
|
|
|
|
|
|
let impl_stability = if stable_or_implied_stable {
|
|
|
|
|
errors::ImplConstStability::Stable { span: item.span }
|
|
|
|
|
} else {
|
|
|
|
|
errors::ImplConstStability::Unstable { span: item.span }
|
|
|
|
|
};
|
|
|
|
|
let trait_stability = if const_stab.is_const_stable() {
|
|
|
|
|
errors::TraitConstStability::Stable { span: trait_span }
|
|
|
|
|
} else {
|
|
|
|
|
errors::TraitConstStability::Unstable { span: trait_span }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
self.tcx.dcx().emit_err(errors::TraitImplConstStabilityMismatch {
|
|
|
|
|
span: item.span,
|
|
|
|
|
impl_stability,
|
|
|
|
|
trait_stability,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-02-22 14:54:47 -05:00
|
|
|
}
|
2020-09-09 22:16:13 +02:00
|
|
|
}
|
|
|
|
|
|
2025-02-07 21:58:08 +08:00
|
|
|
if let hir::Constness::Const = constness
|
|
|
|
|
&& let Some(def_id) = t.trait_def_id()
|
|
|
|
|
{
|
|
|
|
|
// FIXME(const_trait_impl): Improve the span here.
|
|
|
|
|
self.tcx.check_const_stability(def_id, t.path.span, t.path.span);
|
2025-01-12 20:52:30 +00:00
|
|
|
}
|
|
|
|
|
|
2022-02-05 15:26:49 +01:00
|
|
|
for impl_item_ref in *items {
|
2025-07-02 20:15:28 +00:00
|
|
|
let impl_item = self.tcx.associated_item(impl_item_ref.owner_id);
|
2021-11-18 21:35:42 +00:00
|
|
|
|
|
|
|
|
if let Some(def_id) = impl_item.trait_item_def_id {
|
|
|
|
|
// Pass `None` to skip deprecation warnings.
|
2025-07-02 20:15:28 +00:00
|
|
|
self.tcx.check_stability(
|
|
|
|
|
def_id,
|
|
|
|
|
None,
|
|
|
|
|
self.tcx.def_span(impl_item_ref.owner_id),
|
|
|
|
|
None,
|
|
|
|
|
);
|
2016-09-06 01:26:02 +03:00
|
|
|
}
|
2016-11-10 19:08:21 +02:00
|
|
|
}
|
2015-02-25 22:34:21 +11:00
|
|
|
}
|
2015-01-14 15:20:14 -08:00
|
|
|
|
2016-11-10 19:08:21 +02:00
|
|
|
_ => (/* pass */),
|
2015-02-25 22:34:21 +11:00
|
|
|
}
|
2016-11-10 19:08:21 +02:00
|
|
|
intravisit::walk_item(self, item);
|
2015-02-25 22:34:21 +11:00
|
|
|
}
|
2016-10-27 05:17:42 +03:00
|
|
|
|
2025-01-12 20:52:30 +00:00
|
|
|
fn visit_poly_trait_ref(&mut self, t: &'tcx hir::PolyTraitRef<'tcx>) {
|
|
|
|
|
match t.modifiers.constness {
|
|
|
|
|
hir::BoundConstness::Always(span) | hir::BoundConstness::Maybe(span) => {
|
|
|
|
|
if let Some(def_id) = t.trait_ref.trait_def_id() {
|
|
|
|
|
self.tcx.check_const_stability(def_id, t.trait_ref.path.span, span);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
hir::BoundConstness::Never => {}
|
|
|
|
|
}
|
|
|
|
|
intravisit::walk_poly_trait_ref(self, t);
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-25 11:26:36 +03:00
|
|
|
fn visit_path(&mut self, path: &hir::Path<'tcx>, id: hir::HirId) {
|
2019-04-20 19:36:05 +03:00
|
|
|
if let Some(def_id) = path.res.opt_def_id() {
|
2021-06-15 19:35:03 +08:00
|
|
|
let method_span = path.segments.last().map(|s| s.ident.span);
|
2022-04-11 18:12:26 -07:00
|
|
|
let item_is_allowed = self.tcx.check_stability_allow_unstable(
|
2022-06-04 17:05:33 -05:00
|
|
|
def_id,
|
|
|
|
|
Some(id),
|
|
|
|
|
path.span,
|
|
|
|
|
method_span,
|
|
|
|
|
if is_unstable_reexport(self.tcx, id) {
|
|
|
|
|
AllowUnstable::Yes
|
|
|
|
|
} else {
|
|
|
|
|
AllowUnstable::No
|
|
|
|
|
},
|
2022-04-11 18:12:26 -07:00
|
|
|
);
|
|
|
|
|
|
2025-01-01 19:09:01 +01:00
|
|
|
if item_is_allowed {
|
|
|
|
|
// The item itself is allowed; check whether the path there is also allowed.
|
2025-02-02 12:33:40 +01:00
|
|
|
let is_allowed_through_unstable_modules: Option<Symbol> =
|
2025-01-01 19:09:01 +01:00
|
|
|
self.tcx.lookup_stability(def_id).and_then(|stab| match stab.level {
|
|
|
|
|
StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
|
|
|
|
|
allowed_through_unstable_modules
|
|
|
|
|
}
|
|
|
|
|
_ => None,
|
|
|
|
|
});
|
|
|
|
|
|
2025-02-02 12:33:40 +01:00
|
|
|
// Check parent modules stability as well if the item the path refers to is itself
|
|
|
|
|
// stable. We only emit errors for unstable path segments if the item is stable
|
|
|
|
|
// or allowed because stability is often inherited, so the most common case is that
|
|
|
|
|
// both the segments and the item are unstable behind the same feature flag.
|
|
|
|
|
//
|
|
|
|
|
// We check here rather than in `visit_path_segment` to prevent visiting the last
|
|
|
|
|
// path segment twice
|
|
|
|
|
//
|
|
|
|
|
// We include special cases via #[rustc_allowed_through_unstable_modules] for items
|
|
|
|
|
// that were accidentally stabilized through unstable paths before this check was
|
|
|
|
|
// added, such as `core::intrinsics::transmute`
|
|
|
|
|
let parents = path.segments.iter().rev().skip(1);
|
|
|
|
|
for path_segment in parents {
|
|
|
|
|
if let Some(def_id) = path_segment.res.opt_def_id() {
|
|
|
|
|
match is_allowed_through_unstable_modules {
|
|
|
|
|
None => {
|
|
|
|
|
// Emit a hard stability error if this path is not stable.
|
|
|
|
|
|
|
|
|
|
// use `None` for id to prevent deprecation check
|
|
|
|
|
self.tcx.check_stability_allow_unstable(
|
|
|
|
|
def_id,
|
|
|
|
|
None,
|
|
|
|
|
path.span,
|
|
|
|
|
None,
|
|
|
|
|
if is_unstable_reexport(self.tcx, id) {
|
|
|
|
|
AllowUnstable::Yes
|
|
|
|
|
} else {
|
|
|
|
|
AllowUnstable::No
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
Some(deprecation) => {
|
|
|
|
|
// Call the stability check directly so that we can control which
|
|
|
|
|
// diagnostic is emitted.
|
|
|
|
|
let eval_result = self.tcx.eval_stability_allow_unstable(
|
|
|
|
|
def_id,
|
|
|
|
|
None,
|
|
|
|
|
path.span,
|
|
|
|
|
None,
|
|
|
|
|
if is_unstable_reexport(self.tcx, id) {
|
|
|
|
|
AllowUnstable::Yes
|
|
|
|
|
} else {
|
|
|
|
|
AllowUnstable::No
|
|
|
|
|
},
|
2025-01-01 19:09:01 +01:00
|
|
|
);
|
2025-02-02 12:33:40 +01:00
|
|
|
let is_allowed = matches!(eval_result, EvalResult::Allow);
|
|
|
|
|
if !is_allowed {
|
|
|
|
|
// Calculating message for lint involves calling `self.def_path_str`,
|
|
|
|
|
// which will by default invoke the expensive `visible_parent_map` query.
|
|
|
|
|
// Skip all that work if the lint is allowed anyway.
|
2025-03-19 09:41:38 +00:00
|
|
|
if self.tcx.lint_level_at_node(DEPRECATED, id).level
|
2025-02-02 12:33:40 +01:00
|
|
|
== lint::Level::Allow
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Show a deprecation message.
|
|
|
|
|
let def_path =
|
|
|
|
|
with_no_trimmed_paths!(self.tcx.def_path_str(def_id));
|
|
|
|
|
let def_kind = self.tcx.def_descr(def_id);
|
|
|
|
|
let diag = Deprecated {
|
|
|
|
|
sub: None,
|
|
|
|
|
kind: def_kind.to_owned(),
|
|
|
|
|
path: def_path,
|
|
|
|
|
note: Some(deprecation),
|
|
|
|
|
since_kind: lint::DeprecatedSinceKind::InEffect,
|
|
|
|
|
};
|
|
|
|
|
self.tcx.emit_node_span_lint(
|
|
|
|
|
DEPRECATED,
|
|
|
|
|
id,
|
|
|
|
|
method_span.unwrap_or(path.span),
|
|
|
|
|
diag,
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-01-01 19:09:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-04-11 18:12:26 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-10 19:08:21 +02:00
|
|
|
}
|
2022-04-11 18:12:26 -07:00
|
|
|
|
2016-11-10 19:08:21 +02:00
|
|
|
intravisit::walk_path(self, path)
|
2016-10-27 05:17:42 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-04 17:05:33 -05:00
|
|
|
/// Check whether a path is a `use` item that has been marked as unstable.
|
|
|
|
|
///
|
|
|
|
|
/// See issue #94972 for details on why this is a special case
|
2022-12-20 22:10:40 +01:00
|
|
|
fn is_unstable_reexport(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
|
2022-06-04 17:05:33 -05:00
|
|
|
// Get the LocalDefId so we can lookup the item to check the kind.
|
2022-11-05 15:33:58 +00:00
|
|
|
let Some(owner) = id.as_owner() else {
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
let def_id = owner.def_id;
|
2022-06-04 17:05:33 -05:00
|
|
|
|
2025-07-12 10:15:25 +00:00
|
|
|
let Some(stab) = tcx.lookup_stability(def_id) else {
|
2022-06-04 17:05:33 -05:00
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if stab.level.is_stable() {
|
|
|
|
|
// The re-export is not marked as unstable, don't override
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If this is a path that isn't a use, we don't need to do anything special
|
2025-02-21 18:33:05 +11:00
|
|
|
if !matches!(tcx.hir_expect_item(def_id).kind, ItemKind::Use(..)) {
|
2022-06-04 17:05:33 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 22:16:13 +02:00
|
|
|
struct CheckTraitImplStable<'tcx> {
|
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
|
fully_stable: bool,
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-13 22:16:36 -07:00
|
|
|
impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> {
|
2022-11-25 11:26:36 +03:00
|
|
|
fn visit_path(&mut self, path: &hir::Path<'tcx>, _id: hir::HirId) {
|
2020-09-09 22:16:13 +02:00
|
|
|
if let Some(def_id) = path.res.opt_def_id() {
|
|
|
|
|
if let Some(stab) = self.tcx.lookup_stability(def_id) {
|
|
|
|
|
self.fully_stable &= stab.level.is_stable();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
intravisit::walk_path(self, path)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn visit_trait_ref(&mut self, t: &'tcx TraitRef<'tcx>) {
|
|
|
|
|
if let Res::Def(DefKind::Trait, trait_did) = t.path.res {
|
|
|
|
|
if let Some(stab) = self.tcx.lookup_stability(trait_did) {
|
|
|
|
|
self.fully_stable &= stab.level.is_stable();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
intravisit::walk_trait_ref(self, t)
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-11 19:12:36 +00:00
|
|
|
fn visit_ty(&mut self, t: &'tcx Ty<'tcx, AmbigArg>) {
|
2022-10-19 13:33:45 +02:00
|
|
|
if let TyKind::Never = t.kind {
|
|
|
|
|
self.fully_stable = false;
|
|
|
|
|
}
|
2025-07-06 12:59:42 -07:00
|
|
|
if let TyKind::FnPtr(function) = t.kind {
|
2025-02-05 11:18:18 -08:00
|
|
|
if extern_abi_stability(function.abi).is_err() {
|
2022-10-19 13:33:45 +02:00
|
|
|
self.fully_stable = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
intravisit::walk_ty(self, t)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) {
|
|
|
|
|
for ty in fd.inputs {
|
2025-01-18 22:45:41 +00:00
|
|
|
self.visit_ty_unambig(ty)
|
2022-10-19 13:33:45 +02:00
|
|
|
}
|
|
|
|
|
if let hir::FnRetTy::Return(output_ty) = fd.output {
|
|
|
|
|
match output_ty.kind {
|
|
|
|
|
TyKind::Never => {} // `-> !` is stable
|
2025-01-18 22:45:41 +00:00
|
|
|
_ => self.visit_ty_unambig(output_ty),
|
2022-10-19 12:41:35 +02:00
|
|
|
}
|
2020-09-09 22:16:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-27 02:59:49 +00:00
|
|
|
/// Given the list of enabled features that were not language features (i.e., that
|
2015-01-14 15:20:14 -08:00
|
|
|
/// 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.
|
2022-01-30 15:41:35 +01:00
|
|
|
// This is `pub` for rustdoc. rustc should call it through `check_mod_unstable_api_usage`.
|
2019-06-21 23:49:03 +02:00
|
|
|
pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
|
2022-01-30 15:41:35 +01:00
|
|
|
let _prof_timer = tcx.sess.timer("unused_lib_feature_checking");
|
2016-11-10 19:08:21 +02:00
|
|
|
|
2024-10-09 08:30:43 +02:00
|
|
|
let enabled_lang_features = tcx.features().enabled_lang_features();
|
2023-12-21 10:52:27 +01:00
|
|
|
let mut lang_features = UnordSet::default();
|
2024-10-24 23:53:08 +08:00
|
|
|
for EnabledLangFeature { gate_name, attr_sp, stable_since } in enabled_lang_features {
|
|
|
|
|
if let Some(version) = stable_since {
|
2018-07-23 02:03:01 +01:00
|
|
|
// Warn if the user has enabled an already-stable lang feature.
|
2024-10-24 23:53:08 +08:00
|
|
|
unnecessary_stable_feature_lint(tcx, *attr_sp, *gate_name, *version);
|
2018-07-23 02:03:01 +01:00
|
|
|
}
|
2024-10-24 23:53:08 +08:00
|
|
|
if !lang_features.insert(gate_name) {
|
2018-07-23 02:03:01 +01:00
|
|
|
// Warn if the user enables a lang feature multiple times.
|
2024-10-24 23:53:08 +08:00
|
|
|
tcx.dcx().emit_err(errors::DuplicateFeatureErr { span: *attr_sp, feature: *gate_name });
|
2018-07-23 02:03:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
2018-07-23 01:20:33 +01:00
|
|
|
|
2024-10-09 08:30:43 +02:00
|
|
|
let enabled_lib_features = tcx.features().enabled_lib_features();
|
2022-02-21 18:59:24 +01:00
|
|
|
let mut remaining_lib_features = FxIndexMap::default();
|
2024-10-24 23:53:08 +08:00
|
|
|
for EnabledLibFeature { gate_name, attr_sp } in enabled_lib_features {
|
|
|
|
|
if remaining_lib_features.contains_key(gate_name) {
|
2018-07-23 17:34:04 +01:00
|
|
|
// Warn if the user enables a lib feature multiple times.
|
2024-10-24 23:53:08 +08:00
|
|
|
tcx.dcx().emit_err(errors::DuplicateFeatureErr { span: *attr_sp, feature: *gate_name });
|
2018-07-23 01:22:01 +01:00
|
|
|
}
|
2024-10-24 23:53:08 +08:00
|
|
|
remaining_lib_features.insert(*gate_name, *attr_sp);
|
2018-07-23 01:20:33 +01:00
|
|
|
}
|
2018-07-24 00:56:00 +01:00
|
|
|
// `stdbuild` has special handling for `libc`, so we need to
|
|
|
|
|
// recognise the feature when building std.
|
2018-08-06 16:46:08 +01:00
|
|
|
// Likewise, libtest is handled specially, so `test` isn't
|
|
|
|
|
// available as we'd like it to be.
|
2024-10-08 14:06:56 +02:00
|
|
|
// FIXME: only remove `libc` when `stdbuild` is enabled.
|
2018-08-06 16:46:08 +01:00
|
|
|
// FIXME: remove special casing for `test`.
|
2024-01-28 20:53:28 +00:00
|
|
|
// FIXME(#120456) - is `swap_remove` correct?
|
|
|
|
|
remaining_lib_features.swap_remove(&sym::libc);
|
|
|
|
|
remaining_lib_features.swap_remove(&sym::test);
|
2018-07-23 01:20:33 +01:00
|
|
|
|
2022-08-02 17:27:18 +01:00
|
|
|
/// For each feature in `defined_features`..
|
|
|
|
|
///
|
|
|
|
|
/// - If it is in `remaining_lib_features` (those features with `#![feature(..)]` attributes in
|
|
|
|
|
/// the current crate), check if it is stable (or partially stable) and thus an unnecessary
|
|
|
|
|
/// attribute.
|
|
|
|
|
/// - If it is in `remaining_implications` (a feature that is referenced by an `implied_by`
|
|
|
|
|
/// from the current crate), then remove it from the remaining implications.
|
|
|
|
|
///
|
|
|
|
|
/// Once this function has been invoked for every feature (local crate and all extern crates),
|
|
|
|
|
/// then..
|
|
|
|
|
///
|
|
|
|
|
/// - If features remain in `remaining_lib_features`, then the user has enabled a feature that
|
|
|
|
|
/// does not exist.
|
|
|
|
|
/// - If features remain in `remaining_implications`, the `implied_by` refers to a feature that
|
|
|
|
|
/// does not exist.
|
|
|
|
|
///
|
|
|
|
|
/// By structuring the code in this way: checking the features defined from each crate one at a
|
|
|
|
|
/// time, less loading from metadata is performed and thus compiler performance is improved.
|
|
|
|
|
fn check_features<'tcx>(
|
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2024-10-24 23:53:08 +08:00
|
|
|
remaining_lib_features: &mut FxIndexMap<Symbol, Span>,
|
2023-12-21 10:52:27 +01:00
|
|
|
remaining_implications: &mut UnordMap<Symbol, Symbol>,
|
2023-09-23 05:38:49 +00:00
|
|
|
defined_features: &LibFeatures,
|
2023-12-21 10:52:27 +01:00
|
|
|
all_implications: &UnordMap<Symbol, Symbol>,
|
2022-08-02 17:27:18 +01:00
|
|
|
) {
|
2025-06-07 18:57:33 +08:00
|
|
|
for (feature, stability) in defined_features.to_sorted_vec() {
|
|
|
|
|
if let FeatureStability::AcceptedSince(since) = stability
|
2022-07-13 15:10:19 +01:00
|
|
|
&& let Some(span) = remaining_lib_features.get(&feature)
|
|
|
|
|
{
|
|
|
|
|
// Warn if the user has enabled an already-stable lib feature.
|
2022-08-02 17:27:18 +01:00
|
|
|
if let Some(implies) = all_implications.get(&feature) {
|
2023-09-23 05:38:49 +00:00
|
|
|
unnecessary_partially_stable_feature_lint(tcx, *span, feature, *implies, since);
|
2022-07-13 15:10:19 +01:00
|
|
|
} else {
|
2023-09-23 05:38:49 +00:00
|
|
|
unnecessary_stable_feature_lint(tcx, *span, feature, since);
|
2018-08-21 01:39:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-01-28 20:53:28 +00:00
|
|
|
// FIXME(#120456) - is `swap_remove` correct?
|
|
|
|
|
remaining_lib_features.swap_remove(&feature);
|
2022-08-02 17:27:18 +01:00
|
|
|
|
|
|
|
|
// `feature` is the feature doing the implying, but `implied_by` is the feature with
|
|
|
|
|
// the attribute that establishes this relationship. `implied_by` is guaranteed to be a
|
|
|
|
|
// feature defined in the local crate because `remaining_implications` is only the
|
|
|
|
|
// implications from this crate.
|
2023-09-23 05:38:49 +00:00
|
|
|
remaining_implications.remove(&feature);
|
2022-08-02 17:27:18 +01:00
|
|
|
|
2025-06-07 18:57:33 +08:00
|
|
|
if let FeatureStability::Unstable { old_name: Some(alias) } = stability {
|
|
|
|
|
if let Some(span) = remaining_lib_features.swap_remove(&alias) {
|
|
|
|
|
tcx.dcx().emit_err(errors::RenamedFeature { span, feature, alias });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-02 17:27:18 +01:00
|
|
|
if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
|
2019-12-22 17:42:04 -05:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-07-23 01:20:33 +01:00
|
|
|
}
|
|
|
|
|
|
2022-08-02 17:27:18 +01:00
|
|
|
// All local crate implications need to have the feature that implies it confirmed to exist.
|
2023-11-17 23:40:04 +00:00
|
|
|
let mut remaining_implications = tcx.stability_implications(LOCAL_CRATE).clone();
|
2022-08-02 17:27:18 +01:00
|
|
|
|
2024-10-08 14:06:56 +02:00
|
|
|
// We always collect the lib features enabled in the current crate, even if there are
|
2022-08-02 17:27:18 +01:00
|
|
|
// no unknown features, because the collection also does feature attribute validation.
|
2023-11-17 23:40:04 +00:00
|
|
|
let local_defined_features = tcx.lib_features(LOCAL_CRATE);
|
2022-08-02 17:27:18 +01:00
|
|
|
if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() {
|
|
|
|
|
// Loading the implications of all crates is unavoidable to be able to emit the partial
|
|
|
|
|
// stabilization diagnostic, but it can be avoided when there are no
|
|
|
|
|
// `remaining_lib_features`.
|
|
|
|
|
let mut all_implications = remaining_implications.clone();
|
2024-06-06 09:45:50 +00:00
|
|
|
for &cnum in tcx.crates(()) {
|
2023-12-21 10:52:27 +01:00
|
|
|
all_implications
|
|
|
|
|
.extend_unord(tcx.stability_implications(cnum).items().map(|(k, v)| (*k, *v)));
|
2022-08-02 17:27:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
check_features(
|
|
|
|
|
tcx,
|
|
|
|
|
&mut remaining_lib_features,
|
|
|
|
|
&mut remaining_implications,
|
2023-09-23 05:38:49 +00:00
|
|
|
local_defined_features,
|
2022-08-02 17:27:18 +01:00
|
|
|
&all_implications,
|
|
|
|
|
);
|
|
|
|
|
|
2024-06-06 09:45:50 +00:00
|
|
|
for &cnum in tcx.crates(()) {
|
2022-08-02 17:27:18 +01:00
|
|
|
if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
check_features(
|
|
|
|
|
tcx,
|
|
|
|
|
&mut remaining_lib_features,
|
|
|
|
|
&mut remaining_implications,
|
2023-09-23 05:38:49 +00:00
|
|
|
tcx.lib_features(cnum),
|
2022-08-02 17:27:18 +01:00
|
|
|
&all_implications,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-23 01:20:33 +01:00
|
|
|
for (feature, span) in remaining_lib_features {
|
2024-10-24 23:53:08 +08:00
|
|
|
tcx.dcx().emit_err(errors::UnknownFeature { span, feature });
|
2018-07-23 01:20:33 +01:00
|
|
|
}
|
|
|
|
|
|
2023-12-21 10:52:27 +01:00
|
|
|
for (&implied_by, &feature) in remaining_implications.to_sorted_stable_ord() {
|
2023-11-17 23:40:04 +00:00
|
|
|
let local_defined_features = tcx.lib_features(LOCAL_CRATE);
|
|
|
|
|
let span = local_defined_features
|
|
|
|
|
.stability
|
2022-08-02 17:27:18 +01:00
|
|
|
.get(&feature)
|
2023-11-17 23:40:04 +00:00
|
|
|
.expect("feature that implied another does not exist")
|
|
|
|
|
.1;
|
2023-12-18 22:21:37 +11:00
|
|
|
tcx.dcx().emit_err(errors::ImpliedFeatureNotExist { span, feature, implied_by });
|
2022-08-02 17:27:18 +01:00
|
|
|
}
|
|
|
|
|
|
2018-07-23 01:20:33 +01:00
|
|
|
// FIXME(#44232): the `used_features` table no longer exists, so we
|
2020-03-06 12:13:55 +01:00
|
|
|
// don't lint about unused features. We should re-enable this one day!
|
2017-08-31 15:08:34 -07:00
|
|
|
}
|
2015-01-16 10:25:16 -08:00
|
|
|
|
2022-07-13 15:10:19 +01:00
|
|
|
fn unnecessary_partially_stable_feature_lint(
|
|
|
|
|
tcx: TyCtxt<'_>,
|
|
|
|
|
span: Span,
|
|
|
|
|
feature: Symbol,
|
|
|
|
|
implies: Symbol,
|
|
|
|
|
since: Symbol,
|
|
|
|
|
) {
|
2024-01-16 16:27:02 +11:00
|
|
|
tcx.emit_node_span_lint(
|
2022-09-16 11:01:02 +04:00
|
|
|
lint::builtin::STABLE_FEATURES,
|
|
|
|
|
hir::CRATE_HIR_ID,
|
|
|
|
|
span,
|
2023-04-25 18:42:42 +01:00
|
|
|
errors::UnnecessaryPartialStableFeature {
|
|
|
|
|
span,
|
|
|
|
|
line: tcx.sess.source_map().span_extend_to_line(span),
|
|
|
|
|
feature,
|
|
|
|
|
since,
|
|
|
|
|
implies,
|
2022-09-16 11:01:02 +04:00
|
|
|
},
|
|
|
|
|
);
|
2022-07-13 15:10:19 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-13 19:46:08 +02:00
|
|
|
fn unnecessary_stable_feature_lint(
|
|
|
|
|
tcx: TyCtxt<'_>,
|
|
|
|
|
span: Span,
|
|
|
|
|
feature: Symbol,
|
|
|
|
|
mut since: Symbol,
|
|
|
|
|
) {
|
|
|
|
|
if since.as_str() == VERSION_PLACEHOLDER {
|
2023-10-25 14:05:42 -07:00
|
|
|
since = sym::env_CFG_RELEASE;
|
2022-09-13 19:46:08 +02:00
|
|
|
}
|
2024-01-16 16:27:02 +11:00
|
|
|
tcx.emit_node_span_lint(
|
2023-04-25 18:42:42 +01:00
|
|
|
lint::builtin::STABLE_FEATURES,
|
|
|
|
|
hir::CRATE_HIR_ID,
|
|
|
|
|
span,
|
|
|
|
|
errors::UnnecessaryStableFeature { feature, since },
|
|
|
|
|
);
|
2018-07-23 17:34:04 +01:00
|
|
|
}
|