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 as attr, rust_version_symbol, ConstStability, Stability, StabilityLevel, Unstable,
6 UnstableReason, VERSION_PLACEHOLDER,
8 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
9 use rustc_errors::{struct_span_err, Applicability};
11 use rustc_hir::def::{DefKind, Res};
12 use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
13 use rustc_hir::hir_id::CRATE_HIR_ID;
14 use rustc_hir::intravisit::{self, Visitor};
15 use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant};
16 use rustc_middle::hir::nested_filter;
17 use rustc_middle::middle::privacy::AccessLevels;
18 use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index};
19 use rustc_middle::ty::{query::Providers, TyCtxt};
20 use rustc_session::lint;
21 use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED};
22 use rustc_session::Session;
23 use rustc_span::symbol::{sym, Symbol};
25 use rustc_target::spec::abi::Abi;
27 use std::cmp::Ordering;
29 use std::mem::replace;
30 use std::num::NonZeroU32;
34 /// Annotation is required if not inherited from unstable parents.
36 /// Annotation is useless, reject it.
38 /// Deprecation annotation is useless, reject it. (Stability attribute is still required.)
39 DeprecationProhibited,
40 /// Annotation itself is useless, but it can be propagated to children.
44 /// Whether to inherit deprecation flags for nested items. In most cases, we do want to inherit
45 /// deprecation, because nested items rarely have individual deprecation attributes, and so
46 /// should be treated as deprecated if their parent is. However, default generic parameters
47 /// have separate deprecation attributes from their parents, so we do not wish to inherit
48 /// deprecation in this case. For example, inheriting deprecation for `T` in `Foo<T>`
49 /// would cause a duplicate warning arising from both `Foo` and `T` being deprecated.
51 enum InheritDeprecation {
56 impl InheritDeprecation {
57 fn yes(&self) -> bool {
58 matches!(self, InheritDeprecation::Yes)
62 /// Whether to inherit const stability flags for nested items. In most cases, we do not want to
63 /// inherit const stability: just because an enclosing `fn` is const-stable does not mean
64 /// all `extern` imports declared in it should be const-stable! However, trait methods
65 /// inherit const stability attributes from their parent and do not have their own.
66 enum InheritConstStability {
71 impl InheritConstStability {
72 fn yes(&self) -> bool {
73 matches!(self, InheritConstStability::Yes)
77 enum InheritStability {
82 impl InheritStability {
83 fn yes(&self) -> bool {
84 matches!(self, InheritStability::Yes)
88 /// A private tree-walker for producing an `Index`.
89 struct Annotator<'a, 'tcx> {
92 parent_stab: Option<Stability>,
93 parent_const_stab: Option<ConstStability>,
94 parent_depr: Option<DeprecationEntry>,
98 impl<'a, 'tcx> Annotator<'a, 'tcx> {
99 /// Determine the stability for a node based on its attributes and inherited stability. The
100 /// stability is recorded in the index and used as the parent. If the node is a function,
101 /// `fn_sig` is its signature.
106 fn_sig: Option<&'tcx hir::FnSig<'tcx>>,
107 kind: AnnotationKind,
108 inherit_deprecation: InheritDeprecation,
109 inherit_const_stability: InheritConstStability,
110 inherit_from_parent: InheritStability,
113 F: FnOnce(&mut Self),
115 let attrs = self.tcx.hir().attrs(self.tcx.hir().local_def_id_to_hir_id(def_id));
116 debug!("annotate(id = {:?}, attrs = {:?})", def_id, attrs);
118 let depr = attr::find_deprecation(&self.tcx.sess, attrs);
119 let mut is_deprecated = false;
120 if let Some((depr, span)) = &depr {
121 is_deprecated = true;
123 if kind == AnnotationKind::Prohibited || kind == AnnotationKind::DeprecationProhibited {
124 let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
125 self.tcx.struct_span_lint_hir(USELESS_DEPRECATED, hir_id, *span, |lint| {
126 lint.build("this `#[deprecated]` annotation has no effect")
127 .span_suggestion_short(
129 "remove the unnecessary deprecation attribute",
131 rustc_errors::Applicability::MachineApplicable,
137 // `Deprecation` is just two pointers, no need to intern it
138 let depr_entry = DeprecationEntry::local(*depr, def_id);
139 self.index.depr_map.insert(def_id, depr_entry);
140 } else if let Some(parent_depr) = self.parent_depr {
141 if inherit_deprecation.yes() {
142 is_deprecated = true;
143 info!("tagging child {:?} as deprecated from parent", def_id);
144 self.index.depr_map.insert(def_id, parent_depr);
148 if !self.tcx.features().staged_api {
149 // Propagate unstability. This can happen even for non-staged-api crates in case
150 // -Zforce-unstable-if-unmarked is set.
151 if let Some(stab) = self.parent_stab {
152 if inherit_deprecation.yes() && stab.is_unstable() {
153 self.index.stab_map.insert(def_id, stab);
157 self.recurse_with_stability_attrs(
158 depr.map(|(d, _)| DeprecationEntry::local(d, def_id)),
166 let (stab, const_stab, body_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp);
167 let mut const_span = None;
169 let const_stab = const_stab.map(|(const_stab, const_span_node)| {
170 self.index.const_stab_map.insert(def_id, const_stab);
171 const_span = Some(const_span_node);
175 // If the current node is a function, has const stability attributes and if it doesn not have an intrinsic ABI,
176 // check if the function/method is const or the parent impl block is const
177 if let (Some(const_span), Some(fn_sig)) = (const_span, fn_sig) {
178 if fn_sig.header.abi != Abi::RustIntrinsic
179 && fn_sig.header.abi != Abi::PlatformIntrinsic
180 && !fn_sig.header.is_const()
182 if !self.in_trait_impl
183 || (self.in_trait_impl && !self.tcx.is_const_fn_raw(def_id.to_def_id()))
185 missing_const_err(&self.tcx.sess, fn_sig.span, const_span);
190 // `impl const Trait for Type` items forward their const stability to their
191 // immediate children.
192 if const_stab.is_none() {
193 debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab);
194 if let Some(parent) = self.parent_const_stab {
195 if parent.is_const_unstable() {
196 self.index.const_stab_map.insert(def_id, parent);
201 if let Some((rustc_attr::Deprecation { is_since_rustc_version: true, .. }, span)) = &depr {
207 "deprecated attribute must be paired with \
208 either stable or unstable attribute"
214 if let Some((body_stab, _span)) = body_stab {
215 // FIXME: check that this item can have body stability
217 self.index.default_body_stab_map.insert(def_id, body_stab);
218 debug!(?self.index.default_body_stab_map);
221 let stab = stab.map(|(stab, span)| {
222 // Error if prohibited, or can't inherit anything from a container.
223 if kind == AnnotationKind::Prohibited
224 || (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated)
226 self.tcx.sess.struct_span_err(span,"this stability annotation is useless")
227 .span_label(span, "useless stability annotation")
228 .span_label(item_sp, "the stability attribute annotates this item")
232 debug!("annotate: found {:?}", stab);
234 // Check if deprecated_since < stable_since. If it is,
235 // this is *almost surely* an accident.
236 if let (&Some(dep_since), &attr::Stable { since: stab_since, .. }) =
237 (&depr.as_ref().and_then(|(d, _)| d.since), &stab.level)
239 // Explicit version of iter::order::lt to handle parse errors properly
240 for (dep_v, stab_v) in
241 iter::zip(dep_since.as_str().split('.'), stab_since.as_str().split('.'))
243 match stab_v.parse::<u64>() {
245 self.tcx.sess.struct_span_err(span, "invalid stability version found")
246 .span_label(span, "invalid stability version")
247 .span_label(item_sp, "the stability attribute annotates this item")
251 Ok(stab_vp) => match dep_v.parse::<u64>() {
252 Ok(dep_vp) => match dep_vp.cmp(&stab_vp) {
254 self.tcx.sess.struct_span_err(span, "an API can't be stabilized after it is deprecated")
255 .span_label(span, "invalid version")
256 .span_label(item_sp, "the stability attribute annotates this item")
260 Ordering::Equal => continue,
261 Ordering::Greater => break,
265 self.tcx.sess.struct_span_err(span, "invalid deprecation version found")
266 .span_label(span, "invalid deprecation version")
267 .span_label(item_sp, "the stability attribute annotates this item")
277 if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } = stab {
278 self.index.implications.insert(implied_by, feature);
281 self.index.stab_map.insert(def_id, stab);
286 debug!("annotate: stab not found, parent = {:?}", self.parent_stab);
287 if let Some(stab) = self.parent_stab {
288 if inherit_deprecation.yes() && stab.is_unstable() || inherit_from_parent.yes() {
289 self.index.stab_map.insert(def_id, stab);
294 self.recurse_with_stability_attrs(
295 depr.map(|(d, _)| DeprecationEntry::local(d, def_id)),
297 if inherit_const_stability.yes() { const_stab } else { None },
302 fn recurse_with_stability_attrs(
304 depr: Option<DeprecationEntry>,
305 stab: Option<Stability>,
306 const_stab: Option<ConstStability>,
307 f: impl FnOnce(&mut Self),
309 // These will be `Some` if this item changes the corresponding stability attribute.
310 let mut replaced_parent_depr = None;
311 let mut replaced_parent_stab = None;
312 let mut replaced_parent_const_stab = None;
314 if let Some(depr) = depr {
315 replaced_parent_depr = Some(replace(&mut self.parent_depr, Some(depr)));
317 if let Some(stab) = stab {
318 replaced_parent_stab = Some(replace(&mut self.parent_stab, Some(stab)));
320 if let Some(const_stab) = const_stab {
321 replaced_parent_const_stab =
322 Some(replace(&mut self.parent_const_stab, Some(const_stab)));
327 if let Some(orig_parent_depr) = replaced_parent_depr {
328 self.parent_depr = orig_parent_depr;
330 if let Some(orig_parent_stab) = replaced_parent_stab {
331 self.parent_stab = orig_parent_stab;
333 if let Some(orig_parent_const_stab) = replaced_parent_const_stab {
334 self.parent_const_stab = orig_parent_const_stab;
339 impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
340 /// Because stability levels are scoped lexically, we want to walk
341 /// nested items in the context of the outer item, so enable
343 type NestedFilter = nested_filter::All;
345 fn nested_visit_map(&mut self) -> Self::Map {
349 fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
350 let orig_in_trait_impl = self.in_trait_impl;
351 let mut kind = AnnotationKind::Required;
352 let mut const_stab_inherit = InheritConstStability::No;
353 let mut fn_sig = None;
356 // Inherent impls and foreign modules serve only as containers for other items,
357 // they don't have their own stability. They still can be annotated as unstable
358 // and propagate this instability to children, but this annotation is completely
359 // optional. They inherit stability from their parents when unannotated.
360 hir::ItemKind::Impl(hir::Impl { of_trait: None, .. })
361 | hir::ItemKind::ForeignMod { .. } => {
362 self.in_trait_impl = false;
363 kind = AnnotationKind::Container;
365 hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => {
366 self.in_trait_impl = true;
367 kind = AnnotationKind::DeprecationProhibited;
368 const_stab_inherit = InheritConstStability::Yes;
370 hir::ItemKind::Struct(ref sd, _) => {
371 if let Some(ctor_hir_id) = sd.ctor_hir_id() {
373 self.tcx.hir().local_def_id(ctor_hir_id),
376 AnnotationKind::Required,
377 InheritDeprecation::Yes,
378 InheritConstStability::No,
379 InheritStability::Yes,
384 hir::ItemKind::Fn(ref item_fn_sig, _, _) => {
385 fn_sig = Some(item_fn_sig);
395 InheritDeprecation::Yes,
397 InheritStability::No,
398 |v| intravisit::walk_item(v, i),
400 self.in_trait_impl = orig_in_trait_impl;
403 fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
404 let fn_sig = match ti.kind {
405 hir::TraitItemKind::Fn(ref fn_sig, _) => Some(fn_sig),
413 AnnotationKind::Required,
414 InheritDeprecation::Yes,
415 InheritConstStability::No,
416 InheritStability::No,
418 intravisit::walk_trait_item(v, ti);
423 fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
425 if self.in_trait_impl { AnnotationKind::Prohibited } else { AnnotationKind::Required };
427 let fn_sig = match ii.kind {
428 hir::ImplItemKind::Fn(ref fn_sig, _) => Some(fn_sig),
437 InheritDeprecation::Yes,
438 InheritConstStability::No,
439 InheritStability::No,
441 intravisit::walk_impl_item(v, ii);
446 fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) {
448 self.tcx.hir().local_def_id(var.id),
451 AnnotationKind::Required,
452 InheritDeprecation::Yes,
453 InheritConstStability::No,
454 InheritStability::Yes,
456 if let Some(ctor_hir_id) = var.data.ctor_hir_id() {
458 v.tcx.hir().local_def_id(ctor_hir_id),
461 AnnotationKind::Required,
462 InheritDeprecation::Yes,
463 InheritConstStability::No,
464 InheritStability::Yes,
469 intravisit::walk_variant(v, var)
474 fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) {
476 self.tcx.hir().local_def_id(s.hir_id),
479 AnnotationKind::Required,
480 InheritDeprecation::Yes,
481 InheritConstStability::No,
482 InheritStability::Yes,
484 intravisit::walk_field_def(v, s);
489 fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
494 AnnotationKind::Required,
495 InheritDeprecation::Yes,
496 InheritConstStability::No,
497 InheritStability::No,
499 intravisit::walk_foreign_item(v, i);
504 fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
505 let kind = match &p.kind {
506 // Allow stability attributes on default generic arguments.
507 hir::GenericParamKind::Type { default: Some(_), .. }
508 | hir::GenericParamKind::Const { default: Some(_), .. } => AnnotationKind::Container,
509 _ => AnnotationKind::Prohibited,
513 self.tcx.hir().local_def_id(p.hir_id),
517 InheritDeprecation::No,
518 InheritConstStability::No,
519 InheritStability::No,
521 intravisit::walk_generic_param(v, p);
527 struct MissingStabilityAnnotations<'tcx> {
529 access_levels: &'tcx AccessLevels,
532 impl<'tcx> MissingStabilityAnnotations<'tcx> {
533 fn check_missing_stability(&self, def_id: LocalDefId, span: Span) {
534 let stab = self.tcx.stability().local_stability(def_id);
535 if !self.tcx.sess.opts.test && stab.is_none() && self.access_levels.is_reachable(def_id) {
536 let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
537 self.tcx.sess.span_err(span, &format!("{} has missing stability attribute", descr));
541 fn check_missing_const_stability(&self, def_id: LocalDefId, span: Span) {
542 if !self.tcx.features().staged_api {
546 let is_const = self.tcx.is_const_fn(def_id.to_def_id())
547 || self.tcx.is_const_trait_impl_raw(def_id.to_def_id());
550 .lookup_stability(def_id)
551 .map_or(false, |stability| stability.level.is_stable());
552 let missing_const_stability_attribute = self.tcx.lookup_const_stability(def_id).is_none();
553 let is_reachable = self.access_levels.is_reachable(def_id);
555 if is_const && is_stable && missing_const_stability_attribute && is_reachable {
556 let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
557 self.tcx.sess.span_err(span, &format!("{descr} has missing const stability attribute"));
562 impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
563 type NestedFilter = nested_filter::OnlyBodies;
565 fn nested_visit_map(&mut self) -> Self::Map {
569 fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
570 // Inherent impls and foreign modules serve only as containers for other items,
571 // they don't have their own stability. They still can be annotated as unstable
572 // and propagate this instability to children, but this annotation is completely
573 // optional. They inherit stability from their parents when unannotated.
576 hir::ItemKind::Impl(hir::Impl { of_trait: None, .. })
577 | hir::ItemKind::ForeignMod { .. }
579 self.check_missing_stability(i.def_id, i.span);
582 // Ensure stable `const fn` have a const stability attribute.
583 self.check_missing_const_stability(i.def_id, i.span);
585 intravisit::walk_item(self, i)
588 fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
589 self.check_missing_stability(ti.def_id, ti.span);
590 intravisit::walk_trait_item(self, ti);
593 fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
594 let impl_def_id = self.tcx.hir().get_parent_item(ii.hir_id());
595 if self.tcx.impl_trait_ref(impl_def_id).is_none() {
596 self.check_missing_stability(ii.def_id, ii.span);
597 self.check_missing_const_stability(ii.def_id, ii.span);
599 intravisit::walk_impl_item(self, ii);
602 fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) {
603 self.check_missing_stability(self.tcx.hir().local_def_id(var.id), var.span);
604 if let Some(ctor_hir_id) = var.data.ctor_hir_id() {
605 self.check_missing_stability(self.tcx.hir().local_def_id(ctor_hir_id), var.span);
607 intravisit::walk_variant(self, var);
610 fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) {
611 self.check_missing_stability(self.tcx.hir().local_def_id(s.hir_id), s.span);
612 intravisit::walk_field_def(self, s);
615 fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
616 self.check_missing_stability(i.def_id, i.span);
617 intravisit::walk_foreign_item(self, i);
619 // Note that we don't need to `check_missing_stability` for default generic parameters,
620 // as we assume that any default generic parameters without attributes are automatically
621 // stable (assuming they have not inherited instability from their parent).
624 fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index {
625 let mut index = Index {
626 stab_map: Default::default(),
627 const_stab_map: Default::default(),
628 default_body_stab_map: Default::default(),
629 depr_map: Default::default(),
630 implications: Default::default(),
634 let mut annotator = Annotator {
638 parent_const_stab: None,
640 in_trait_impl: false,
643 // If the `-Z force-unstable-if-unmarked` flag is passed then we provide
644 // a parent stability annotation which indicates that this is private
645 // with the `rustc_private` feature. This is intended for use when
646 // compiling `librustc_*` crates themselves so we can leverage crates.io
647 // while maintaining the invariant that all sysroot crates are unstable
648 // by default and are unable to be used.
649 if tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
650 let stability = Stability {
651 level: attr::StabilityLevel::Unstable {
652 reason: UnstableReason::Default,
653 issue: NonZeroU32::new(27812),
657 feature: sym::rustc_private,
659 annotator.parent_stab = Some(stability);
664 tcx.hir().span(CRATE_HIR_ID),
666 AnnotationKind::Required,
667 InheritDeprecation::Yes,
668 InheritConstStability::No,
669 InheritStability::No,
670 |v| tcx.hir().walk_toplevel_module(v),
676 /// Cross-references the feature names of unstable APIs with enabled
677 /// features and possibly prints errors.
678 fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
679 tcx.hir().visit_item_likes_in_module(module_def_id, &mut Checker { tcx });
682 pub(crate) fn provide(providers: &mut Providers) {
683 *providers = Providers {
684 check_mod_unstable_api_usage,
686 stability_implications: |tcx, _| tcx.stability().implications.clone(),
687 lookup_stability: |tcx, id| tcx.stability().local_stability(id.expect_local()),
688 lookup_const_stability: |tcx, id| tcx.stability().local_const_stability(id.expect_local()),
689 lookup_default_body_stability: |tcx, id| {
690 tcx.stability().local_default_body_stability(id.expect_local())
692 lookup_deprecation_entry: |tcx, id| {
693 tcx.stability().local_deprecation_entry(id.expect_local())
699 struct Checker<'tcx> {
703 impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
704 type NestedFilter = nested_filter::OnlyBodies;
706 /// Because stability levels are scoped lexically, we want to walk
707 /// nested items in the context of the outer item, so enable
709 fn nested_visit_map(&mut self) -> Self::Map {
713 fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
715 hir::ItemKind::ExternCrate(_) => {
716 // compiler-generated `extern crate` items have a dummy span.
717 // `std` is still checked for the `restricted-std` feature.
718 if item.span.is_dummy() && item.ident.name != sym::std {
722 let Some(cnum) = self.tcx.extern_mod_stmt_cnum(item.def_id) else {
725 let def_id = cnum.as_def_id();
726 self.tcx.check_stability(def_id, Some(item.hir_id()), item.span, None);
729 // For implementations of traits, check the stability of each item
730 // individually as it's possible to have a stable trait with unstable
732 hir::ItemKind::Impl(hir::Impl {
733 of_trait: Some(ref t),
739 let features = self.tcx.features();
740 if features.staged_api {
741 let attrs = self.tcx.hir().attrs(item.hir_id());
742 let (stab, const_stab, _) =
743 attr::find_stability(&self.tcx.sess, attrs, item.span);
745 // If this impl block has an #[unstable] attribute, give an
746 // error if all involved types and traits are stable, because
747 // it will have no effect.
748 // See: https://github.com/rust-lang/rust/issues/55436
749 if let Some((Stability { level: attr::Unstable { .. }, .. }, span)) = stab {
750 let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true };
752 c.visit_trait_ref(t);
754 self.tcx.struct_span_lint_hir(
755 INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
759 .build("an `#[unstable]` annotation here has no effect")
760 .note("see issue #55436 <https://github.com/rust-lang/rust/issues/55436> for more information")
766 // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable
767 // needs to have an error emitted.
768 if features.const_trait_impl
769 && *constness == hir::Constness::Const
770 && const_stab.map_or(false, |(stab, _)| stab.is_const_stable())
774 .struct_span_err(item.span, "trait implementations cannot be const stable yet")
775 .note("see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information")
780 for impl_item_ref in *items {
781 let impl_item = self.tcx.associated_item(impl_item_ref.id.def_id);
783 if let Some(def_id) = impl_item.trait_item_def_id {
784 // Pass `None` to skip deprecation warnings.
785 self.tcx.check_stability(def_id, None, impl_item_ref.span, None);
792 intravisit::walk_item(self, item);
795 fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, id: hir::HirId) {
796 if let Some(def_id) = path.res.opt_def_id() {
797 let method_span = path.segments.last().map(|s| s.ident.span);
798 let item_is_allowed = self.tcx.check_stability_allow_unstable(
803 if is_unstable_reexport(self.tcx, id) {
810 let is_allowed_through_unstable_modules = |def_id| {
812 .lookup_stability(def_id)
813 .map(|stab| match stab.level {
814 StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
815 allowed_through_unstable_modules
822 if item_is_allowed && !is_allowed_through_unstable_modules(def_id) {
823 // Check parent modules stability as well if the item the path refers to is itself
824 // stable. We only emit warnings for unstable path segments if the item is stable
825 // or allowed because stability is often inherited, so the most common case is that
826 // both the segments and the item are unstable behind the same feature flag.
828 // We check here rather than in `visit_path_segment` to prevent visiting the last
829 // path segment twice
831 // We include special cases via #[rustc_allowed_through_unstable_modules] for items
832 // that were accidentally stabilized through unstable paths before this check was
833 // added, such as `core::intrinsics::transmute`
834 let parents = path.segments.iter().rev().skip(1);
835 for path_segment in parents {
836 if let Some(def_id) = path_segment.res.opt_def_id() {
837 // use `None` for id to prevent deprecation check
838 self.tcx.check_stability_allow_unstable(
843 if is_unstable_reexport(self.tcx, id) {
854 intravisit::walk_path(self, path)
858 /// Check whether a path is a `use` item that has been marked as unstable.
860 /// See issue #94972 for details on why this is a special case
861 fn is_unstable_reexport<'tcx>(tcx: TyCtxt<'tcx>, id: hir::HirId) -> bool {
862 // Get the LocalDefId so we can lookup the item to check the kind.
863 let Some(def_id) = tcx.hir().opt_local_def_id(id) else { return false; };
865 let Some(stab) = tcx.stability().local_stability(def_id) else {
869 if stab.level.is_stable() {
870 // The re-export is not marked as unstable, don't override
874 // If this is a path that isn't a use, we don't need to do anything special
875 if !matches!(tcx.hir().item(hir::ItemId { def_id }).kind, ItemKind::Use(..)) {
882 struct CheckTraitImplStable<'tcx> {
887 impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> {
888 fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _id: hir::HirId) {
889 if let Some(def_id) = path.res.opt_def_id() {
890 if let Some(stab) = self.tcx.lookup_stability(def_id) {
891 self.fully_stable &= stab.level.is_stable();
894 intravisit::walk_path(self, path)
897 fn visit_trait_ref(&mut self, t: &'tcx TraitRef<'tcx>) {
898 if let Res::Def(DefKind::Trait, trait_did) = t.path.res {
899 if let Some(stab) = self.tcx.lookup_stability(trait_did) {
900 self.fully_stable &= stab.level.is_stable();
903 intravisit::walk_trait_ref(self, t)
906 fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) {
907 if let TyKind::Never = t.kind {
908 self.fully_stable = false;
910 intravisit::walk_ty(self, t)
914 /// Given the list of enabled features that were not language features (i.e., that
915 /// were expected to be library features), and the list of features used from
916 /// libraries, identify activated features that don't exist and error about them.
917 pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
919 tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api;
921 let access_levels = &tcx.privacy_access_levels(());
922 let mut missing = MissingStabilityAnnotations { tcx, access_levels };
923 missing.check_missing_stability(CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID));
924 tcx.hir().walk_toplevel_module(&mut missing);
925 tcx.hir().visit_all_item_likes_in_crate(&mut missing);
928 let declared_lang_features = &tcx.features().declared_lang_features;
929 let mut lang_features = FxHashSet::default();
930 for &(feature, span, since) in declared_lang_features {
931 if let Some(since) = since {
932 // Warn if the user has enabled an already-stable lang feature.
933 unnecessary_stable_feature_lint(tcx, span, feature, since);
935 if !lang_features.insert(feature) {
936 // Warn if the user enables a lang feature multiple times.
937 duplicate_feature_err(tcx.sess, span, feature);
941 let declared_lib_features = &tcx.features().declared_lib_features;
942 let mut remaining_lib_features = FxIndexMap::default();
943 for (feature, span) in declared_lib_features {
944 if !tcx.sess.opts.unstable_features.is_nightly_build() {
949 "`#![feature]` may not be used on the {} release channel",
950 env!("CFG_RELEASE_CHANNEL")
954 if remaining_lib_features.contains_key(&feature) {
955 // Warn if the user enables a lib feature multiple times.
956 duplicate_feature_err(tcx.sess, *span, *feature);
958 remaining_lib_features.insert(feature, *span);
960 // `stdbuild` has special handling for `libc`, so we need to
961 // recognise the feature when building std.
962 // Likewise, libtest is handled specially, so `test` isn't
963 // available as we'd like it to be.
964 // FIXME: only remove `libc` when `stdbuild` is active.
965 // FIXME: remove special casing for `test`.
966 remaining_lib_features.remove(&sym::libc);
967 remaining_lib_features.remove(&sym::test);
969 /// For each feature in `defined_features`..
971 /// - If it is in `remaining_lib_features` (those features with `#![feature(..)]` attributes in
972 /// the current crate), check if it is stable (or partially stable) and thus an unnecessary
974 /// - If it is in `remaining_implications` (a feature that is referenced by an `implied_by`
975 /// from the current crate), then remove it from the remaining implications.
977 /// Once this function has been invoked for every feature (local crate and all extern crates),
980 /// - If features remain in `remaining_lib_features`, then the user has enabled a feature that
982 /// - If features remain in `remaining_implications`, the `implied_by` refers to a feature that
985 /// By structuring the code in this way: checking the features defined from each crate one at a
986 /// time, less loading from metadata is performed and thus compiler performance is improved.
987 fn check_features<'tcx>(
989 remaining_lib_features: &mut FxIndexMap<&Symbol, Span>,
990 remaining_implications: &mut FxHashMap<Symbol, Symbol>,
991 defined_features: &[(Symbol, Option<Symbol>)],
992 all_implications: &FxHashMap<Symbol, Symbol>,
994 for (feature, since) in defined_features {
995 if let Some(since) = since && let Some(span) = remaining_lib_features.get(&feature) {
996 // Warn if the user has enabled an already-stable lib feature.
997 if let Some(implies) = all_implications.get(&feature) {
998 unnecessary_partially_stable_feature_lint(tcx, *span, *feature, *implies, *since);
1000 unnecessary_stable_feature_lint(tcx, *span, *feature, *since);
1004 remaining_lib_features.remove(feature);
1006 // `feature` is the feature doing the implying, but `implied_by` is the feature with
1007 // the attribute that establishes this relationship. `implied_by` is guaranteed to be a
1008 // feature defined in the local crate because `remaining_implications` is only the
1009 // implications from this crate.
1010 remaining_implications.remove(feature);
1012 if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
1018 // All local crate implications need to have the feature that implies it confirmed to exist.
1019 let mut remaining_implications =
1020 tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone();
1022 // We always collect the lib features declared in the current crate, even if there are
1023 // no unknown features, because the collection also does feature attribute validation.
1024 let local_defined_features = tcx.lib_features(()).to_vec();
1025 if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() {
1026 // Loading the implications of all crates is unavoidable to be able to emit the partial
1027 // stabilization diagnostic, but it can be avoided when there are no
1028 // `remaining_lib_features`.
1029 let mut all_implications = remaining_implications.clone();
1030 for &cnum in tcx.crates(()) {
1031 all_implications.extend(tcx.stability_implications(cnum));
1036 &mut remaining_lib_features,
1037 &mut remaining_implications,
1038 local_defined_features.as_slice(),
1042 for &cnum in tcx.crates(()) {
1043 if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
1048 &mut remaining_lib_features,
1049 &mut remaining_implications,
1050 tcx.defined_lib_features(cnum).to_vec().as_slice(),
1056 for (feature, span) in remaining_lib_features {
1057 struct_span_err!(tcx.sess, span, E0635, "unknown feature `{}`", feature).emit();
1060 for (implied_by, feature) in remaining_implications {
1061 let local_defined_features = tcx.lib_features(());
1062 let span = local_defined_features
1065 .map(|(_, span)| span)
1066 .or_else(|| local_defined_features.unstable.get(&feature))
1067 .expect("feature that implied another does not exist");
1071 format!("feature `{implied_by}` implying `{feature}` does not exist"),
1076 // FIXME(#44232): the `used_features` table no longer exists, so we
1077 // don't lint about unused features. We should re-enable this one day!
1080 fn unnecessary_partially_stable_feature_lint(
1087 tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| {
1088 lint.build(&format!(
1089 "the feature `{feature}` has been partially stabilized since {since} and is succeeded \
1090 by the feature `{implies}`"
1095 "if you are using features which are still unstable, change to using `{implies}`"
1098 Applicability::MaybeIncorrect,
1101 tcx.sess.source_map().span_extend_to_line(span),
1102 "if you are using features which are now stable, remove this line",
1104 Applicability::MaybeIncorrect,
1110 fn unnecessary_stable_feature_lint(
1116 if since.as_str() == VERSION_PLACEHOLDER {
1117 since = rust_version_symbol();
1119 tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| {
1120 lint.build(&format!(
1121 "the feature `{feature}` has been stable since {since} and no longer requires an \
1122 attribute to enable",
1128 fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) {
1129 struct_span_err!(sess, span, E0636, "the feature `{}` has already been declared", feature)
1133 fn missing_const_err(session: &Session, fn_sig_span: Span, const_span: Span) {
1134 const ERROR_MSG: &'static str = "attributes `#[rustc_const_unstable]` \
1135 and `#[rustc_const_stable]` require \
1136 the function or method to be `const`";
1139 .struct_span_err(fn_sig_span, ERROR_MSG)
1140 .span_help(fn_sig_span, "make the function or method const")
1141 .span_label(const_span, "attribute specified here")