1 //! A pass that annotates every item and method with its stability level,
2 //! propagating default levels lexically from parent to children ast nodes.
5 self, CannotStabilizeDeprecated, DeprecatedAttribute, DuplicateFeatureErr,
6 FeatureOnlyOnNightly, ImpliedFeatureNotExist, InvalidDeprecationVersion, InvalidStability,
7 MissingConstErr, MissingConstStabAttr, MissingStabilityAttr, TraitImplConstStable,
8 UnknownFeature, UselessStability,
11 self as attr, rust_version_symbol, ConstStability, Stability, StabilityLevel, Unstable,
12 UnstableReason, VERSION_PLACEHOLDER,
14 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
15 use rustc_errors::Applicability;
17 use rustc_hir::def::{DefKind, Res};
18 use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
19 use rustc_hir::hir_id::CRATE_HIR_ID;
20 use rustc_hir::intravisit::{self, Visitor};
21 use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant};
22 use rustc_middle::hir::nested_filter;
23 use rustc_middle::middle::privacy::EffectiveVisibilities;
24 use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index};
25 use rustc_middle::ty::{query::Providers, TyCtxt};
26 use rustc_session::lint;
27 use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED};
28 use rustc_span::symbol::{sym, Symbol};
30 use rustc_target::spec::abi::Abi;
32 use std::cmp::Ordering;
34 use std::mem::replace;
35 use std::num::NonZeroU32;
39 /// Annotation is required if not inherited from unstable parents.
41 /// Annotation is useless, reject it.
43 /// Deprecation annotation is useless, reject it. (Stability attribute is still required.)
44 DeprecationProhibited,
45 /// Annotation itself is useless, but it can be propagated to children.
49 /// Whether to inherit deprecation flags for nested items. In most cases, we do want to inherit
50 /// deprecation, because nested items rarely have individual deprecation attributes, and so
51 /// should be treated as deprecated if their parent is. However, default generic parameters
52 /// have separate deprecation attributes from their parents, so we do not wish to inherit
53 /// deprecation in this case. For example, inheriting deprecation for `T` in `Foo<T>`
54 /// would cause a duplicate warning arising from both `Foo` and `T` being deprecated.
56 enum InheritDeprecation {
61 impl InheritDeprecation {
62 fn yes(&self) -> bool {
63 matches!(self, InheritDeprecation::Yes)
67 /// Whether to inherit const stability flags for nested items. In most cases, we do not want to
68 /// inherit const stability: just because an enclosing `fn` is const-stable does not mean
69 /// all `extern` imports declared in it should be const-stable! However, trait methods
70 /// inherit const stability attributes from their parent and do not have their own.
71 enum InheritConstStability {
76 impl InheritConstStability {
77 fn yes(&self) -> bool {
78 matches!(self, InheritConstStability::Yes)
82 enum InheritStability {
87 impl InheritStability {
88 fn yes(&self) -> bool {
89 matches!(self, InheritStability::Yes)
93 /// A private tree-walker for producing an `Index`.
94 struct Annotator<'a, 'tcx> {
97 parent_stab: Option<Stability>,
98 parent_const_stab: Option<ConstStability>,
99 parent_depr: Option<DeprecationEntry>,
103 impl<'a, 'tcx> Annotator<'a, 'tcx> {
104 /// Determine the stability for a node based on its attributes and inherited stability. The
105 /// stability is recorded in the index and used as the parent. If the node is a function,
106 /// `fn_sig` is its signature.
111 fn_sig: Option<&'tcx hir::FnSig<'tcx>>,
112 kind: AnnotationKind,
113 inherit_deprecation: InheritDeprecation,
114 inherit_const_stability: InheritConstStability,
115 inherit_from_parent: InheritStability,
118 F: FnOnce(&mut Self),
120 let attrs = self.tcx.hir().attrs(self.tcx.hir().local_def_id_to_hir_id(def_id));
121 debug!("annotate(id = {:?}, attrs = {:?})", def_id, attrs);
123 let depr = attr::find_deprecation(&self.tcx.sess, attrs);
124 let mut is_deprecated = false;
125 if let Some((depr, span)) = &depr {
126 is_deprecated = true;
128 if kind == AnnotationKind::Prohibited || kind == AnnotationKind::DeprecationProhibited {
129 let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
130 self.tcx.emit_spanned_lint(
134 errors::DeprecatedAnnotationHasNoEffect { span: *span },
138 // `Deprecation` is just two pointers, no need to intern it
139 let depr_entry = DeprecationEntry::local(*depr, def_id);
140 self.index.depr_map.insert(def_id, depr_entry);
141 } else if let Some(parent_depr) = self.parent_depr {
142 if inherit_deprecation.yes() {
143 is_deprecated = true;
144 info!("tagging child {:?} as deprecated from parent", def_id);
145 self.index.depr_map.insert(def_id, parent_depr);
149 if !self.tcx.features().staged_api {
150 // Propagate unstability. This can happen even for non-staged-api crates in case
151 // -Zforce-unstable-if-unmarked is set.
152 if let Some(stab) = self.parent_stab {
153 if inherit_deprecation.yes() && stab.is_unstable() {
154 self.index.stab_map.insert(def_id, stab);
158 self.recurse_with_stability_attrs(
159 depr.map(|(d, _)| DeprecationEntry::local(d, def_id)),
167 let (stab, const_stab, body_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp);
168 let mut const_span = None;
170 let const_stab = const_stab.map(|(const_stab, const_span_node)| {
171 self.index.const_stab_map.insert(def_id, const_stab);
172 const_span = Some(const_span_node);
176 // If the current node is a function, has const stability attributes and if it doesn not have an intrinsic ABI,
177 // check if the function/method is const or the parent impl block is const
178 if let (Some(const_span), Some(fn_sig)) = (const_span, fn_sig) {
179 if fn_sig.header.abi != Abi::RustIntrinsic
180 && fn_sig.header.abi != Abi::PlatformIntrinsic
181 && !fn_sig.header.is_const()
183 if !self.in_trait_impl
184 || (self.in_trait_impl && !self.tcx.is_const_fn_raw(def_id.to_def_id()))
188 .emit_err(MissingConstErr { fn_sig_span: fn_sig.span, const_span });
193 // `impl const Trait for Type` items forward their const stability to their
194 // immediate children.
195 if const_stab.is_none() {
196 debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab);
197 if let Some(parent) = self.parent_const_stab {
198 if parent.is_const_unstable() {
199 self.index.const_stab_map.insert(def_id, parent);
204 if let Some((rustc_attr::Deprecation { is_since_rustc_version: true, .. }, span)) = &depr {
206 self.tcx.sess.emit_err(DeprecatedAttribute { span: *span });
210 if let Some((body_stab, _span)) = body_stab {
211 // FIXME: check that this item can have body stability
213 self.index.default_body_stab_map.insert(def_id, body_stab);
214 debug!(?self.index.default_body_stab_map);
217 let stab = stab.map(|(stab, span)| {
218 // Error if prohibited, or can't inherit anything from a container.
219 if kind == AnnotationKind::Prohibited
220 || (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated)
222 self.tcx.sess.emit_err(UselessStability { span, item_sp });
225 debug!("annotate: found {:?}", stab);
227 // Check if deprecated_since < stable_since. If it is,
228 // this is *almost surely* an accident.
229 if let (&Some(dep_since), &attr::Stable { since: stab_since, .. }) =
230 (&depr.as_ref().and_then(|(d, _)| d.since), &stab.level)
232 // Explicit version of iter::order::lt to handle parse errors properly
233 for (dep_v, stab_v) in
234 iter::zip(dep_since.as_str().split('.'), stab_since.as_str().split('.'))
236 match stab_v.parse::<u64>() {
238 self.tcx.sess.emit_err(InvalidStability { span, item_sp });
241 Ok(stab_vp) => match dep_v.parse::<u64>() {
242 Ok(dep_vp) => match dep_vp.cmp(&stab_vp) {
246 .emit_err(CannotStabilizeDeprecated { span, item_sp });
249 Ordering::Equal => continue,
250 Ordering::Greater => break,
256 .emit_err(InvalidDeprecationVersion { span, item_sp });
265 if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } =
268 self.index.implications.insert(implied_by, feature);
271 self.index.stab_map.insert(def_id, stab);
276 debug!("annotate: stab not found, parent = {:?}", self.parent_stab);
277 if let Some(stab) = self.parent_stab {
278 if inherit_deprecation.yes() && stab.is_unstable() || inherit_from_parent.yes() {
279 self.index.stab_map.insert(def_id, stab);
284 self.recurse_with_stability_attrs(
285 depr.map(|(d, _)| DeprecationEntry::local(d, def_id)),
287 if inherit_const_stability.yes() { const_stab } else { None },
292 fn recurse_with_stability_attrs(
294 depr: Option<DeprecationEntry>,
295 stab: Option<Stability>,
296 const_stab: Option<ConstStability>,
297 f: impl FnOnce(&mut Self),
299 // These will be `Some` if this item changes the corresponding stability attribute.
300 let mut replaced_parent_depr = None;
301 let mut replaced_parent_stab = None;
302 let mut replaced_parent_const_stab = None;
304 if let Some(depr) = depr {
305 replaced_parent_depr = Some(replace(&mut self.parent_depr, Some(depr)));
307 if let Some(stab) = stab {
308 replaced_parent_stab = Some(replace(&mut self.parent_stab, Some(stab)));
310 if let Some(const_stab) = const_stab {
311 replaced_parent_const_stab =
312 Some(replace(&mut self.parent_const_stab, Some(const_stab)));
317 if let Some(orig_parent_depr) = replaced_parent_depr {
318 self.parent_depr = orig_parent_depr;
320 if let Some(orig_parent_stab) = replaced_parent_stab {
321 self.parent_stab = orig_parent_stab;
323 if let Some(orig_parent_const_stab) = replaced_parent_const_stab {
324 self.parent_const_stab = orig_parent_const_stab;
329 impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
330 /// Because stability levels are scoped lexically, we want to walk
331 /// nested items in the context of the outer item, so enable
333 type NestedFilter = nested_filter::All;
335 fn nested_visit_map(&mut self) -> Self::Map {
339 fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
340 let orig_in_trait_impl = self.in_trait_impl;
341 let mut kind = AnnotationKind::Required;
342 let mut const_stab_inherit = InheritConstStability::No;
343 let mut fn_sig = None;
346 // Inherent impls and foreign modules serve only as containers for other items,
347 // they don't have their own stability. They still can be annotated as unstable
348 // and propagate this instability to children, but this annotation is completely
349 // optional. They inherit stability from their parents when unannotated.
350 hir::ItemKind::Impl(hir::Impl { of_trait: None, .. })
351 | hir::ItemKind::ForeignMod { .. } => {
352 self.in_trait_impl = false;
353 kind = AnnotationKind::Container;
355 hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => {
356 self.in_trait_impl = true;
357 kind = AnnotationKind::DeprecationProhibited;
358 const_stab_inherit = InheritConstStability::Yes;
360 hir::ItemKind::Struct(ref sd, _) => {
361 if let Some(ctor_def_id) = sd.ctor_def_id() {
366 AnnotationKind::Required,
367 InheritDeprecation::Yes,
368 InheritConstStability::No,
369 InheritStability::Yes,
374 hir::ItemKind::Fn(ref item_fn_sig, _, _) => {
375 fn_sig = Some(item_fn_sig);
385 InheritDeprecation::Yes,
387 InheritStability::No,
388 |v| intravisit::walk_item(v, i),
390 self.in_trait_impl = orig_in_trait_impl;
393 fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
394 let fn_sig = match ti.kind {
395 hir::TraitItemKind::Fn(ref fn_sig, _) => Some(fn_sig),
403 AnnotationKind::Required,
404 InheritDeprecation::Yes,
405 InheritConstStability::No,
406 InheritStability::No,
408 intravisit::walk_trait_item(v, ti);
413 fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
415 if self.in_trait_impl { AnnotationKind::Prohibited } else { AnnotationKind::Required };
417 let fn_sig = match ii.kind {
418 hir::ImplItemKind::Fn(ref fn_sig, _) => Some(fn_sig),
427 InheritDeprecation::Yes,
428 InheritConstStability::No,
429 InheritStability::No,
431 intravisit::walk_impl_item(v, ii);
436 fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) {
441 AnnotationKind::Required,
442 InheritDeprecation::Yes,
443 InheritConstStability::No,
444 InheritStability::Yes,
446 if let Some(ctor_def_id) = var.data.ctor_def_id() {
451 AnnotationKind::Required,
452 InheritDeprecation::Yes,
453 InheritConstStability::No,
454 InheritStability::Yes,
459 intravisit::walk_variant(v, var)
464 fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) {
469 AnnotationKind::Required,
470 InheritDeprecation::Yes,
471 InheritConstStability::No,
472 InheritStability::Yes,
474 intravisit::walk_field_def(v, s);
479 fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
484 AnnotationKind::Required,
485 InheritDeprecation::Yes,
486 InheritConstStability::No,
487 InheritStability::No,
489 intravisit::walk_foreign_item(v, i);
494 fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
495 let kind = match &p.kind {
496 // Allow stability attributes on default generic arguments.
497 hir::GenericParamKind::Type { default: Some(_), .. }
498 | hir::GenericParamKind::Const { default: Some(_), .. } => AnnotationKind::Container,
499 _ => AnnotationKind::Prohibited,
507 InheritDeprecation::No,
508 InheritConstStability::No,
509 InheritStability::No,
511 intravisit::walk_generic_param(v, p);
517 struct MissingStabilityAnnotations<'tcx> {
519 effective_visibilities: &'tcx EffectiveVisibilities,
522 impl<'tcx> MissingStabilityAnnotations<'tcx> {
523 fn check_missing_stability(&self, def_id: LocalDefId, span: Span) {
524 let stab = self.tcx.stability().local_stability(def_id);
525 if !self.tcx.sess.opts.test
527 && self.effective_visibilities.is_reachable(def_id)
529 let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
530 self.tcx.sess.emit_err(MissingStabilityAttr { span, descr });
534 fn check_missing_const_stability(&self, def_id: LocalDefId, span: Span) {
535 if !self.tcx.features().staged_api {
539 // if the const impl is derived using the `derive_const` attribute,
540 // then it would be "stable" at least for the impl.
541 // We gate usages of it using `feature(const_trait_impl)` anyways
542 // so there is no unstable leakage
543 if self.tcx.is_builtin_derive(def_id.to_def_id()) {
547 let is_const = self.tcx.is_const_fn(def_id.to_def_id())
548 || self.tcx.is_const_trait_impl_raw(def_id.to_def_id());
551 .lookup_stability(def_id)
552 .map_or(false, |stability| stability.level.is_stable());
553 let missing_const_stability_attribute = self.tcx.lookup_const_stability(def_id).is_none();
554 let is_reachable = self.effective_visibilities.is_reachable(def_id);
556 if is_const && is_stable && missing_const_stability_attribute && is_reachable {
557 let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
558 self.tcx.sess.emit_err(MissingConstStabAttr { span, descr });
563 impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
564 type NestedFilter = nested_filter::OnlyBodies;
566 fn nested_visit_map(&mut self) -> Self::Map {
570 fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
571 // Inherent impls and foreign modules serve only as containers for other items,
572 // they don't have their own stability. They still can be annotated as unstable
573 // and propagate this instability to children, but this annotation is completely
574 // optional. They inherit stability from their parents when unannotated.
577 hir::ItemKind::Impl(hir::Impl { of_trait: None, .. })
578 | hir::ItemKind::ForeignMod { .. }
580 self.check_missing_stability(i.owner_id.def_id, i.span);
583 // Ensure stable `const fn` have a const stability attribute.
584 self.check_missing_const_stability(i.owner_id.def_id, i.span);
586 intravisit::walk_item(self, i)
589 fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
590 self.check_missing_stability(ti.owner_id.def_id, ti.span);
591 intravisit::walk_trait_item(self, ti);
594 fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
595 let impl_def_id = self.tcx.hir().get_parent_item(ii.hir_id());
596 if self.tcx.impl_trait_ref(impl_def_id).is_none() {
597 self.check_missing_stability(ii.owner_id.def_id, ii.span);
598 self.check_missing_const_stability(ii.owner_id.def_id, ii.span);
600 intravisit::walk_impl_item(self, ii);
603 fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) {
604 self.check_missing_stability(var.def_id, var.span);
605 if let Some(ctor_def_id) = var.data.ctor_def_id() {
606 self.check_missing_stability(ctor_def_id, var.span);
608 intravisit::walk_variant(self, var);
611 fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) {
612 self.check_missing_stability(s.def_id, s.span);
613 intravisit::walk_field_def(self, s);
616 fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
617 self.check_missing_stability(i.owner_id.def_id, i.span);
618 intravisit::walk_foreign_item(self, i);
620 // Note that we don't need to `check_missing_stability` for default generic parameters,
621 // as we assume that any default generic parameters without attributes are automatically
622 // stable (assuming they have not inherited instability from their parent).
625 fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index {
626 let mut index = Index {
627 stab_map: Default::default(),
628 const_stab_map: Default::default(),
629 default_body_stab_map: Default::default(),
630 depr_map: Default::default(),
631 implications: Default::default(),
635 let mut annotator = Annotator {
639 parent_const_stab: None,
641 in_trait_impl: false,
644 // If the `-Z force-unstable-if-unmarked` flag is passed then we provide
645 // a parent stability annotation which indicates that this is private
646 // with the `rustc_private` feature. This is intended for use when
647 // compiling `librustc_*` crates themselves so we can leverage crates.io
648 // while maintaining the invariant that all sysroot crates are unstable
649 // by default and are unable to be used.
650 if tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
651 let stability = Stability {
652 level: attr::StabilityLevel::Unstable {
653 reason: UnstableReason::Default,
654 issue: NonZeroU32::new(27812),
658 feature: sym::rustc_private,
660 annotator.parent_stab = Some(stability);
665 tcx.hir().span(CRATE_HIR_ID),
667 AnnotationKind::Required,
668 InheritDeprecation::Yes,
669 InheritConstStability::No,
670 InheritStability::No,
671 |v| tcx.hir().walk_toplevel_module(v),
677 /// Cross-references the feature names of unstable APIs with enabled
678 /// features and possibly prints errors.
679 fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
680 tcx.hir().visit_item_likes_in_module(module_def_id, &mut Checker { tcx });
683 pub(crate) fn provide(providers: &mut Providers) {
684 *providers = Providers {
685 check_mod_unstable_api_usage,
687 stability_implications: |tcx, _| tcx.stability().implications.clone(),
688 lookup_stability: |tcx, id| tcx.stability().local_stability(id.expect_local()),
689 lookup_const_stability: |tcx, id| tcx.stability().local_const_stability(id.expect_local()),
690 lookup_default_body_stability: |tcx, id| {
691 tcx.stability().local_default_body_stability(id.expect_local())
693 lookup_deprecation_entry: |tcx, id| {
694 tcx.stability().local_deprecation_entry(id.expect_local())
700 struct Checker<'tcx> {
704 impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
705 type NestedFilter = nested_filter::OnlyBodies;
707 /// Because stability levels are scoped lexically, we want to walk
708 /// nested items in the context of the outer item, so enable
710 fn nested_visit_map(&mut self) -> Self::Map {
714 fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
716 hir::ItemKind::ExternCrate(_) => {
717 // compiler-generated `extern crate` items have a dummy span.
718 // `std` is still checked for the `restricted-std` feature.
719 if item.span.is_dummy() && item.ident.name != sym::std {
723 let Some(cnum) = self.tcx.extern_mod_stmt_cnum(item.owner_id.def_id) else {
726 let def_id = cnum.as_def_id();
727 self.tcx.check_stability(def_id, Some(item.hir_id()), item.span, None);
730 // For implementations of traits, check the stability of each item
731 // individually as it's possible to have a stable trait with unstable
733 hir::ItemKind::Impl(hir::Impl {
734 of_trait: Some(ref t),
740 let features = self.tcx.features();
741 if features.staged_api {
742 let attrs = self.tcx.hir().attrs(item.hir_id());
743 let (stab, const_stab, _) =
744 attr::find_stability(&self.tcx.sess, attrs, item.span);
746 // If this impl block has an #[unstable] attribute, give an
747 // error if all involved types and traits are stable, because
748 // it will have no effect.
749 // See: https://github.com/rust-lang/rust/issues/55436
750 if let Some((Stability { level: attr::Unstable { .. }, .. }, span)) = stab {
751 let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true };
753 c.visit_trait_ref(t);
755 self.tcx.struct_span_lint_hir(
756 INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
759 "an `#[unstable]` annotation here has no effect",
760 |lint| lint.note("see issue #55436 <https://github.com/rust-lang/rust/issues/55436> for more information")
765 // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable
766 // needs to have an error emitted.
767 if features.const_trait_impl
768 && *constness == hir::Constness::Const
769 && const_stab.map_or(false, |(stab, _)| stab.is_const_stable())
771 self.tcx.sess.emit_err(TraitImplConstStable { span: item.span });
775 for impl_item_ref in *items {
776 let impl_item = self.tcx.associated_item(impl_item_ref.id.owner_id);
778 if let Some(def_id) = impl_item.trait_item_def_id {
779 // Pass `None` to skip deprecation warnings.
780 self.tcx.check_stability(def_id, None, impl_item_ref.span, None);
787 intravisit::walk_item(self, item);
790 fn visit_path(&mut self, path: &hir::Path<'tcx>, id: hir::HirId) {
791 if let Some(def_id) = path.res.opt_def_id() {
792 let method_span = path.segments.last().map(|s| s.ident.span);
793 let item_is_allowed = self.tcx.check_stability_allow_unstable(
798 if is_unstable_reexport(self.tcx, id) {
805 let is_allowed_through_unstable_modules = |def_id| {
807 .lookup_stability(def_id)
808 .map(|stab| match stab.level {
809 StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
810 allowed_through_unstable_modules
817 if item_is_allowed && !is_allowed_through_unstable_modules(def_id) {
818 // Check parent modules stability as well if the item the path refers to is itself
819 // stable. We only emit warnings for unstable path segments if the item is stable
820 // or allowed because stability is often inherited, so the most common case is that
821 // both the segments and the item are unstable behind the same feature flag.
823 // We check here rather than in `visit_path_segment` to prevent visiting the last
824 // path segment twice
826 // We include special cases via #[rustc_allowed_through_unstable_modules] for items
827 // that were accidentally stabilized through unstable paths before this check was
828 // added, such as `core::intrinsics::transmute`
829 let parents = path.segments.iter().rev().skip(1);
830 for path_segment in parents {
831 if let Some(def_id) = path_segment.res.opt_def_id() {
832 // use `None` for id to prevent deprecation check
833 self.tcx.check_stability_allow_unstable(
838 if is_unstable_reexport(self.tcx, id) {
849 intravisit::walk_path(self, path)
853 /// Check whether a path is a `use` item that has been marked as unstable.
855 /// See issue #94972 for details on why this is a special case
856 fn is_unstable_reexport<'tcx>(tcx: TyCtxt<'tcx>, id: hir::HirId) -> bool {
857 // Get the LocalDefId so we can lookup the item to check the kind.
858 let Some(def_id) = tcx.hir().opt_local_def_id(id) else { return false; };
860 let Some(stab) = tcx.stability().local_stability(def_id) else {
864 if stab.level.is_stable() {
865 // The re-export is not marked as unstable, don't override
869 // If this is a path that isn't a use, we don't need to do anything special
870 if !matches!(tcx.hir().expect_item(def_id).kind, ItemKind::Use(..)) {
877 struct CheckTraitImplStable<'tcx> {
882 impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> {
883 fn visit_path(&mut self, path: &hir::Path<'tcx>, _id: hir::HirId) {
884 if let Some(def_id) = path.res.opt_def_id() {
885 if let Some(stab) = self.tcx.lookup_stability(def_id) {
886 self.fully_stable &= stab.level.is_stable();
889 intravisit::walk_path(self, path)
892 fn visit_trait_ref(&mut self, t: &'tcx TraitRef<'tcx>) {
893 if let Res::Def(DefKind::Trait, trait_did) = t.path.res {
894 if let Some(stab) = self.tcx.lookup_stability(trait_did) {
895 self.fully_stable &= stab.level.is_stable();
898 intravisit::walk_trait_ref(self, t)
901 fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) {
902 if let TyKind::Never = t.kind {
903 self.fully_stable = false;
905 if let TyKind::BareFn(f) = t.kind {
906 if rustc_target::spec::abi::is_stable(f.abi.name()).is_err() {
907 self.fully_stable = false;
910 intravisit::walk_ty(self, t)
913 fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) {
914 for ty in fd.inputs {
917 if let hir::FnRetTy::Return(output_ty) = fd.output {
918 match output_ty.kind {
919 TyKind::Never => {} // `-> !` is stable
920 _ => self.visit_ty(output_ty),
926 /// Given the list of enabled features that were not language features (i.e., that
927 /// were expected to be library features), and the list of features used from
928 /// libraries, identify activated features that don't exist and error about them.
929 pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
931 tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api;
933 let effective_visibilities = &tcx.effective_visibilities(());
934 let mut missing = MissingStabilityAnnotations { tcx, effective_visibilities };
935 missing.check_missing_stability(CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID));
936 tcx.hir().walk_toplevel_module(&mut missing);
937 tcx.hir().visit_all_item_likes_in_crate(&mut missing);
940 let declared_lang_features = &tcx.features().declared_lang_features;
941 let mut lang_features = FxHashSet::default();
942 for &(feature, span, since) in declared_lang_features {
943 if let Some(since) = since {
944 // Warn if the user has enabled an already-stable lang feature.
945 unnecessary_stable_feature_lint(tcx, span, feature, since);
947 if !lang_features.insert(feature) {
948 // Warn if the user enables a lang feature multiple times.
949 tcx.sess.emit_err(DuplicateFeatureErr { span, feature });
953 let declared_lib_features = &tcx.features().declared_lib_features;
954 let mut remaining_lib_features = FxIndexMap::default();
955 for (feature, span) in declared_lib_features {
956 if !tcx.sess.opts.unstable_features.is_nightly_build() {
957 tcx.sess.emit_err(FeatureOnlyOnNightly {
959 release_channel: env!("CFG_RELEASE_CHANNEL"),
962 if remaining_lib_features.contains_key(&feature) {
963 // Warn if the user enables a lib feature multiple times.
964 tcx.sess.emit_err(DuplicateFeatureErr { span: *span, feature: *feature });
966 remaining_lib_features.insert(feature, *span);
968 // `stdbuild` has special handling for `libc`, so we need to
969 // recognise the feature when building std.
970 // Likewise, libtest is handled specially, so `test` isn't
971 // available as we'd like it to be.
972 // FIXME: only remove `libc` when `stdbuild` is active.
973 // FIXME: remove special casing for `test`.
974 remaining_lib_features.remove(&sym::libc);
975 remaining_lib_features.remove(&sym::test);
977 /// For each feature in `defined_features`..
979 /// - If it is in `remaining_lib_features` (those features with `#![feature(..)]` attributes in
980 /// the current crate), check if it is stable (or partially stable) and thus an unnecessary
982 /// - If it is in `remaining_implications` (a feature that is referenced by an `implied_by`
983 /// from the current crate), then remove it from the remaining implications.
985 /// Once this function has been invoked for every feature (local crate and all extern crates),
988 /// - If features remain in `remaining_lib_features`, then the user has enabled a feature that
990 /// - If features remain in `remaining_implications`, the `implied_by` refers to a feature that
993 /// By structuring the code in this way: checking the features defined from each crate one at a
994 /// time, less loading from metadata is performed and thus compiler performance is improved.
995 fn check_features<'tcx>(
997 remaining_lib_features: &mut FxIndexMap<&Symbol, Span>,
998 remaining_implications: &mut FxHashMap<Symbol, Symbol>,
999 defined_features: &[(Symbol, Option<Symbol>)],
1000 all_implications: &FxHashMap<Symbol, Symbol>,
1002 for (feature, since) in defined_features {
1003 if let Some(since) = since && let Some(span) = remaining_lib_features.get(&feature) {
1004 // Warn if the user has enabled an already-stable lib feature.
1005 if let Some(implies) = all_implications.get(&feature) {
1006 unnecessary_partially_stable_feature_lint(tcx, *span, *feature, *implies, *since);
1008 unnecessary_stable_feature_lint(tcx, *span, *feature, *since);
1012 remaining_lib_features.remove(feature);
1014 // `feature` is the feature doing the implying, but `implied_by` is the feature with
1015 // the attribute that establishes this relationship. `implied_by` is guaranteed to be a
1016 // feature defined in the local crate because `remaining_implications` is only the
1017 // implications from this crate.
1018 remaining_implications.remove(feature);
1020 if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
1026 // All local crate implications need to have the feature that implies it confirmed to exist.
1027 let mut remaining_implications =
1028 tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone();
1030 // We always collect the lib features declared in the current crate, even if there are
1031 // no unknown features, because the collection also does feature attribute validation.
1032 let local_defined_features = tcx.lib_features(()).to_vec();
1033 if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() {
1034 // Loading the implications of all crates is unavoidable to be able to emit the partial
1035 // stabilization diagnostic, but it can be avoided when there are no
1036 // `remaining_lib_features`.
1037 let mut all_implications = remaining_implications.clone();
1038 for &cnum in tcx.crates(()) {
1039 all_implications.extend(tcx.stability_implications(cnum));
1044 &mut remaining_lib_features,
1045 &mut remaining_implications,
1046 local_defined_features.as_slice(),
1050 for &cnum in tcx.crates(()) {
1051 if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
1056 &mut remaining_lib_features,
1057 &mut remaining_implications,
1058 tcx.defined_lib_features(cnum).to_vec().as_slice(),
1064 for (feature, span) in remaining_lib_features {
1065 tcx.sess.emit_err(UnknownFeature { span, feature: *feature });
1068 for (implied_by, feature) in remaining_implications {
1069 let local_defined_features = tcx.lib_features(());
1070 let span = *local_defined_features
1073 .map(|(_, span)| span)
1074 .or_else(|| local_defined_features.unstable.get(&feature))
1075 .expect("feature that implied another does not exist");
1076 tcx.sess.emit_err(ImpliedFeatureNotExist { span, feature, implied_by });
1079 // FIXME(#44232): the `used_features` table no longer exists, so we
1080 // don't lint about unused features. We should re-enable this one day!
1083 fn unnecessary_partially_stable_feature_lint(
1090 tcx.struct_span_lint_hir(
1091 lint::builtin::STABLE_FEATURES,
1095 "the feature `{feature}` has been partially stabilized since {since} and is succeeded \
1096 by the feature `{implies}`"
1099 lint.span_suggestion(
1102 "if you are using features which are still unstable, change to using `{implies}`"
1105 Applicability::MaybeIncorrect,
1108 tcx.sess.source_map().span_extend_to_line(span),
1109 "if you are using features which are now stable, remove this line",
1111 Applicability::MaybeIncorrect,
1117 fn unnecessary_stable_feature_lint(
1123 if since.as_str() == VERSION_PLACEHOLDER {
1124 since = rust_version_symbol();
1126 tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, format!("the feature `{feature}` has been stable since {since} and no longer requires an attribute to enable"), |lint| {