1 //! A pass that annotates every item and method with its stability level,
2 //! propagating default levels lexically from parent to children ast nodes.
4 use rustc_ast::ast::Attribute;
5 use rustc_attr::{self as attr, ConstStability, Stability};
6 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
7 use rustc_errors::struct_span_err;
9 use rustc_hir::def::{DefKind, Res};
10 use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
11 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
12 use rustc_hir::{Generics, HirId, Item, StructField, Variant};
13 use rustc_middle::hir::map::Map;
14 use rustc_middle::middle::privacy::AccessLevels;
15 use rustc_middle::middle::stability::{DeprecationEntry, Index};
16 use rustc_middle::ty::query::Providers;
17 use rustc_middle::ty::TyCtxt;
18 use rustc_session::lint;
19 use rustc_session::parse::feature_err;
20 use rustc_session::Session;
21 use rustc_span::symbol::{sym, Symbol};
23 use rustc_trait_selection::traits::misc::can_type_implement_copy;
25 use std::cmp::Ordering;
26 use std::mem::replace;
27 use std::num::NonZeroU32;
31 // Annotation is required if not inherited from unstable parents
33 // Annotation is useless, reject it
35 // Annotation itself is useless, but it can be propagated to children
39 // A private tree-walker for producing an Index.
40 struct Annotator<'a, 'tcx> {
42 index: &'a mut Index<'tcx>,
43 parent_stab: Option<&'tcx Stability>,
44 parent_const_stab: Option<&'tcx ConstStability>,
45 parent_depr: Option<DeprecationEntry>,
49 impl<'a, 'tcx> Annotator<'a, 'tcx> {
50 // Determine the stability for a node based on its attributes and inherited
51 // stability. The stability is recorded in the index and used as the parent.
62 debug!("annotate(id = {:?}, attrs = {:?})", hir_id, attrs);
63 let mut did_error = false;
64 if !self.tcx.features().staged_api {
65 did_error = self.forbid_staged_api_attrs(hir_id, attrs);
68 let depr = if did_error {
71 attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp)
73 let mut is_deprecated = false;
74 if let Some(depr) = &depr {
77 if kind == AnnotationKind::Prohibited {
78 self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless");
81 // `Deprecation` is just two pointers, no need to intern it
82 let depr_entry = DeprecationEntry::local(depr.clone(), hir_id);
83 self.index.depr_map.insert(hir_id, depr_entry);
84 } else if let Some(parent_depr) = self.parent_depr.clone() {
86 info!("tagging child {:?} as deprecated from parent", hir_id);
87 self.index.depr_map.insert(hir_id, parent_depr);
90 if self.tcx.features().staged_api {
91 if let Some(..) = attrs.iter().find(|a| a.check_name(sym::deprecated)) {
92 self.tcx.sess.span_err(
94 "`#[deprecated]` cannot be used in staged API; \
95 use `#[rustc_deprecated]` instead",
99 self.recurse_with_stability_attrs(
100 depr.map(|d| DeprecationEntry::local(d, hir_id)),
108 let (stab, const_stab) = attr::find_stability(&self.tcx.sess.parse_sess, attrs, item_sp);
110 let const_stab = const_stab.map(|const_stab| {
111 let const_stab = self.tcx.intern_const_stability(const_stab);
112 self.index.const_stab_map.insert(hir_id, const_stab);
116 if const_stab.is_none() {
117 debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab);
118 if let Some(parent) = self.parent_const_stab {
119 if parent.level.is_unstable() {
120 self.index.const_stab_map.insert(hir_id, parent);
125 if depr.as_ref().map_or(false, |d| d.is_since_rustc_version) {
131 "rustc_deprecated attribute must be paired with \
132 either stable or unstable attribute"
138 let stab = stab.map(|stab| {
139 // Error if prohibited, or can't inherit anything from a container.
140 if kind == AnnotationKind::Prohibited
141 || (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated)
143 self.tcx.sess.span_err(item_sp, "This stability annotation is useless");
146 debug!("annotate: found {:?}", stab);
147 let stab = self.tcx.intern_stability(stab);
149 // Check if deprecated_since < stable_since. If it is,
150 // this is *almost surely* an accident.
151 if let (&Some(dep_since), &attr::Stable { since: stab_since }) =
152 (&depr.as_ref().and_then(|d| d.since), &stab.level)
154 // Explicit version of iter::order::lt to handle parse errors properly
155 for (dep_v, stab_v) in
156 dep_since.as_str().split('.').zip(stab_since.as_str().split('.'))
158 if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::<u64>(), stab_v.parse()) {
159 match dep_v.cmp(&stab_v) {
161 self.tcx.sess.span_err(
163 "An API can't be stabilized \
164 after it is deprecated",
168 Ordering::Equal => continue,
169 Ordering::Greater => break,
172 // Act like it isn't less because the question is now nonsensical,
173 // and this makes us not do anything else interesting.
174 self.tcx.sess.span_err(
176 "Invalid stability or deprecation \
184 self.index.stab_map.insert(hir_id, stab);
189 debug!("annotate: stab not found, parent = {:?}", self.parent_stab);
190 if let Some(stab) = self.parent_stab {
191 if stab.level.is_unstable() {
192 self.index.stab_map.insert(hir_id, stab);
197 self.recurse_with_stability_attrs(
198 depr.map(|d| DeprecationEntry::local(d, hir_id)),
205 fn recurse_with_stability_attrs(
207 depr: Option<DeprecationEntry>,
208 stab: Option<&'tcx Stability>,
209 const_stab: Option<&'tcx ConstStability>,
210 f: impl FnOnce(&mut Self),
212 // These will be `Some` if this item changes the corresponding stability attribute.
213 let mut replaced_parent_depr = None;
214 let mut replaced_parent_stab = None;
215 let mut replaced_parent_const_stab = None;
217 if let Some(depr) = depr {
218 replaced_parent_depr = Some(replace(&mut self.parent_depr, Some(depr)));
220 if let Some(stab) = stab {
221 replaced_parent_stab = Some(replace(&mut self.parent_stab, Some(stab)));
223 if let Some(const_stab) = const_stab {
224 replaced_parent_const_stab =
225 Some(replace(&mut self.parent_const_stab, Some(const_stab)));
230 if let Some(orig_parent_depr) = replaced_parent_depr {
231 self.parent_depr = orig_parent_depr;
233 if let Some(orig_parent_stab) = replaced_parent_stab {
234 self.parent_stab = orig_parent_stab;
236 if let Some(orig_parent_const_stab) = replaced_parent_const_stab {
237 self.parent_const_stab = orig_parent_const_stab;
241 // returns true if an error occurred, used to suppress some spurious errors
242 fn forbid_staged_api_attrs(&mut self, hir_id: HirId, attrs: &[Attribute]) -> bool {
243 // Emit errors for non-staged-api crates.
244 let unstable_attrs = [
247 sym::rustc_deprecated,
248 sym::rustc_const_unstable,
249 sym::rustc_const_stable,
251 let mut has_error = false;
253 let name = attr.name_or_empty();
254 if unstable_attrs.contains(&name) {
255 attr::mark_used(attr);
260 "stability attributes may not be used outside of the standard library",
267 // Propagate unstability. This can happen even for non-staged-api crates in case
268 // -Zforce-unstable-if-unmarked is set.
269 if let Some(stab) = self.parent_stab {
270 if stab.level.is_unstable() {
271 self.index.stab_map.insert(hir_id, stab);
279 impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
280 /// Because stability levels are scoped lexically, we want to walk
281 /// nested items in the context of the outer item, so enable
283 type Map = Map<'tcx>;
285 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
286 NestedVisitorMap::All(self.tcx.hir())
289 fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
290 let orig_in_trait_impl = self.in_trait_impl;
291 let mut kind = AnnotationKind::Required;
293 // Inherent impls and foreign modules serve only as containers for other items,
294 // they don't have their own stability. They still can be annotated as unstable
295 // and propagate this unstability to children, but this annotation is completely
296 // optional. They inherit stability from their parents when unannotated.
297 hir::ItemKind::Impl { of_trait: None, .. } | hir::ItemKind::ForeignMod(..) => {
298 self.in_trait_impl = false;
299 kind = AnnotationKind::Container;
301 hir::ItemKind::Impl { of_trait: Some(_), .. } => {
302 self.in_trait_impl = true;
304 hir::ItemKind::Struct(ref sd, _) => {
305 if let Some(ctor_hir_id) = sd.ctor_hir_id() {
306 self.annotate(ctor_hir_id, &i.attrs, i.span, AnnotationKind::Required, |_| {})
312 self.annotate(i.hir_id, &i.attrs, i.span, kind, |v| intravisit::walk_item(v, i));
313 self.in_trait_impl = orig_in_trait_impl;
316 fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
317 self.annotate(ti.hir_id, &ti.attrs, ti.span, AnnotationKind::Required, |v| {
318 intravisit::walk_trait_item(v, ti);
322 fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
324 if self.in_trait_impl { AnnotationKind::Prohibited } else { AnnotationKind::Required };
325 self.annotate(ii.hir_id, &ii.attrs, ii.span, kind, |v| {
326 intravisit::walk_impl_item(v, ii);
330 fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) {
331 self.annotate(var.id, &var.attrs, var.span, AnnotationKind::Required, |v| {
332 if let Some(ctor_hir_id) = var.data.ctor_hir_id() {
333 v.annotate(ctor_hir_id, &var.attrs, var.span, AnnotationKind::Required, |_| {});
336 intravisit::walk_variant(v, var, g, item_id)
340 fn visit_struct_field(&mut self, s: &'tcx StructField<'tcx>) {
341 self.annotate(s.hir_id, &s.attrs, s.span, AnnotationKind::Required, |v| {
342 intravisit::walk_struct_field(v, s);
346 fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
347 self.annotate(i.hir_id, &i.attrs, i.span, AnnotationKind::Required, |v| {
348 intravisit::walk_foreign_item(v, i);
352 fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
353 self.annotate(md.hir_id, &md.attrs, md.span, AnnotationKind::Required, |_| {});
357 struct MissingStabilityAnnotations<'tcx> {
359 access_levels: &'tcx AccessLevels,
362 impl<'tcx> MissingStabilityAnnotations<'tcx> {
363 fn check_missing_stability(&self, hir_id: HirId, span: Span) {
364 let stab = self.tcx.stability().local_stability(hir_id);
366 !self.tcx.sess.opts.test && stab.is_none() && self.access_levels.is_reachable(hir_id);
368 let def_id = self.tcx.hir().local_def_id(hir_id);
369 let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
370 self.tcx.sess.span_err(span, &format!("{} has missing stability attribute", descr));
375 impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
376 type Map = Map<'tcx>;
378 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
379 NestedVisitorMap::OnlyBodies(self.tcx.hir())
382 fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
384 // Inherent impls and foreign modules serve only as containers for other items,
385 // they don't have their own stability. They still can be annotated as unstable
386 // and propagate this unstability to children, but this annotation is completely
387 // optional. They inherit stability from their parents when unannotated.
388 hir::ItemKind::Impl { of_trait: None, .. } | hir::ItemKind::ForeignMod(..) => {}
390 _ => self.check_missing_stability(i.hir_id, i.span),
393 intravisit::walk_item(self, i)
396 fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
397 self.check_missing_stability(ti.hir_id, ti.span);
398 intravisit::walk_trait_item(self, ti);
401 fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
402 let impl_def_id = self.tcx.hir().local_def_id(self.tcx.hir().get_parent_item(ii.hir_id));
403 if self.tcx.impl_trait_ref(impl_def_id).is_none() {
404 self.check_missing_stability(ii.hir_id, ii.span);
406 intravisit::walk_impl_item(self, ii);
409 fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) {
410 self.check_missing_stability(var.id, var.span);
411 intravisit::walk_variant(self, var, g, item_id);
414 fn visit_struct_field(&mut self, s: &'tcx StructField<'tcx>) {
415 self.check_missing_stability(s.hir_id, s.span);
416 intravisit::walk_struct_field(self, s);
419 fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
420 self.check_missing_stability(i.hir_id, i.span);
421 intravisit::walk_foreign_item(self, i);
424 fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
425 self.check_missing_stability(md.hir_id, md.span);
429 fn new_index(tcx: TyCtxt<'tcx>) -> Index<'tcx> {
431 tcx.sess.opts.debugging_opts.force_unstable_if_unmarked || tcx.features().staged_api;
432 let mut staged_api = FxHashMap::default();
433 staged_api.insert(LOCAL_CRATE, is_staged_api);
434 let mut index = Index {
436 stab_map: Default::default(),
437 const_stab_map: Default::default(),
438 depr_map: Default::default(),
439 active_features: Default::default(),
442 let active_lib_features = &tcx.features().declared_lib_features;
443 let active_lang_features = &tcx.features().declared_lang_features;
445 // Put the active features into a map for quick lookup.
446 index.active_features = active_lib_features
449 .chain(active_lang_features.iter().map(|&(s, ..)| s))
453 let krate = tcx.hir().krate();
454 let mut annotator = Annotator {
458 parent_const_stab: None,
460 in_trait_impl: false,
463 // If the `-Z force-unstable-if-unmarked` flag is passed then we provide
464 // a parent stability annotation which indicates that this is private
465 // with the `rustc_private` feature. This is intended for use when
466 // compiling `librustc_*` crates themselves so we can leverage crates.io
467 // while maintaining the invariant that all sysroot crates are unstable
468 // by default and are unable to be used.
469 if tcx.sess.opts.debugging_opts.force_unstable_if_unmarked {
470 let reason = "this crate is being loaded from the sysroot, an \
471 unstable location; did you mean to load this crate \
472 from crates.io via `Cargo.toml` instead?";
473 let stability = tcx.intern_stability(Stability {
474 level: attr::StabilityLevel::Unstable {
475 reason: Some(Symbol::intern(reason)),
476 issue: NonZeroU32::new(27812),
479 feature: sym::rustc_private,
481 annotator.parent_stab = Some(stability);
488 AnnotationKind::Required,
489 |v| intravisit::walk_crate(v, krate),
495 /// Cross-references the feature names of unstable APIs with enabled
496 /// features and possibly prints errors.
497 fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
498 tcx.hir().visit_item_likes_in_module(module_def_id, &mut Checker { tcx }.as_deep_visitor());
501 pub(crate) fn provide(providers: &mut Providers) {
502 *providers = Providers { check_mod_unstable_api_usage, ..*providers };
503 providers.stability_index = |tcx, cnum| {
504 assert_eq!(cnum, LOCAL_CRATE);
509 struct Checker<'tcx> {
513 impl Visitor<'tcx> for Checker<'tcx> {
514 type Map = Map<'tcx>;
516 /// Because stability levels are scoped lexically, we want to walk
517 /// nested items in the context of the outer item, so enable
519 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
520 NestedVisitorMap::OnlyBodies(self.tcx.hir())
523 fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
525 hir::ItemKind::ExternCrate(_) => {
526 // compiler-generated `extern crate` items have a dummy span.
527 // `std` is still checked for the `restricted-std` feature.
528 if item.span.is_dummy() && item.ident.as_str() != "std" {
532 let def_id = self.tcx.hir().local_def_id(item.hir_id);
533 let cnum = match self.tcx.extern_mod_stmt_cnum(def_id) {
537 let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX };
538 self.tcx.check_stability(def_id, Some(item.hir_id), item.span);
541 // For implementations of traits, check the stability of each item
542 // individually as it's possible to have a stable trait with unstable
544 hir::ItemKind::Impl { of_trait: Some(ref t), items, .. } => {
545 if let Res::Def(DefKind::Trait, trait_did) = t.path.res {
546 for impl_item_ref in items {
547 let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
548 let trait_item_def_id = self
550 .associated_items(trait_did)
551 .filter_by_name_unhygienic(impl_item.ident.name)
553 .map(|item| item.def_id);
554 if let Some(def_id) = trait_item_def_id {
555 // Pass `None` to skip deprecation warnings.
556 self.tcx.check_stability(def_id, None, impl_item.span);
562 // There's no good place to insert stability check for non-Copy unions,
563 // so semi-randomly perform it here in stability.rs
564 hir::ItemKind::Union(..) if !self.tcx.features().untagged_unions => {
565 let def_id = self.tcx.hir().local_def_id(item.hir_id);
566 let adt_def = self.tcx.adt_def(def_id);
567 let ty = self.tcx.type_of(def_id);
569 if adt_def.has_dtor(self.tcx) {
571 &self.tcx.sess.parse_sess,
572 sym::untagged_unions,
574 "unions with `Drop` implementations are unstable",
578 let param_env = self.tcx.param_env(def_id);
579 if can_type_implement_copy(self.tcx, param_env, ty).is_err() {
581 &self.tcx.sess.parse_sess,
582 sym::untagged_unions,
584 "unions with non-`Copy` fields are unstable",
593 intravisit::walk_item(self, item);
596 fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, id: hir::HirId) {
597 if let Some(def_id) = path.res.opt_def_id() {
598 self.tcx.check_stability(def_id, Some(id), path.span)
600 intravisit::walk_path(self, path)
604 /// Given the list of enabled features that were not language features (i.e., that
605 /// were expected to be library features), and the list of features used from
606 /// libraries, identify activated features that don't exist and error about them.
607 pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
608 let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE);
610 if tcx.stability().staged_api[&LOCAL_CRATE] {
611 let krate = tcx.hir().krate();
612 let mut missing = MissingStabilityAnnotations { tcx, access_levels };
613 missing.check_missing_stability(hir::CRATE_HIR_ID, krate.item.span);
614 intravisit::walk_crate(&mut missing, krate);
615 krate.visit_all_item_likes(&mut missing.as_deep_visitor());
618 let declared_lang_features = &tcx.features().declared_lang_features;
619 let mut lang_features = FxHashSet::default();
620 for &(feature, span, since) in declared_lang_features {
621 if let Some(since) = since {
622 // Warn if the user has enabled an already-stable lang feature.
623 unnecessary_stable_feature_lint(tcx, span, feature, since);
625 if !lang_features.insert(feature) {
626 // Warn if the user enables a lang feature multiple times.
627 duplicate_feature_err(tcx.sess, span, feature);
631 let declared_lib_features = &tcx.features().declared_lib_features;
632 let mut remaining_lib_features = FxHashMap::default();
633 for (feature, span) in declared_lib_features {
634 if remaining_lib_features.contains_key(&feature) {
635 // Warn if the user enables a lib feature multiple times.
636 duplicate_feature_err(tcx.sess, *span, *feature);
638 remaining_lib_features.insert(feature, *span);
640 // `stdbuild` has special handling for `libc`, so we need to
641 // recognise the feature when building std.
642 // Likewise, libtest is handled specially, so `test` isn't
643 // available as we'd like it to be.
644 // FIXME: only remove `libc` when `stdbuild` is active.
645 // FIXME: remove special casing for `test`.
646 remaining_lib_features.remove(&sym::libc);
647 remaining_lib_features.remove(&sym::test);
649 let check_features = |remaining_lib_features: &mut FxHashMap<_, _>, defined_features: &[_]| {
650 for &(feature, since) in defined_features {
651 if let Some(since) = since {
652 if let Some(span) = remaining_lib_features.get(&feature) {
653 // Warn if the user has enabled an already-stable lib feature.
654 unnecessary_stable_feature_lint(tcx, *span, feature, since);
657 remaining_lib_features.remove(&feature);
658 if remaining_lib_features.is_empty() {
664 // We always collect the lib features declared in the current crate, even if there are
665 // no unknown features, because the collection also does feature attribute validation.
666 let local_defined_features = tcx.lib_features().to_vec();
667 if !remaining_lib_features.is_empty() {
668 check_features(&mut remaining_lib_features, &local_defined_features);
670 for &cnum in &*tcx.crates() {
671 if remaining_lib_features.is_empty() {
674 check_features(&mut remaining_lib_features, tcx.defined_lib_features(cnum));
678 for (feature, span) in remaining_lib_features {
679 struct_span_err!(tcx.sess, span, E0635, "unknown feature `{}`", feature).emit();
682 // FIXME(#44232): the `used_features` table no longer exists, so we
683 // don't lint about unused features. We should re-enable this one day!
686 fn unnecessary_stable_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol, since: Symbol) {
687 tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| {
689 "the feature `{}` has been stable since {} and no longer requires \
690 an attribute to enable",
697 fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) {
698 struct_span_err!(sess, span, E0636, "the feature `{}` has already been declared", feature)