1//! A pass that annotates every item and method with its stability level,2//! propagating default levels lexically from parent to children ast nodes.34use std::num::NonZero;56use rustc_ast_lowering::stability::extern_abi_stability;7use rustc_attr_ir::{8 AttributeKind, ConstStability, DefaultBodyStability, DeprecatedSince, Stability,9 StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER, find_attr,10};11use rustc_data_structures::fx::FxIndexMap;12use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};13use rustc_feature::{EnabledLangFeature, EnabledLibFeature, UNSTABLE_LANG_FEATURES};14use rustc_hir::def::{DefKind, Res};15use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModId};16use rustc_hir::intravisit::{self, Visitor};17use rustc_hir::{18 self as hir, AmbigArg, Constness, FieldDef, HirId, Item, ItemKind, Path, TraitRef, Ty, TyKind,19 UsePath, Variant,20};21use rustc_lint_defs::builtin::{22 DUPLICATE_FEATURES, INEFFECTIVE_UNSTABLE_REEXPORTS, INEFFECTIVE_UNSTABLE_TRAIT_IMPL,23 STABLE_FEATURES, UNSTABLE_IMPORTS,24};25use rustc_middle::hir::nested_filter;26use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures};27use rustc_middle::middle::privacy::EffectiveVisibilities;28use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, EvalResult};29use rustc_middle::query::{LocalCrate, Providers};30use rustc_middle::ty::{AssocContainer, TyCtxt};31use rustc_span::{Span, Symbol, span_bug, sym};32use tracing::instrument;3334use crate::diagnostics;3536#[derive(PartialEq)]37enum AnnotationKind {38 /// Annotation is required if not inherited from unstable parents.39 Required,40 /// Annotation is useless, reject it.41 Prohibited,42 /// Deprecation annotation is useless, reject it. (Stability attribute is still required.)43 DeprecationProhibited,44 /// Annotation itself is useless, but it can be propagated to children.45 Container,46}4748fn inherit_deprecation(def_kind: DefKind) -> bool {49 match def_kind {50 DefKind::LifetimeParam | DefKind::TyParam | DefKind::ConstParam => false,51 _ => true,52 }53}5455fn inherit_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {56 let def_kind = tcx.def_kind(def_id);57 match def_kind {58 DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {59 match tcx.def_kind(tcx.local_parent(def_id)) {60 DefKind::Trait | DefKind::Impl { .. } => true,61 _ => false,62 }63 }64 DefKind::Closure => true,65 _ => false,66 }67}6869fn annotation_kind(tcx: TyCtxt<'_>, def_id: LocalDefId) -> AnnotationKind {70 let def_kind = tcx.def_kind(def_id);71 match def_kind {72 // Inherent impls and foreign modules serve only as containers for other items,73 // they don't have their own stability. They still can be annotated as unstable74 // and propagate this unstability to children, but this annotation is completely75 // optional. They inherit stability from their parents when unannotated.76 DefKind::Impl { of_trait: false } | DefKind::ForeignMod => AnnotationKind::Container,77 DefKind::Impl { of_trait: true } => AnnotationKind::DeprecationProhibited,7879 // Allow stability attributes on default generic arguments.80 DefKind::TyParam | DefKind::ConstParam => {81 match &tcx.hir_node_by_def_id(def_id).expect_generic_param().kind {82 hir::GenericParamKind::Type { default: Some(_), .. }83 | hir::GenericParamKind::Const { default: Some(_), .. } => {84 AnnotationKind::Container85 }86 _ => AnnotationKind::Prohibited,87 }88 }8990 // Impl items in trait impls cannot have stability.91 DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst => {92 match tcx.def_kind(tcx.local_parent(def_id)) {93 DefKind::Impl { of_trait: true } => AnnotationKind::Prohibited,94 _ => AnnotationKind::Required,95 }96 }9798 _ => AnnotationKind::Required,99 }100}101102fn lookup_deprecation_entry(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DeprecationEntry> {103 let depr = find_attr!(tcx, def_id,104 Deprecated { deprecation, span: _ } => *deprecation105 );106107 let Some(depr) = depr else {108 if inherit_deprecation(tcx.def_kind(def_id)) {109 let parent_id = tcx.opt_local_parent(def_id)?;110 let parent_depr = tcx.lookup_deprecation_entry(parent_id)?;111 return Some(parent_depr);112 }113114 return None;115 };116117 // `Deprecation` is just two pointers, no need to intern it118 Some(DeprecationEntry::local(depr, def_id))119}120121fn inherit_stability(def_kind: DefKind) -> bool {122 match def_kind {123 DefKind::Field | DefKind::Variant | DefKind::Ctor(..) => true,124 _ => false,125 }126}127128/// If the `-Z force-unstable-if-unmarked` flag is passed then we provide129/// a parent stability annotation which indicates that this is private130/// with the `rustc_private` feature. This is intended for use when131/// compiling library and `rustc_*` crates themselves so we can leverage crates.io132/// while maintaining the invariant that all sysroot crates are unstable133/// by default and are unable to be used.134const FORCE_UNSTABLE: Stability = Stability {135 level: StabilityLevel::Unstable {136 reason: UnstableReason::Default,137 issue: NonZero::new(27812),138 implied_by: None,139 old_name: None,140 },141 feature: sym::rustc_private,142};143144#[instrument(level = "debug", skip(tcx))]145fn lookup_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<Stability> {146 // Propagate unstability. This can happen even for non-staged-api crates in case147 // -Zforce-unstable-if-unmarked is set.148 if !tcx.features().staged_api() {149 if !tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {150 return None;151 }152153 let Some(parent) = tcx.opt_local_parent(def_id) else { return Some(FORCE_UNSTABLE) };154155 if inherit_deprecation(tcx.def_kind(def_id)) {156 let parent = tcx.lookup_stability(parent)?;157 if parent.is_unstable() {158 return Some(parent);159 }160 }161162 return None;163 }164165 // # Regular stability166 let stab = find_attr!(tcx, def_id, Stability { stability, span: _ } => *stability);167168 if let Some(stab) = stab {169 return Some(stab);170 }171172 if inherit_deprecation(tcx.def_kind(def_id)) {173 let Some(parent) = tcx.opt_local_parent(def_id) else {174 return tcx175 .sess176 .opts177 .unstable_opts178 .force_unstable_if_unmarked179 .then_some(FORCE_UNSTABLE);180 };181 let parent = tcx.lookup_stability(parent)?;182 if parent.is_unstable() || inherit_stability(tcx.def_kind(def_id)) {183 return Some(parent);184 }185 }186187 None188}189190#[instrument(level = "debug", skip(tcx))]191fn lookup_default_body_stability(192 tcx: TyCtxt<'_>,193 def_id: LocalDefId,194) -> Option<DefaultBodyStability> {195 if !tcx.features().staged_api() {196 return None;197 }198199 // FIXME: check that this item can have body stability200 find_attr!(tcx, def_id, RustcBodyStability { stability, .. } => *stability)201}202203#[instrument(level = "debug", skip(tcx))]204fn lookup_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ConstStability> {205 if !tcx.features().staged_api() {206 // Propagate unstability. This can happen even for non-staged-api crates in case207 // -Zforce-unstable-if-unmarked is set.208 if inherit_deprecation(tcx.def_kind(def_id)) {209 let parent = tcx.opt_local_parent(def_id)?;210 let parent_stab = tcx.lookup_stability(parent)?;211 if parent_stab.is_unstable()212 && let Some(fn_sig) = tcx.hir_node_by_def_id(def_id).fn_sig()213 && matches!(fn_sig.header.constness, Constness::Const { .. })214 {215 let const_stable_indirect = find_attr!(tcx, def_id, RustcConstStableIndirect);216 return Some(ConstStability::unmarked(const_stable_indirect, parent_stab));217 }218 }219220 return None;221 }222223 let const_stable_indirect = find_attr!(tcx, def_id, RustcConstStableIndirect);224 let const_stab =225 find_attr!(tcx, def_id, RustcConstStability { stability, span: _ } => *stability);226227 // After checking the immediate attributes, get rid of the span and compute implied228 // const stability: inherit feature gate from regular stability.229 let mut const_stab = const_stab230 .map(|const_stab| ConstStability::from_partial(const_stab, const_stable_indirect));231232 // If this is a const fn but not annotated with stability markers, see if we can inherit233 // regular stability.234 if let Some(fn_sig) = tcx.hir_node_by_def_id(def_id).fn_sig()235 && matches!(fn_sig.header.constness, Constness::Const { .. })236 && const_stab.is_none()237 // We only ever inherit unstable features.238 && let Some(inherit_regular_stab) = tcx.lookup_stability(def_id)239 && inherit_regular_stab.is_unstable()240 {241 const_stab = Some(ConstStability {242 // We subject these implicitly-const functions to recursive const stability.243 const_stable_indirect: true,244 promotable: false,245 level: inherit_regular_stab.level,246 feature: inherit_regular_stab.feature,247 });248 }249250 if let Some(const_stab) = const_stab {251 return Some(const_stab);252 }253254 // `impl const Trait for Type` items forward their const stability to their immediate children.255 // FIXME(const_trait_impl): how is this supposed to interact with `#[rustc_const_stable_indirect]`?256 // Currently, once that is set, we do not inherit anything from the parent any more.257 if inherit_const_stability(tcx, def_id) {258 let parent = tcx.opt_local_parent(def_id)?;259 let parent = tcx.lookup_const_stability(parent)?;260 if parent.is_const_unstable() {261 return Some(parent);262 }263 }264265 None266}267268fn stability_implications(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> UnordMap<Symbol, Symbol> {269 let mut implications = UnordMap::default();270271 let mut register_implication = |def_id| {272 if let Some(stability) = tcx.lookup_stability(def_id)273 && let StabilityLevel::Unstable { implied_by: Some(implied_by), .. } = stability.level274 {275 implications.insert(implied_by, stability.feature);276 }277278 if let Some(stability) = tcx.lookup_const_stability(def_id)279 && let StabilityLevel::Unstable { implied_by: Some(implied_by), .. } = stability.level280 {281 implications.insert(implied_by, stability.feature);282 }283 };284285 if tcx.features().staged_api() {286 register_implication(CRATE_DEF_ID);287 for def_id in tcx.hir_crate_items(()).definitions() {288 register_implication(def_id);289 let def_kind = tcx.def_kind(def_id);290 if def_kind.is_adt() {291 let adt = tcx.adt_def(def_id);292 for variant in adt.variants() {293 if variant.def_id != def_id.to_def_id() {294 register_implication(variant.def_id.expect_local());295 }296 for field in &variant.fields {297 register_implication(field.did.expect_local());298 }299 if let Some(ctor_def_id) = variant.ctor_def_id() {300 register_implication(ctor_def_id.expect_local())301 }302 }303 }304 if def_kind.has_generics() {305 for param in tcx.generics_of(def_id).own_params.iter() {306 register_implication(param.def_id.expect_local())307 }308 }309 }310 }311312 implications313}314315struct MissingStabilityAnnotations<'tcx> {316 tcx: TyCtxt<'tcx>,317 effective_visibilities: &'tcx EffectiveVisibilities,318}319320impl<'tcx> MissingStabilityAnnotations<'tcx> {321 /// Verify that deprecation and stability attributes make sense with one another.322 #[instrument(level = "trace", skip(self))]323 fn check_compatible_stability(&self, def_id: LocalDefId) {324 if !self.tcx.features().staged_api() {325 return;326 }327328 let depr = self.tcx.lookup_deprecation_entry(def_id);329 let stab = self.tcx.lookup_stability(def_id);330 let const_stab = self.tcx.lookup_const_stability(def_id);331332 macro_rules! find_attr_span {333 ($name:ident) => {{334 let attrs = self.tcx.hir_attrs(self.tcx.local_def_id_to_hir_id(def_id));335 find_attr!(attrs, AttributeKind::$name { span, .. } => *span)336 }}337 }338339 if stab.is_none()340 && depr.is_some_and(|d| d.attr.is_since_rustc_version())341 && let Some(span) = find_attr_span!(Deprecated)342 {343 self.tcx.dcx().emit_err(diagnostics::DeprecatedAttribute { span });344 }345346 if let Some(stab) = stab {347 // Error if prohibited, or can't inherit anything from a container.348 let kind = annotation_kind(self.tcx, def_id);349 if kind == AnnotationKind::Prohibited350 || (kind == AnnotationKind::Container && stab.level.is_stable() && depr.is_some())351 {352 if let Some(span) = find_attr_span!(Stability) {353 let item_sp = self.tcx.def_span(def_id);354 self.tcx.dcx().emit_err(diagnostics::UselessStability { span, item_sp });355 }356 }357358 // Check if deprecated_since < stable_since. If it is,359 // this is *almost surely* an accident.360 if let Some(depr) = depr361 && let DeprecatedSince::RustcVersion(dep_since) = depr.attr.since362 && let StabilityLevel::Stable { since: stab_since, .. } = stab.level363 && let Some(span) = find_attr_span!(Stability)364 {365 let item_sp = self.tcx.def_span(def_id);366 match stab_since {367 StableSince::Current => {368 self.tcx369 .dcx()370 .emit_err(diagnostics::CannotStabilizeDeprecated { span, item_sp });371 }372 StableSince::Version(stab_since) => {373 if dep_since < stab_since {374 self.tcx375 .dcx()376 .emit_err(diagnostics::CannotStabilizeDeprecated { span, item_sp });377 }378 }379 StableSince::Err(_) => {380 // An error already reported. Assume the unparseable stabilization381 // version is older than the deprecation version.382 }383 }384 }385 }386387 // If the current node is a function with const stability attributes (directly given or388 // implied), check if the function/method is const or the parent impl block is const.389 let fn_sig = self.tcx.hir_node_by_def_id(def_id).fn_sig();390 if let Some(fn_sig) = fn_sig391 && !matches!(fn_sig.header.constness, Constness::Const { .. })392 && const_stab.is_some()393 && find_attr_span!(RustcConstStability).is_some()394 {395 self.tcx.dcx().emit_err(diagnostics::MissingConstErr { fn_sig_span: fn_sig.span });396 }397398 // If this is marked const *stable*, it must also be regular-stable.399 if let Some(const_stab) = const_stab400 && let Some(fn_sig) = fn_sig401 && const_stab.is_const_stable()402 && !stab.is_some_and(|s| s.is_stable())403 && let Some(path_span) = find_attr_span!(RustcConstStability)404 {405 self.tcx.dcx().emit_err(diagnostics::ConstStableNotStable {406 fn_sig_span: fn_sig.span,407 path_span,408 });409 }410411 if let Some(stab) = &const_stab412 && stab.is_const_stable()413 && stab.const_stable_indirect414 && let Some(span) = find_attr_span!(RustcConstStability)415 {416 self.tcx.dcx().emit_err(diagnostics::RustcConstStableIndirectPairing { span });417 }418 }419420 #[instrument(level = "debug", skip(self))]421 fn check_missing_stability(&self, def_id: LocalDefId) {422 let stab = self.tcx.lookup_stability(def_id);423 self.tcx.ensure_ok().lookup_const_stability(def_id);424 if !self.tcx.sess.is_test_crate()425 && stab.is_none()426 && self.effective_visibilities.is_reachable(def_id)427 {428 let descr = self.tcx.def_descr(def_id.to_def_id());429 let span = self.tcx.def_span(def_id);430 self.tcx.dcx().emit_err(diagnostics::MissingStabilityAttr { span, descr });431 }432 }433434 fn check_missing_const_stability(&self, def_id: LocalDefId) {435 let is_const = self.tcx.is_const_fn(def_id.to_def_id())436 || (self.tcx.def_kind(def_id.to_def_id()) == DefKind::Trait437 && self.tcx.is_const_trait(def_id.to_def_id()));438439 // Reachable const fn/trait must have a stability attribute.440 if is_const441 && self.effective_visibilities.is_reachable(def_id)442 && self.tcx.lookup_const_stability(def_id).is_none()443 {444 let span = self.tcx.def_span(def_id);445 let descr = self.tcx.def_descr(def_id.to_def_id());446 self.tcx.dcx().emit_err(diagnostics::MissingConstStabAttr { span, descr });447 }448 }449}450451impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {452 type NestedFilter = nested_filter::OnlyBodies;453454 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {455 self.tcx456 }457458 fn visit_item(&mut self, i: &'tcx Item<'tcx>) {459 self.check_compatible_stability(i.owner_id.def_id);460461 // Inherent impls and foreign modules serve only as containers for other items,462 // they don't have their own stability. They still can be annotated as unstable463 // and propagate this instability to children, but this annotation is completely464 // optional. They inherit stability from their parents when unannotated.465 if !matches!(466 i.kind,467 hir::ItemKind::Impl(hir::Impl { of_trait: None, .. })468 | hir::ItemKind::ForeignMod { .. }469 ) {470 self.check_missing_stability(i.owner_id.def_id);471 }472473 // Ensure stable `const fn` have a const stability attribute.474 self.check_missing_const_stability(i.owner_id.def_id);475476 intravisit::walk_item(self, i)477 }478479 fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {480 self.check_compatible_stability(ti.owner_id.def_id);481 self.check_missing_stability(ti.owner_id.def_id);482 intravisit::walk_trait_item(self, ti);483 }484485 fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {486 self.check_compatible_stability(ii.owner_id.def_id);487 if let hir::ImplItemImplKind::Inherent { .. } = ii.impl_kind {488 self.check_missing_stability(ii.owner_id.def_id);489 self.check_missing_const_stability(ii.owner_id.def_id);490 }491 intravisit::walk_impl_item(self, ii);492 }493494 fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) {495 self.check_compatible_stability(var.def_id);496 self.check_missing_stability(var.def_id);497 if let Some(ctor_def_id) = var.data.ctor_def_id() {498 self.check_missing_stability(ctor_def_id);499 }500 intravisit::walk_variant(self, var);501 }502503 fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) {504 self.check_compatible_stability(s.def_id);505 self.check_missing_stability(s.def_id);506 intravisit::walk_field_def(self, s);507 }508509 fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {510 self.check_compatible_stability(i.owner_id.def_id);511 self.check_missing_stability(i.owner_id.def_id);512 intravisit::walk_foreign_item(self, i);513 }514515 fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {516 self.check_compatible_stability(p.def_id);517 // Note that we don't need to `check_missing_stability` for default generic parameters,518 // as we assume that any default generic parameters without attributes are automatically519 // stable (assuming they have not inherited instability from their parent).520 intravisit::walk_generic_param(self, p);521 }522}523524/// Cross-references the feature names of unstable APIs with enabled525/// features and possibly prints errors.526fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, mod_id: LocalModId) {527 let mut checker = Checker { tcx, mod_id, unstable_reexports: FxIndexMap::default() };528 tcx.hir_visit_item_likes_in_module(mod_id, &mut checker);529 checker.emit_ineffective_unstable_reexports();530531 let is_staged_api =532 tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api();533 if is_staged_api {534 let effective_visibilities = &tcx.effective_visibilities(());535 let mut missing = MissingStabilityAnnotations { tcx, effective_visibilities };536 if mod_id.is_top_level_module() {537 missing.check_missing_stability(CRATE_DEF_ID);538 }539 tcx.hir_visit_item_likes_in_module(mod_id, &mut missing);540 }541542 if mod_id.is_top_level_module() {543 check_unused_or_stable_features(tcx)544 }545}546547pub(crate) fn provide(providers: &mut Providers) {548 *providers = Providers {549 check_mod_unstable_api_usage,550 stability_implications,551 lookup_stability,552 lookup_const_stability,553 lookup_default_body_stability,554 lookup_deprecation_entry,555 ..*providers556 };557}558559struct UnstableReexport {560 hir_id: HirId,561 span: Span,562 has_target: bool,563 all_targets_stable: bool,564}565566struct Checker<'tcx> {567 tcx: TyCtxt<'tcx>,568 mod_id: LocalModId,569 unstable_reexports: FxIndexMap<Span, UnstableReexport>,570}571572impl<'tcx> Checker<'tcx> {573 fn unstable_reexport_span(&self, hir_id: HirId) -> Option<Span> {574 let attrs = self.tcx.hir_attrs(hir_id);575 let (stability, span) =576 find_attr!(attrs, Stability { stability, span } => (*stability, *span))?;577578 stability.level.is_unstable().then_some(span)579 }580581 fn classify_reexport_targets<Id>(582 &self,583 targets: impl IntoIterator<Item = Res<Id>>,584 ) -> (bool, bool) {585 let mut has_target = false;586 let mut all_targets_stable = true;587588 for res in targets {589 match res {590 Res::Def(_, def_id) => {591 has_target = true;592593 match self.tcx.lookup_stability(def_id) {594 Some(stability) if stability.level.is_unstable() => {595 all_targets_stable = false;596 }597 Some(_) => {}598599 None => {600 // Items from crates without staged API metadata are601 // effectively stable. Unmarked items in staged API602 // crates are diagnosed by the existing stability checks.603 if self.tcx.lookup_stability(def_id.krate.as_def_id()).is_some() {604 all_targets_stable = false;605 }606 }607 }608 }609610 // Primitives are stable and have no DefId.611 Res::PrimTy(_) => {612 has_target = true;613 }614615 // Do not lint if the target cannot be classified.616 _ => {617 all_targets_stable = false;618 }619 }620 }621622 (has_target, all_targets_stable)623 }624625 fn record_unstable_reexport(626 &mut self,627 hir_id: HirId,628 attr_span: Span,629 span: Span,630 has_target: bool,631 all_targets_stable: bool,632 ) {633 let entry = self.unstable_reexports.entry(attr_span).or_insert(UnstableReexport {634 hir_id,635 span,636 has_target: false,637 all_targets_stable: true,638 });639640 entry.has_target |= has_target;641 entry.all_targets_stable &= all_targets_stable;642 }643644 fn check_single_unstable_reexport(&mut self, hir_id: HirId, path: &'tcx UsePath<'tcx>) {645 let Some(attr_span) = self.unstable_reexport_span(hir_id) else {646 return;647 };648649 let (has_target, all_targets_stable) =650 self.classify_reexport_targets(path.res.present_items());651652 self.record_unstable_reexport(hir_id, attr_span, path.span, has_target, all_targets_stable);653 }654655 fn check_glob_unstable_reexport(656 &mut self,657 hir_id: HirId,658 glob_def_id: LocalDefId,659 path: &'tcx UsePath<'tcx>,660 ) {661 let Some(attr_span) = self.unstable_reexport_span(hir_id) else {662 return;663 };664665 let glob_def_id = glob_def_id.to_def_id();666667 let targets = self668 .tcx669 .module_children_local(self.mod_id.to_local_def_id())670 .iter()671 .filter(|child| {672 child.reexport_chain.iter().any(|reexport| reexport.id() == Some(glob_def_id))673 })674 .map(|child| child.res);675676 let (has_target, all_targets_stable) = self.classify_reexport_targets(targets);677678 self.record_unstable_reexport(hir_id, attr_span, path.span, has_target, all_targets_stable);679 }680681 fn containing_module_is_unstable(&self) -> bool {682 self.tcx683 .lookup_stability(self.mod_id.to_local_def_id())684 .is_some_and(|stability| stability.level.is_unstable())685 }686687 fn emit_ineffective_unstable_reexports(&self) {688 // an unstable module already makes its re-exports unstable689 // keep the explicit annotation without linting it as ineffective690 if self.unstable_reexports.is_empty() || self.containing_module_is_unstable() {691 return;692 }693694 for reexport in self.unstable_reexports.values() {695 if reexport.has_target && reexport.all_targets_stable {696 self.tcx.emit_node_span_lint(697 INEFFECTIVE_UNSTABLE_REEXPORTS,698 reexport.hir_id,699 reexport.span,700 diagnostics::IneffectiveUnstableReexport,701 );702 }703 }704 }705}706707impl<'tcx> Visitor<'tcx> for Checker<'tcx> {708 type NestedFilter = nested_filter::OnlyBodies;709710 /// Because stability levels are scoped lexically, we want to walk711 /// nested items in the context of the outer item, so enable712 /// deep-walking.713 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {714 self.tcx715 }716717 fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {718 match item.kind {719 hir::ItemKind::ExternCrate(_, ident) => {720 // compiler-generated `extern crate` items have a dummy span.721 // `std` is still checked for the `restricted-std` feature.722 if item.span.is_dummy() && ident.name != sym::std {723 return;724 }725726 let Some(cnum) = self.tcx.extern_mod_stmt_cnum(item.owner_id.def_id) else {727 return;728 };729 let def_id = cnum.as_def_id();730 self.tcx.check_stability(def_id, Some(item.hir_id()), item.span, None);731 }732733 // For implementations of traits, check the stability of each item734 // individually as it's possible to have a stable trait with unstable735 // items.736 hir::ItemKind::Impl(hir::Impl {737 of_trait: Some(of_trait),738 self_ty,739 items,740 constness,741 ..742 }) => {743 let features = self.tcx.features();744 if features.staged_api() {745 let attrs = self.tcx.hir_attrs(item.hir_id());746 let stab = find_attr!(attrs, Stability{stability, span} => (*stability, *span));747748 // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem749 let const_stab =750 find_attr!(attrs, RustcConstStability{stability, ..} => *stability);751752 let unstable_feature_stab = find_attr!(attrs, UnstableFeatureBound(i) => i)753 .map(|i| i.as_slice())754 .unwrap_or_default();755756 // If this impl block has an #[unstable] attribute, give an757 // error if all involved types and traits are stable, because758 // it will have no effect.759 // See: https://github.com/rust-lang/rust/issues/55436760 //761 // The exception is when there are both #[unstable_feature_bound(..)] and762 // #![unstable(feature = "..", issue = "..")] that have the same symbol because763 // that can effectively mark an impl as unstable.764 //765 // For example:766 // ```767 // #[unstable_feature_bound(feat_foo)]768 // #[unstable(feature = "feat_foo", issue = "none")]769 // impl Foo for Bar {}770 // ```771 if let Some((772 Stability { level: StabilityLevel::Unstable { .. }, feature },773 span,774 )) = stab775 {776 let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true };777 c.visit_ty_unambig(self_ty);778 c.visit_trait_ref(&of_trait.trait_ref);779780 // Skip the lint if the impl is marked as unstable using781 // #[unstable_feature_bound(..)]782 let mut unstable_feature_bound_in_effect = false;783 for (unstable_bound_feat_name, _) in unstable_feature_stab {784 if *unstable_bound_feat_name == feature {785 unstable_feature_bound_in_effect = true;786 }787 }788789 // do not lint when the trait isn't resolved, since resolution error should790 // be fixed first791 if of_trait.trait_ref.path.res != Res::Err792 && c.fully_stable793 && !unstable_feature_bound_in_effect794 {795 self.tcx.emit_node_span_lint(796 INEFFECTIVE_UNSTABLE_TRAIT_IMPL,797 item.hir_id(),798 span,799 diagnostics::IneffectiveUnstableImpl,800 );801 }802 }803804 if features.const_trait_impl()805 && let hir::Constness::Const { .. } = constness806 {807 let stable_or_implied_stable = match const_stab {808 None => true,809 Some(stab) if stab.is_const_stable() => {810 // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable811 // needs to have an error emitted.812 // Note: Remove this error once `const_trait_impl` is stabilized813 self.tcx.dcx().emit_err(diagnostics::TraitImplConstStable {814 span: item.span,815 });816 true817 }818 Some(_) => false,819 };820821 if let Some(trait_id) = of_trait.trait_ref.trait_def_id()822 && let Some(const_stab) = self.tcx.lookup_const_stability(trait_id)823 {824 // the const stability of a trait impl must match the const stability on the trait.825 if const_stab.is_const_stable() != stable_or_implied_stable {826 let trait_span = self.tcx.def_ident_span(trait_id).unwrap();827828 let impl_stability = if stable_or_implied_stable {829 diagnostics::ImplConstStability::Stable { span: item.span }830 } else {831 diagnostics::ImplConstStability::Unstable { span: item.span }832 };833 let trait_stability = if const_stab.is_const_stable() {834 diagnostics::TraitConstStability::Stable { span: trait_span }835 } else {836 diagnostics::TraitConstStability::Unstable { span: trait_span }837 };838839 self.tcx.dcx().emit_err(840 diagnostics::TraitImplConstStabilityMismatch {841 span: item.span,842 impl_stability,843 trait_stability,844 },845 );846 }847 }848 }849 }850851 if let hir::Constness::Const { .. } = constness852 && let Some(def_id) = of_trait.trait_ref.trait_def_id()853 {854 // FIXME(const_trait_impl): Improve the span here.855 self.tcx.check_const_stability(856 def_id,857 of_trait.trait_ref.path.span,858 of_trait.trait_ref.path.span,859 );860 }861862 for impl_item_ref in items {863 let impl_item = self.tcx.associated_item(impl_item_ref.owner_id);864865 if let AssocContainer::TraitImpl(Ok(def_id)) = impl_item.container {866 // Pass `None` to skip deprecation warnings.867 self.tcx.check_stability(868 def_id,869 None,870 self.tcx.def_span(impl_item_ref.owner_id),871 None,872 );873 }874 }875 }876877 _ => (/* pass */),878 }879 intravisit::walk_item(self, item);880 }881882 fn visit_poly_trait_ref(&mut self, t: &'tcx hir::PolyTraitRef<'tcx>) {883 match t.modifiers.constness {884 hir::BoundConstness::Always(span) | hir::BoundConstness::Maybe(span) => {885 if let Some(def_id) = t.trait_ref.trait_def_id() {886 self.tcx.check_const_stability(def_id, t.trait_ref.path.span, span);887 }888 }889 hir::BoundConstness::Never => {}890 }891 intravisit::walk_poly_trait_ref(self, t);892 }893894 fn visit_use(&mut self, tree: &'tcx hir::UseTree<'tcx>, hir_id: HirId, def_id: LocalDefId) {895 let mut v = vec![];896897 #[instrument(skip(visitor))]898 fn recurse<'tcx>(899 visitor: &mut Checker<'tcx>,900 tree: &'tcx hir::UseTree<'tcx>,901 hir_id: HirId,902 def_id: LocalDefId,903 stack: &mut Vec<&'tcx [hir::PathSegment<'tcx>]>,904 ) {905 let UsePath { segments, res, span } = *tree.prefix;906907 match tree.kind {908 hir::UseKind::Single(_) | hir::UseKind::Glob => {909 if visitor.tcx.features().staged_api()910 && visitor.tcx.local_visibility(def_id).is_public()911 {912 if let hir::UseKind::Single(_) = tree.kind {913 visitor.check_single_unstable_reexport(hir_id, tree.prefix);914 } else {915 visitor.check_glob_unstable_reexport(hir_id, def_id, tree.prefix);916 }917 }918 // A use item can import something from two namespaces at the same time.919 // For deprecation/stability we don't want to warn twice.920 // This specifically happens with constructors for unit/tuple structs.921 if let Some(res) = res.value_ns.or(res.type_ns) {922 visitor.check_path(&Path { segments, res, span }, hir_id, stack);923 }924925 // Though, visit the macro namespace if it exists,926 // regardless of the checks above relating to constructors.927 if let Some(res) = res.macro_ns {928 visitor.check_path(&Path { segments, res, span }, hir_id, stack);929 }930 }931 hir::UseKind::Nested { items } => {932 stack.push(tree.prefix.segments);933 if items.is_empty() {934 // need to handle `use foo::bar::{};`935 visitor.check_path(936 &Path {937 segments,938 res: segments.last().map_or(Res::Err, |seg| seg.res),939 span,940 },941 hir_id,942 stack,943 );944 } else {945 for (tree, id, def_id) in items {946 recurse(visitor, tree, *id, *def_id, stack);947 }948 }949 stack.pop();950 }951 }952 }953 recurse(self, tree, hir_id, def_id, &mut v);954 }955956 fn visit_path(&mut self, path: &hir::Path<'tcx>, id: hir::HirId) {957 self.check_path(path, id, &[]);958959 intravisit::walk_path(self, path)960 }961}962963impl<'tcx> Checker<'tcx> {964 fn check_path(965 &mut self,966 path: &hir::Path<'tcx>,967 id: hir::HirId,968 prefix: &[&[hir::PathSegment<'tcx>]],969 ) {970 if let Some(def_id) = path.res.opt_def_id() {971 let method_span = path.segments.last().map(|s| s.ident.span);972 let item_is_allowed = self.tcx.check_stability_allow_unstable(973 def_id,974 Some(id),975 path.span,976 method_span,977 if is_unstable_reexport(self.tcx, id) {978 AllowUnstable::Yes979 } else {980 AllowUnstable::No981 },982 );983984 if item_is_allowed {985 // The item itself is allowed; check whether the path there is also allowed.986 let is_allowed_through_unstable_modules: Option<(Symbol, Symbol)> =987 self.tcx.lookup_stability(def_id).and_then(|stab| match stab.level {988 StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {989 allowed_through_unstable_modules990 }991 _ => None,992 });993 let segments = prefix994 .into_iter()995 .flat_map(|i| i.into_iter())996 .chain(path.segments.iter().rev().skip(1).rev());997 let intrinsics_module = segments.clone().last();998 for segment in segments {999 self.check_path_segments(1000 path.span,1001 segment,1002 intrinsics_module,1003 id,1004 method_span,1005 is_allowed_through_unstable_modules,1006 );1007 }1008 }1009 }1010 }1011 /// Check parent modules stability as well if the item the path refers to is itself1012 /// stable. We only emit errors for unstable path segments if the item is stable1013 /// or allowed because stability is often inherited, so the most common case is that1014 /// both the segments and the item are unstable behind the same feature flag.1015 ///1016 /// We check here rather than in `visit_path_segment` to prevent visiting the last1017 /// path segment twice1018 ///1019 /// We include special cases via `#[rustc_allowed_through_unstable_modules]` for items1020 /// that were accidentally stabilized through unstable paths before this check was1021 /// added, such as `core::intrinsics::transmute`1022 fn check_path_segments(1023 &mut self,1024 span: Span,1025 path_segment: &hir::PathSegment<'_>,1026 intrinsics_module: Option<&hir::PathSegment<'_>>,1027 id: HirId,1028 method_span: Option<Span>,1029 is_allowed_through_unstable_modules: Option<(Symbol, Symbol)>,1030 ) {1031 // The item itself is allowed; check whether the path there is also allowed.10321033 if let Some(def_id) = path_segment.res.opt_def_id() {1034 match is_allowed_through_unstable_modules {1035 None => {1036 // Emit a hard stability error if this path is not stable.10371038 // use `None` for id to prevent deprecation check1039 self.tcx.check_stability_allow_unstable(1040 def_id,1041 None,1042 path_segment.ident.span,1043 None,1044 if is_unstable_reexport(self.tcx, id) {1045 AllowUnstable::Yes1046 } else {1047 AllowUnstable::No1048 },1049 );1050 }1051 Some((message, suggestion)) => {1052 // Call the stability check directly so that we can control which1053 // diagnostic is emitted.1054 let eval_result = self.tcx.eval_stability_allow_unstable(1055 def_id,1056 None,1057 span,1058 None,1059 if is_unstable_reexport(self.tcx, id) {1060 AllowUnstable::Yes1061 } else {1062 AllowUnstable::No1063 },1064 );1065 let is_allowed = matches!(eval_result, EvalResult::Allow);1066 if !is_allowed {1067 // Show a deprecation message.1068 let intrinsics_module = intrinsics_module.unwrap_or_else(|| {1069 span_bug!(1070 span,1071 "no module for `is_allowed_through_unstable_modules` intrinsic {path_segment:?}"1072 )1073 });1074 let diag = diagnostics::RustcAtumSuggestion {1075 message,1076 import_span: span,1077 unstable_mod_span: { intrinsics_module.ident.span },1078 module: intrinsics_module.ident,1079 suggestion,1080 };1081 self.tcx.emit_node_span_lint(1082 UNSTABLE_IMPORTS,1083 id,1084 method_span.unwrap_or(span),1085 diag,1086 );1087 }1088 }1089 }1090 }1091 }1092}10931094/// Check whether a path is a `use` item that has been marked as unstable.1095///1096/// See issue #94972 for details on why this is a special case1097fn is_unstable_reexport(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {1098 // Get the LocalDefId so we can lookup the item to check the kind.1099 let def_id = id.owner.def_id;11001101 let Some(stab) = tcx.lookup_stability(def_id) else {1102 return false;1103 };11041105 if stab.level.is_stable() {1106 // The re-export is not marked as unstable, don't override1107 return false;1108 }11091110 // If this is a path that isn't a use, we don't need to do anything special1111 if !matches!(1112 tcx.hir_node(id),1113 hir::Node::Item(hir::Item { kind: ItemKind::Use(..), .. }) | hir::Node::NestedUseTree(_)1114 ) {1115 return false;1116 }11171118 true1119}11201121struct CheckTraitImplStable<'tcx> {1122 tcx: TyCtxt<'tcx>,1123 fully_stable: bool,1124}11251126impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> {1127 fn visit_path(&mut self, path: &hir::Path<'tcx>, _id: hir::HirId) {1128 if let Some(def_id) = path.res.opt_def_id()1129 && let Some(stab) = self.tcx.lookup_stability(def_id)1130 {1131 self.fully_stable &= stab.level.is_stable();1132 }1133 intravisit::walk_path(self, path)1134 }11351136 fn visit_trait_ref(&mut self, t: &'tcx TraitRef<'tcx>) {1137 if let Res::Def(DefKind::Trait, trait_did) = t.path.res {1138 if let Some(stab) = self.tcx.lookup_stability(trait_did) {1139 self.fully_stable &= stab.level.is_stable();1140 }1141 }1142 intravisit::walk_trait_ref(self, t)1143 }11441145 fn visit_ty(&mut self, t: &'tcx Ty<'tcx, AmbigArg>) {1146 if let TyKind::FnPtr(function) = t.kind {1147 if extern_abi_stability(function.abi).is_err() {1148 self.fully_stable = false;1149 }1150 }1151 intravisit::walk_ty(self, t)1152 }1153}11541155/// Given the list of enabled features that were not language features (i.e., that1156/// were expected to be library features), and the list of features used from1157/// libraries, identify activated features that don't exist and error about them.1158// This is `pub` for rustdoc. rustc should call it through `check_mod_unstable_api_usage`.1159pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {1160 let _prof_timer = tcx.sess.timer("unused_lib_feature_checking");11611162 let enabled_lang_features = tcx.features().enabled_lang_features();1163 let mut lang_features = UnordSet::default();1164 for EnabledLangFeature { gate_name, attr_sp, stable_since } in enabled_lang_features {1165 if let Some(version) = stable_since {1166 // Mark the feature as enabled, to ensure that it is not marked as unused.1167 let _ = tcx.features().enabled(*gate_name);11681169 // Warn if the user has enabled an already-stable lang feature.1170 unnecessary_stable_feature_lint(tcx, *attr_sp, *gate_name, *version);1171 }1172 if !lang_features.insert(gate_name) {1173 // Warn if the user enables a lang feature multiple times.1174 duplicate_feature_lint(tcx, *attr_sp, *gate_name);1175 }1176 }11771178 let enabled_lib_features = tcx.features().enabled_lib_features();1179 let mut remaining_lib_features = FxIndexMap::default();1180 for EnabledLibFeature { gate_name, attr_sp } in enabled_lib_features {1181 if remaining_lib_features.contains_key(gate_name) {1182 // Warn if the user enables a lib feature multiple times.1183 duplicate_feature_lint(tcx, *attr_sp, *gate_name);1184 }1185 remaining_lib_features.insert(*gate_name, *attr_sp);1186 }1187 // `stdbuild` has special handling for `libc`, so we need to1188 // recognise the feature when building std.1189 // Likewise, libtest is handled specially, so `test` isn't1190 // available as we'd like it to be.1191 // FIXME: only remove `libc` when `stdbuild` is enabled.1192 // FIXME: remove special casing for `test`.1193 // FIXME(#120456) - is `swap_remove` correct?1194 remaining_lib_features.swap_remove(&sym::libc);1195 remaining_lib_features.swap_remove(&sym::test);11961197 /// For each feature in `defined_features`..1198 ///1199 /// - If it is in `remaining_lib_features` (those features with `#![feature(..)]` attributes in1200 /// the current crate), check if it is stable (or partially stable) and thus an unnecessary1201 /// attribute.1202 /// - If it is in `remaining_implications` (a feature that is referenced by an `implied_by`1203 /// from the current crate), then remove it from the remaining implications.1204 ///1205 /// Once this function has been invoked for every feature (local crate and all extern crates),1206 /// then..1207 ///1208 /// - If features remain in `remaining_lib_features`, then the user has enabled a feature that1209 /// does not exist.1210 /// - If features remain in `remaining_implications`, the `implied_by` refers to a feature that1211 /// does not exist.1212 ///1213 /// By structuring the code in this way: checking the features defined from each crate one at a1214 /// time, less loading from metadata is performed and thus compiler performance is improved.1215 fn check_features<'tcx>(1216 tcx: TyCtxt<'tcx>,1217 remaining_lib_features: &mut FxIndexMap<Symbol, Span>,1218 remaining_implications: &mut UnordMap<Symbol, Symbol>,1219 defined_features: &LibFeatures,1220 all_implications: &UnordMap<Symbol, Symbol>,1221 ) {1222 for (feature, stability) in defined_features.to_sorted_vec() {1223 if let FeatureStability::AcceptedSince(since) = stability1224 && let Some(span) = remaining_lib_features.get(&feature)1225 {1226 // Mark the feature as enabled, to ensure that it is not marked as unused.1227 let _ = tcx.features().enabled(feature);12281229 // Warn if the user has enabled an already-stable lib feature.1230 if let Some(implies) = all_implications.get(&feature) {1231 unnecessary_partially_stable_feature_lint(tcx, *span, feature, *implies, since);1232 } else {1233 unnecessary_stable_feature_lint(tcx, *span, feature, since);1234 }1235 }1236 // FIXME(#120456) - is `swap_remove` correct?1237 remaining_lib_features.swap_remove(&feature);12381239 // `feature` is the feature doing the implying, but `implied_by` is the feature with1240 // the attribute that establishes this relationship. `implied_by` is guaranteed to be a1241 // feature defined in the local crate because `remaining_implications` is only the1242 // implications from this crate.1243 remaining_implications.remove(&feature);12441245 if let FeatureStability::Unstable { old_name: Some(alias) } = stability1246 && let Some(span) = remaining_lib_features.swap_remove(&alias)1247 {1248 tcx.dcx().emit_err(diagnostics::RenamedFeature { span, feature, alias });1249 }12501251 if remaining_lib_features.is_empty() && remaining_implications.is_empty() {1252 break;1253 }1254 }1255 }12561257 // All local crate implications need to have the feature that implies it confirmed to exist.1258 let mut remaining_implications = tcx.stability_implications(LOCAL_CRATE).clone();12591260 // We always collect the lib features enabled in the current crate, even if there are1261 // no unknown features, because the collection also does feature attribute validation.1262 let local_defined_features = tcx.lib_features(LOCAL_CRATE);1263 if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() {1264 let crates = tcx.crates(());12651266 // Loading the implications of all crates is unavoidable to be able to emit the partial1267 // stabilization diagnostic, but it can be avoided when there are no1268 // `remaining_lib_features`.1269 let mut all_implications = remaining_implications.clone();1270 for &cnum in crates {1271 all_implications1272 .extend_unord(tcx.stability_implications(cnum).items().map(|(k, v)| (*k, *v)));1273 }12741275 check_features(1276 tcx,1277 &mut remaining_lib_features,1278 &mut remaining_implications,1279 local_defined_features,1280 &all_implications,1281 );12821283 for &cnum in crates {1284 if remaining_lib_features.is_empty() && remaining_implications.is_empty() {1285 break;1286 }1287 check_features(1288 tcx,1289 &mut remaining_lib_features,1290 &mut remaining_implications,1291 tcx.lib_features(cnum),1292 &all_implications,1293 );1294 }12951296 if !remaining_lib_features.is_empty() {1297 let lang_features =1298 UNSTABLE_LANG_FEATURES.iter().map(|feature| feature.name).collect::<Vec<_>>();1299 let lib_features = crates1300 .iter()1301 .flat_map(|&cnum| {1302 tcx.lib_features(cnum).stability.keys().copied().into_sorted_stable_ord()1303 })1304 .collect::<Vec<_>>();13051306 let valid_feature_names = [lang_features, lib_features].concat();13071308 // Collect all of the marked as "removed" features1309 let unstable_removed_features = crates1310 .iter()1311 .flat_map(|&cnum| {1312 find_attr!(tcx, cnum.as_def_id(), UnstableRemoved(rem_features) => rem_features)1313 .into_flat_iter()1314 })1315 .collect::<Vec<_>>();13161317 for (feature, span) in remaining_lib_features {1318 if let Some(removed) =1319 unstable_removed_features.iter().find(|removed| removed.feature == feature)1320 {1321 tcx.dcx().emit_err(diagnostics::FeatureRemoved {1322 span,1323 feature,1324 reason: removed.reason,1325 link: removed.link,1326 since: removed.since.to_string(),1327 });1328 } else {1329 let suggestion =1330 feature.find_similar(&valid_feature_names).map(|(actual_name, _)| {1331 diagnostics::MisspelledFeature { span, actual_name }1332 });1333 tcx.dcx().emit_err(diagnostics::UnknownFeature { span, feature, suggestion });1334 }1335 }1336 }1337 }13381339 for (&implied_by, &feature) in remaining_implications.to_sorted_stable_ord() {1340 let local_defined_features = tcx.lib_features(LOCAL_CRATE);1341 let span = local_defined_features1342 .stability1343 .get(&feature)1344 .expect("feature that implied another does not exist")1345 .1;1346 tcx.dcx().emit_err(diagnostics::ImpliedFeatureNotExist { span, feature, implied_by });1347 }1348}13491350fn unnecessary_partially_stable_feature_lint(1351 tcx: TyCtxt<'_>,1352 span: Span,1353 feature: Symbol,1354 implies: Symbol,1355 since: Symbol,1356) {1357 tcx.emit_node_span_lint(1358 STABLE_FEATURES,1359 hir::CRATE_HIR_ID,1360 span,1361 diagnostics::UnnecessaryPartialStableFeature {1362 span,1363 line: tcx.sess.source_map().span_extend_to_line(span),1364 feature,1365 since,1366 implies,1367 },1368 );1369}13701371fn unnecessary_stable_feature_lint(1372 tcx: TyCtxt<'_>,1373 span: Span,1374 feature: Symbol,1375 mut since: Symbol,1376) {1377 if since.as_str() == VERSION_PLACEHOLDER {1378 since = sym::env_CFG_RELEASE;1379 }1380 tcx.emit_node_span_lint(1381 STABLE_FEATURES,1382 hir::CRATE_HIR_ID,1383 span,1384 diagnostics::UnnecessaryStableFeature { feature, since },1385 );1386}13871388fn duplicate_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol) {1389 tcx.emit_node_span_lint(1390 DUPLICATE_FEATURES,1391 hir::CRATE_HIR_ID,1392 span,1393 diagnostics::DuplicateFeature { feature },1394 );1395}