rustc_passes/
stability.rs

1//! A pass that annotates every item and method with its stability level,
2//! propagating default levels lexically from parent to children ast nodes.
3
4use std::mem::replace;
5use std::num::NonZero;
6
7use rustc_ast_lowering::stability::extern_abi_stability;
8use rustc_attr_data_structures::{
9    self as attrs, AttributeKind, ConstStability, DeprecatedSince, PartialConstStability,
10    Stability, StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER, find_attr,
11};
12use rustc_data_structures::fx::FxIndexMap;
13use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
14use rustc_feature::{ACCEPTED_LANG_FEATURES, EnabledLangFeature, EnabledLibFeature};
15use rustc_hir::def::{DefKind, Res};
16use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId};
17use rustc_hir::hir_id::CRATE_HIR_ID;
18use rustc_hir::intravisit::{self, Visitor, VisitorExt};
19use rustc_hir::{self as hir, AmbigArg, FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant};
20use rustc_middle::hir::nested_filter;
21use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures};
22use rustc_middle::middle::privacy::EffectiveVisibilities;
23use rustc_middle::middle::stability::{
24    AllowUnstable, Deprecated, DeprecationEntry, EvalResult, Index,
25};
26use rustc_middle::query::Providers;
27use rustc_middle::ty::TyCtxt;
28use rustc_middle::ty::print::with_no_trimmed_paths;
29use rustc_session::lint;
30use rustc_session::lint::builtin::{
31    DEPRECATED, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED,
32};
33use rustc_span::{Span, Symbol, sym};
34use tracing::{debug, info};
35
36use crate::errors;
37
38#[derive(PartialEq)]
39enum AnnotationKind {
40    /// Annotation is required if not inherited from unstable parents.
41    Required,
42    /// Annotation is useless, reject it.
43    Prohibited,
44    /// Deprecation annotation is useless, reject it. (Stability attribute is still required.)
45    DeprecationProhibited,
46    /// Annotation itself is useless, but it can be propagated to children.
47    Container,
48}
49
50/// Whether to inherit deprecation flags for nested items. In most cases, we do want to inherit
51/// deprecation, because nested items rarely have individual deprecation attributes, and so
52/// should be treated as deprecated if their parent is. However, default generic parameters
53/// have separate deprecation attributes from their parents, so we do not wish to inherit
54/// deprecation in this case. For example, inheriting deprecation for `T` in `Foo<T>`
55/// would cause a duplicate warning arising from both `Foo` and `T` being deprecated.
56#[derive(Clone)]
57enum InheritDeprecation {
58    Yes,
59    No,
60}
61
62impl InheritDeprecation {
63    fn yes(&self) -> bool {
64        matches!(self, InheritDeprecation::Yes)
65    }
66}
67
68/// Whether to inherit const stability flags for nested items. In most cases, we do not want to
69/// inherit const stability: just because an enclosing `fn` is const-stable does not mean
70/// all `extern` imports declared in it should be const-stable! However, trait methods
71/// inherit const stability attributes from their parent and do not have their own.
72enum InheritConstStability {
73    Yes,
74    No,
75}
76
77impl InheritConstStability {
78    fn yes(&self) -> bool {
79        matches!(self, InheritConstStability::Yes)
80    }
81}
82
83enum InheritStability {
84    Yes,
85    No,
86}
87
88impl InheritStability {
89    fn yes(&self) -> bool {
90        matches!(self, InheritStability::Yes)
91    }
92}
93
94/// A private tree-walker for producing an `Index`.
95struct Annotator<'a, 'tcx> {
96    tcx: TyCtxt<'tcx>,
97    index: &'a mut Index,
98    parent_stab: Option<Stability>,
99    parent_const_stab: Option<ConstStability>,
100    parent_depr: Option<DeprecationEntry>,
101    in_trait_impl: bool,
102}
103
104impl<'a, 'tcx> Annotator<'a, 'tcx> {
105    /// Determine the stability for a node based on its attributes and inherited stability. The
106    /// stability is recorded in the index and used as the parent. If the node is a function,
107    /// `fn_sig` is its signature.
108    fn annotate<F>(
109        &mut self,
110        def_id: LocalDefId,
111        item_sp: Span,
112        fn_sig: Option<&'tcx hir::FnSig<'tcx>>,
113        kind: AnnotationKind,
114        inherit_deprecation: InheritDeprecation,
115        inherit_const_stability: InheritConstStability,
116        inherit_from_parent: InheritStability,
117        visit_children: F,
118    ) where
119        F: FnOnce(&mut Self),
120    {
121        let attrs = self.tcx.hir_attrs(self.tcx.local_def_id_to_hir_id(def_id));
122        debug!("annotate(id = {:?}, attrs = {:?})", def_id, attrs);
123
124        let depr = attrs::find_attr!(attrs, AttributeKind::Deprecation{deprecation, span} => (*deprecation, *span));
125        let const_stability_indirect = find_attr!(attrs, AttributeKind::ConstStabilityIndirect);
126
127        let mut is_deprecated = false;
128        if let Some((depr, span)) = &depr {
129            is_deprecated = true;
130
131            if matches!(kind, AnnotationKind::Prohibited | AnnotationKind::DeprecationProhibited) {
132                let hir_id = self.tcx.local_def_id_to_hir_id(def_id);
133                self.tcx.emit_node_span_lint(
134                    USELESS_DEPRECATED,
135                    hir_id,
136                    *span,
137                    errors::DeprecatedAnnotationHasNoEffect { span: *span },
138                );
139            }
140
141            // `Deprecation` is just two pointers, no need to intern it
142            let depr_entry = DeprecationEntry::local(*depr, def_id);
143            self.index.depr_map.insert(def_id, depr_entry);
144        } else if let Some(parent_depr) = self.parent_depr {
145            if inherit_deprecation.yes() {
146                is_deprecated = true;
147                info!("tagging child {:?} as deprecated from parent", def_id);
148                self.index.depr_map.insert(def_id, parent_depr);
149            }
150        }
151
152        if !self.tcx.features().staged_api() {
153            // Propagate unstability. This can happen even for non-staged-api crates in case
154            // -Zforce-unstable-if-unmarked is set.
155            if let Some(stab) = self.parent_stab {
156                if inherit_deprecation.yes() && stab.is_unstable() {
157                    self.index.stab_map.insert(def_id, stab);
158                    if fn_sig.is_some_and(|s| s.header.is_const()) {
159                        self.index.const_stab_map.insert(
160                            def_id,
161                            ConstStability::unmarked(const_stability_indirect, stab),
162                        );
163                    }
164                }
165            }
166
167            self.recurse_with_stability_attrs(
168                depr.map(|(d, _)| DeprecationEntry::local(d, def_id)),
169                None,
170                None,
171                visit_children,
172            );
173            return;
174        }
175
176        // # Regular and body stability
177        let stab = attrs::find_attr!(attrs, AttributeKind::Stability { stability, span } => (*stability, *span));
178        let body_stab =
179            attrs::find_attr!(attrs, AttributeKind::BodyStability { stability, .. } => *stability);
180
181        if let Some((depr, span)) = &depr
182            && depr.is_since_rustc_version()
183            && stab.is_none()
184        {
185            self.tcx.dcx().emit_err(errors::DeprecatedAttribute { span: *span });
186        }
187
188        if let Some(body_stab) = body_stab {
189            // FIXME: check that this item can have body stability
190
191            self.index.default_body_stab_map.insert(def_id, body_stab);
192            debug!(?self.index.default_body_stab_map);
193        }
194
195        let stab = stab.map(|(stab, span)| {
196            // Error if prohibited, or can't inherit anything from a container.
197            if kind == AnnotationKind::Prohibited
198                || (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated)
199            {
200                self.tcx.dcx().emit_err(errors::UselessStability { span, item_sp });
201            }
202
203            debug!("annotate: found {:?}", stab);
204
205            // Check if deprecated_since < stable_since. If it is,
206            // this is *almost surely* an accident.
207            if let (
208                &Some(DeprecatedSince::RustcVersion(dep_since)),
209                &attrs::StabilityLevel::Stable { since: stab_since, .. },
210            ) = (&depr.as_ref().map(|(d, _)| d.since), &stab.level)
211            {
212                match stab_since {
213                    StableSince::Current => {
214                        self.tcx
215                            .dcx()
216                            .emit_err(errors::CannotStabilizeDeprecated { span, item_sp });
217                    }
218                    StableSince::Version(stab_since) => {
219                        if dep_since < stab_since {
220                            self.tcx
221                                .dcx()
222                                .emit_err(errors::CannotStabilizeDeprecated { span, item_sp });
223                        }
224                    }
225                    StableSince::Err => {
226                        // An error already reported. Assume the unparseable stabilization
227                        // version is older than the deprecation version.
228                    }
229                }
230            }
231
232            // Stable *language* features shouldn't be used as unstable library features.
233            // (Not doing this for stable library features is checked by tidy.)
234            if let Stability { level: StabilityLevel::Unstable { .. }, feature } = stab {
235                if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() {
236                    self.tcx
237                        .dcx()
238                        .emit_err(errors::UnstableAttrForAlreadyStableFeature { span, item_sp });
239                }
240            }
241            if let Stability {
242                level: StabilityLevel::Unstable { implied_by: Some(implied_by), .. },
243                feature,
244            } = stab
245            {
246                self.index.implications.insert(implied_by, feature);
247            }
248
249            self.index.stab_map.insert(def_id, stab);
250            stab
251        });
252
253        if stab.is_none() {
254            debug!("annotate: stab not found, parent = {:?}", self.parent_stab);
255            if let Some(stab) = self.parent_stab {
256                if inherit_deprecation.yes() && stab.is_unstable() || inherit_from_parent.yes() {
257                    self.index.stab_map.insert(def_id, stab);
258                }
259            }
260        }
261
262        let final_stab = self.index.stab_map.get(&def_id);
263
264        // # Const stability
265
266        let const_stab = attrs::find_attr!(attrs, AttributeKind::ConstStability { stability, span } => (*stability, *span));
267
268        // If the current node is a function with const stability attributes (directly given or
269        // implied), check if the function/method is const or the parent impl block is const.
270        if let Some(fn_sig) = fn_sig
271            && !fn_sig.header.is_const()
272            && const_stab.is_some()
273        {
274            self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span });
275        }
276
277        // If this is marked const *stable*, it must also be regular-stable.
278        if let Some((const_stab, const_span)) = const_stab
279            && let Some(fn_sig) = fn_sig
280            && const_stab.is_const_stable()
281            && !stab.is_some_and(|s| s.is_stable())
282        {
283            self.tcx
284                .dcx()
285                .emit_err(errors::ConstStableNotStable { fn_sig_span: fn_sig.span, const_span });
286        }
287
288        // Stable *language* features shouldn't be used as unstable library features.
289        // (Not doing this for stable library features is checked by tidy.)
290        if let Some((
291            PartialConstStability { level: StabilityLevel::Unstable { .. }, feature, .. },
292            const_span,
293        )) = const_stab
294        {
295            if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() {
296                self.tcx.dcx().emit_err(errors::UnstableAttrForAlreadyStableFeature {
297                    span: const_span,
298                    item_sp,
299                });
300            }
301        }
302
303        if let Some((stab, span)) = &const_stab
304            && stab.is_const_stable()
305            && const_stability_indirect
306        {
307            self.tcx.dcx().emit_err(errors::RustcConstStableIndirectPairing { span: *span });
308        }
309
310        // After checking the immediate attributes, get rid of the span and compute implied
311        // const stability: inherit feature gate from regular stability.
312        let mut const_stab = const_stab
313            .map(|(stab, _span)| ConstStability::from_partial(stab, const_stability_indirect));
314
315        // If this is a const fn but not annotated with stability markers, see if we can inherit regular stability.
316        if fn_sig.is_some_and(|s| s.header.is_const()) && const_stab.is_none() &&
317            // We only ever inherit unstable features.
318            let Some(inherit_regular_stab) =
319                final_stab.filter(|s| s.is_unstable())
320        {
321            const_stab = Some(ConstStability {
322                // We subject these implicitly-const functions to recursive const stability.
323                const_stable_indirect: true,
324                promotable: false,
325                level: inherit_regular_stab.level,
326                feature: inherit_regular_stab.feature,
327            });
328        }
329
330        // Now that everything is computed, insert it into the table.
331        const_stab.inspect(|const_stab| {
332            self.index.const_stab_map.insert(def_id, *const_stab);
333        });
334
335        if let Some(ConstStability {
336            level: StabilityLevel::Unstable { implied_by: Some(implied_by), .. },
337            feature,
338            ..
339        }) = const_stab
340        {
341            self.index.implications.insert(implied_by, feature);
342        }
343
344        // `impl const Trait for Type` items forward their const stability to their
345        // immediate children.
346        // FIXME(const_trait_impl): how is this supposed to interact with `#[rustc_const_stable_indirect]`?
347        // Currently, once that is set, we do not inherit anything from the parent any more.
348        if const_stab.is_none() {
349            debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab);
350            if let Some(parent) = self.parent_const_stab {
351                if parent.is_const_unstable() {
352                    self.index.const_stab_map.insert(def_id, parent);
353                }
354            }
355        }
356
357        self.recurse_with_stability_attrs(
358            depr.map(|(d, _)| DeprecationEntry::local(d, def_id)),
359            stab,
360            inherit_const_stability.yes().then_some(const_stab).flatten(),
361            visit_children,
362        );
363    }
364
365    fn recurse_with_stability_attrs(
366        &mut self,
367        depr: Option<DeprecationEntry>,
368        stab: Option<Stability>,
369        const_stab: Option<ConstStability>,
370        f: impl FnOnce(&mut Self),
371    ) {
372        // These will be `Some` if this item changes the corresponding stability attribute.
373        let mut replaced_parent_depr = None;
374        let mut replaced_parent_stab = None;
375        let mut replaced_parent_const_stab = None;
376
377        if let Some(depr) = depr {
378            replaced_parent_depr = Some(replace(&mut self.parent_depr, Some(depr)));
379        }
380        if let Some(stab) = stab {
381            replaced_parent_stab = Some(replace(&mut self.parent_stab, Some(stab)));
382        }
383        if let Some(const_stab) = const_stab {
384            replaced_parent_const_stab =
385                Some(replace(&mut self.parent_const_stab, Some(const_stab)));
386        }
387
388        f(self);
389
390        if let Some(orig_parent_depr) = replaced_parent_depr {
391            self.parent_depr = orig_parent_depr;
392        }
393        if let Some(orig_parent_stab) = replaced_parent_stab {
394            self.parent_stab = orig_parent_stab;
395        }
396        if let Some(orig_parent_const_stab) = replaced_parent_const_stab {
397            self.parent_const_stab = orig_parent_const_stab;
398        }
399    }
400}
401
402impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
403    /// Because stability levels are scoped lexically, we want to walk
404    /// nested items in the context of the outer item, so enable
405    /// deep-walking.
406    type NestedFilter = nested_filter::All;
407
408    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
409        self.tcx
410    }
411
412    fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
413        let orig_in_trait_impl = self.in_trait_impl;
414        let mut kind = AnnotationKind::Required;
415        let mut const_stab_inherit = InheritConstStability::No;
416        let mut fn_sig = None;
417
418        match i.kind {
419            // Inherent impls and foreign modules serve only as containers for other items,
420            // they don't have their own stability. They still can be annotated as unstable
421            // and propagate this instability to children, but this annotation is completely
422            // optional. They inherit stability from their parents when unannotated.
423            hir::ItemKind::Impl(hir::Impl { of_trait: None, .. })
424            | hir::ItemKind::ForeignMod { .. } => {
425                self.in_trait_impl = false;
426                kind = AnnotationKind::Container;
427            }
428            hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => {
429                self.in_trait_impl = true;
430                kind = AnnotationKind::DeprecationProhibited;
431                const_stab_inherit = InheritConstStability::Yes;
432            }
433            hir::ItemKind::Struct(_, _, ref sd) => {
434                if let Some(ctor_def_id) = sd.ctor_def_id() {
435                    self.annotate(
436                        ctor_def_id,
437                        i.span,
438                        None,
439                        AnnotationKind::Required,
440                        InheritDeprecation::Yes,
441                        InheritConstStability::No,
442                        InheritStability::Yes,
443                        |_| {},
444                    )
445                }
446            }
447            hir::ItemKind::Fn { sig: ref item_fn_sig, .. } => {
448                fn_sig = Some(item_fn_sig);
449            }
450            _ => {}
451        }
452
453        self.annotate(
454            i.owner_id.def_id,
455            i.span,
456            fn_sig,
457            kind,
458            InheritDeprecation::Yes,
459            const_stab_inherit,
460            InheritStability::No,
461            |v| intravisit::walk_item(v, i),
462        );
463        self.in_trait_impl = orig_in_trait_impl;
464    }
465
466    fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
467        let fn_sig = match ti.kind {
468            hir::TraitItemKind::Fn(ref fn_sig, _) => Some(fn_sig),
469            _ => None,
470        };
471
472        self.annotate(
473            ti.owner_id.def_id,
474            ti.span,
475            fn_sig,
476            AnnotationKind::Required,
477            InheritDeprecation::Yes,
478            InheritConstStability::No,
479            InheritStability::No,
480            |v| {
481                intravisit::walk_trait_item(v, ti);
482            },
483        );
484    }
485
486    fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
487        let kind =
488            if self.in_trait_impl { AnnotationKind::Prohibited } else { AnnotationKind::Required };
489
490        let fn_sig = match ii.kind {
491            hir::ImplItemKind::Fn(ref fn_sig, _) => Some(fn_sig),
492            _ => None,
493        };
494
495        self.annotate(
496            ii.owner_id.def_id,
497            ii.span,
498            fn_sig,
499            kind,
500            InheritDeprecation::Yes,
501            InheritConstStability::No,
502            InheritStability::No,
503            |v| {
504                intravisit::walk_impl_item(v, ii);
505            },
506        );
507    }
508
509    fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) {
510        self.annotate(
511            var.def_id,
512            var.span,
513            None,
514            AnnotationKind::Required,
515            InheritDeprecation::Yes,
516            InheritConstStability::No,
517            InheritStability::Yes,
518            |v| {
519                if let Some(ctor_def_id) = var.data.ctor_def_id() {
520                    v.annotate(
521                        ctor_def_id,
522                        var.span,
523                        None,
524                        AnnotationKind::Required,
525                        InheritDeprecation::Yes,
526                        InheritConstStability::No,
527                        InheritStability::Yes,
528                        |_| {},
529                    );
530                }
531
532                intravisit::walk_variant(v, var)
533            },
534        )
535    }
536
537    fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) {
538        self.annotate(
539            s.def_id,
540            s.span,
541            None,
542            AnnotationKind::Required,
543            InheritDeprecation::Yes,
544            InheritConstStability::No,
545            InheritStability::Yes,
546            |v| {
547                intravisit::walk_field_def(v, s);
548            },
549        );
550    }
551
552    fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
553        let fn_sig = match &i.kind {
554            rustc_hir::ForeignItemKind::Fn(fn_sig, ..) => Some(fn_sig),
555            _ => None,
556        };
557        self.annotate(
558            i.owner_id.def_id,
559            i.span,
560            fn_sig,
561            AnnotationKind::Required,
562            InheritDeprecation::Yes,
563            InheritConstStability::No,
564            InheritStability::No,
565            |v| {
566                intravisit::walk_foreign_item(v, i);
567            },
568        );
569    }
570
571    fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
572        let kind = match &p.kind {
573            // Allow stability attributes on default generic arguments.
574            hir::GenericParamKind::Type { default: Some(_), .. }
575            | hir::GenericParamKind::Const { default: Some(_), .. } => AnnotationKind::Container,
576            _ => AnnotationKind::Prohibited,
577        };
578
579        self.annotate(
580            p.def_id,
581            p.span,
582            None,
583            kind,
584            InheritDeprecation::No,
585            InheritConstStability::No,
586            InheritStability::No,
587            |v| {
588                intravisit::walk_generic_param(v, p);
589            },
590        );
591    }
592}
593
594struct MissingStabilityAnnotations<'tcx> {
595    tcx: TyCtxt<'tcx>,
596    effective_visibilities: &'tcx EffectiveVisibilities,
597}
598
599impl<'tcx> MissingStabilityAnnotations<'tcx> {
600    fn check_missing_stability(&self, def_id: LocalDefId, span: Span) {
601        let stab = self.tcx.stability().local_stability(def_id);
602        if !self.tcx.sess.is_test_crate()
603            && stab.is_none()
604            && self.effective_visibilities.is_reachable(def_id)
605        {
606            let descr = self.tcx.def_descr(def_id.to_def_id());
607            self.tcx.dcx().emit_err(errors::MissingStabilityAttr { span, descr });
608        }
609    }
610
611    fn check_missing_const_stability(&self, def_id: LocalDefId, span: Span) {
612        let is_const = self.tcx.is_const_fn(def_id.to_def_id())
613            || (self.tcx.def_kind(def_id.to_def_id()) == DefKind::Trait
614                && self.tcx.is_const_trait(def_id.to_def_id()));
615
616        // Reachable const fn/trait must have a stability attribute.
617        if is_const
618            && self.effective_visibilities.is_reachable(def_id)
619            && self.tcx.lookup_const_stability(def_id).is_none()
620        {
621            let descr = self.tcx.def_descr(def_id.to_def_id());
622            self.tcx.dcx().emit_err(errors::MissingConstStabAttr { span, descr });
623        }
624    }
625}
626
627impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
628    type NestedFilter = nested_filter::OnlyBodies;
629
630    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
631        self.tcx
632    }
633
634    fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
635        // Inherent impls and foreign modules serve only as containers for other items,
636        // they don't have their own stability. They still can be annotated as unstable
637        // and propagate this instability to children, but this annotation is completely
638        // optional. They inherit stability from their parents when unannotated.
639        if !matches!(
640            i.kind,
641            hir::ItemKind::Impl(hir::Impl { of_trait: None, .. })
642                | hir::ItemKind::ForeignMod { .. }
643        ) {
644            self.check_missing_stability(i.owner_id.def_id, i.span);
645        }
646
647        // Ensure stable `const fn` have a const stability attribute.
648        self.check_missing_const_stability(i.owner_id.def_id, i.span);
649
650        intravisit::walk_item(self, i)
651    }
652
653    fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
654        self.check_missing_stability(ti.owner_id.def_id, ti.span);
655        intravisit::walk_trait_item(self, ti);
656    }
657
658    fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
659        let impl_def_id = self.tcx.hir_get_parent_item(ii.hir_id());
660        if self.tcx.impl_trait_ref(impl_def_id).is_none() {
661            self.check_missing_stability(ii.owner_id.def_id, ii.span);
662            self.check_missing_const_stability(ii.owner_id.def_id, ii.span);
663        }
664        intravisit::walk_impl_item(self, ii);
665    }
666
667    fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) {
668        self.check_missing_stability(var.def_id, var.span);
669        if let Some(ctor_def_id) = var.data.ctor_def_id() {
670            self.check_missing_stability(ctor_def_id, var.span);
671        }
672        intravisit::walk_variant(self, var);
673    }
674
675    fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) {
676        self.check_missing_stability(s.def_id, s.span);
677        intravisit::walk_field_def(self, s);
678    }
679
680    fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
681        self.check_missing_stability(i.owner_id.def_id, i.span);
682        intravisit::walk_foreign_item(self, i);
683    }
684    // Note that we don't need to `check_missing_stability` for default generic parameters,
685    // as we assume that any default generic parameters without attributes are automatically
686    // stable (assuming they have not inherited instability from their parent).
687}
688
689fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index {
690    let mut index = Index {
691        stab_map: Default::default(),
692        const_stab_map: Default::default(),
693        default_body_stab_map: Default::default(),
694        depr_map: Default::default(),
695        implications: Default::default(),
696    };
697
698    {
699        let mut annotator = Annotator {
700            tcx,
701            index: &mut index,
702            parent_stab: None,
703            parent_const_stab: None,
704            parent_depr: None,
705            in_trait_impl: false,
706        };
707
708        // If the `-Z force-unstable-if-unmarked` flag is passed then we provide
709        // a parent stability annotation which indicates that this is private
710        // with the `rustc_private` feature. This is intended for use when
711        // compiling `librustc_*` crates themselves so we can leverage crates.io
712        // while maintaining the invariant that all sysroot crates are unstable
713        // by default and are unable to be used.
714        if tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
715            let stability = Stability {
716                level: attrs::StabilityLevel::Unstable {
717                    reason: UnstableReason::Default,
718                    issue: NonZero::new(27812),
719                    is_soft: false,
720                    implied_by: None,
721                    old_name: None,
722                },
723                feature: sym::rustc_private,
724            };
725            annotator.parent_stab = Some(stability);
726        }
727
728        annotator.annotate(
729            CRATE_DEF_ID,
730            tcx.hir_span(CRATE_HIR_ID),
731            None,
732            AnnotationKind::Required,
733            InheritDeprecation::Yes,
734            InheritConstStability::No,
735            InheritStability::No,
736            |v| tcx.hir_walk_toplevel_module(v),
737        );
738    }
739    index
740}
741
742/// Cross-references the feature names of unstable APIs with enabled
743/// features and possibly prints errors.
744fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
745    tcx.hir_visit_item_likes_in_module(module_def_id, &mut Checker { tcx });
746}
747
748pub(crate) fn provide(providers: &mut Providers) {
749    *providers = Providers {
750        check_mod_unstable_api_usage,
751        stability_index,
752        stability_implications: |tcx, _| tcx.stability().implications.clone(),
753        lookup_stability: |tcx, id| tcx.stability().local_stability(id),
754        lookup_const_stability: |tcx, id| tcx.stability().local_const_stability(id),
755        lookup_default_body_stability: |tcx, id| tcx.stability().local_default_body_stability(id),
756        lookup_deprecation_entry: |tcx, id| tcx.stability().local_deprecation_entry(id),
757        ..*providers
758    };
759}
760
761struct Checker<'tcx> {
762    tcx: TyCtxt<'tcx>,
763}
764
765impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
766    type NestedFilter = nested_filter::OnlyBodies;
767
768    /// Because stability levels are scoped lexically, we want to walk
769    /// nested items in the context of the outer item, so enable
770    /// deep-walking.
771    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
772        self.tcx
773    }
774
775    fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
776        match item.kind {
777            hir::ItemKind::ExternCrate(_, ident) => {
778                // compiler-generated `extern crate` items have a dummy span.
779                // `std` is still checked for the `restricted-std` feature.
780                if item.span.is_dummy() && ident.name != sym::std {
781                    return;
782                }
783
784                let Some(cnum) = self.tcx.extern_mod_stmt_cnum(item.owner_id.def_id) else {
785                    return;
786                };
787                let def_id = cnum.as_def_id();
788                self.tcx.check_stability(def_id, Some(item.hir_id()), item.span, None);
789            }
790
791            // For implementations of traits, check the stability of each item
792            // individually as it's possible to have a stable trait with unstable
793            // items.
794            hir::ItemKind::Impl(hir::Impl {
795                of_trait: Some(t), self_ty, items, constness, ..
796            }) => {
797                let features = self.tcx.features();
798                if features.staged_api() {
799                    let attrs = self.tcx.hir_attrs(item.hir_id());
800                    let stab = attrs::find_attr!(attrs, AttributeKind::Stability{stability, span} => (*stability, *span));
801
802                    // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem
803                    let const_stab = attrs::find_attr!(attrs, AttributeKind::ConstStability{stability, ..} => *stability);
804
805                    // If this impl block has an #[unstable] attribute, give an
806                    // error if all involved types and traits are stable, because
807                    // it will have no effect.
808                    // See: https://github.com/rust-lang/rust/issues/55436
809                    if let Some((
810                        Stability { level: attrs::StabilityLevel::Unstable { .. }, .. },
811                        span,
812                    )) = stab
813                    {
814                        let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true };
815                        c.visit_ty_unambig(self_ty);
816                        c.visit_trait_ref(t);
817
818                        // do not lint when the trait isn't resolved, since resolution error should
819                        // be fixed first
820                        if t.path.res != Res::Err && c.fully_stable {
821                            self.tcx.emit_node_span_lint(
822                                INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
823                                item.hir_id(),
824                                span,
825                                errors::IneffectiveUnstableImpl,
826                            );
827                        }
828                    }
829
830                    if features.const_trait_impl()
831                        && let hir::Constness::Const = constness
832                    {
833                        let stable_or_implied_stable = match const_stab {
834                            None => true,
835                            Some(stab) if stab.is_const_stable() => {
836                                // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable
837                                // needs to have an error emitted.
838                                // Note: Remove this error once `const_trait_impl` is stabilized
839                                self.tcx
840                                    .dcx()
841                                    .emit_err(errors::TraitImplConstStable { span: item.span });
842                                true
843                            }
844                            Some(_) => false,
845                        };
846
847                        if let Some(trait_id) = t.trait_def_id()
848                            && let Some(const_stab) = self.tcx.lookup_const_stability(trait_id)
849                        {
850                            // the const stability of a trait impl must match the const stability on the trait.
851                            if const_stab.is_const_stable() != stable_or_implied_stable {
852                                let trait_span = self.tcx.def_ident_span(trait_id).unwrap();
853
854                                let impl_stability = if stable_or_implied_stable {
855                                    errors::ImplConstStability::Stable { span: item.span }
856                                } else {
857                                    errors::ImplConstStability::Unstable { span: item.span }
858                                };
859                                let trait_stability = if const_stab.is_const_stable() {
860                                    errors::TraitConstStability::Stable { span: trait_span }
861                                } else {
862                                    errors::TraitConstStability::Unstable { span: trait_span }
863                                };
864
865                                self.tcx.dcx().emit_err(errors::TraitImplConstStabilityMismatch {
866                                    span: item.span,
867                                    impl_stability,
868                                    trait_stability,
869                                });
870                            }
871                        }
872                    }
873                }
874
875                if let hir::Constness::Const = constness
876                    && let Some(def_id) = t.trait_def_id()
877                {
878                    // FIXME(const_trait_impl): Improve the span here.
879                    self.tcx.check_const_stability(def_id, t.path.span, t.path.span);
880                }
881
882                for impl_item_ref in *items {
883                    let impl_item = self.tcx.associated_item(impl_item_ref.id.owner_id);
884
885                    if let Some(def_id) = impl_item.trait_item_def_id {
886                        // Pass `None` to skip deprecation warnings.
887                        self.tcx.check_stability(def_id, None, impl_item_ref.span, None);
888                    }
889                }
890            }
891
892            _ => (/* pass */),
893        }
894        intravisit::walk_item(self, item);
895    }
896
897    fn visit_poly_trait_ref(&mut self, t: &'tcx hir::PolyTraitRef<'tcx>) {
898        match t.modifiers.constness {
899            hir::BoundConstness::Always(span) | hir::BoundConstness::Maybe(span) => {
900                if let Some(def_id) = t.trait_ref.trait_def_id() {
901                    self.tcx.check_const_stability(def_id, t.trait_ref.path.span, span);
902                }
903            }
904            hir::BoundConstness::Never => {}
905        }
906        intravisit::walk_poly_trait_ref(self, t);
907    }
908
909    fn visit_path(&mut self, path: &hir::Path<'tcx>, id: hir::HirId) {
910        if let Some(def_id) = path.res.opt_def_id() {
911            let method_span = path.segments.last().map(|s| s.ident.span);
912            let item_is_allowed = self.tcx.check_stability_allow_unstable(
913                def_id,
914                Some(id),
915                path.span,
916                method_span,
917                if is_unstable_reexport(self.tcx, id) {
918                    AllowUnstable::Yes
919                } else {
920                    AllowUnstable::No
921                },
922            );
923
924            if item_is_allowed {
925                // The item itself is allowed; check whether the path there is also allowed.
926                let is_allowed_through_unstable_modules: Option<Symbol> =
927                    self.tcx.lookup_stability(def_id).and_then(|stab| match stab.level {
928                        StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
929                            allowed_through_unstable_modules
930                        }
931                        _ => None,
932                    });
933
934                // Check parent modules stability as well if the item the path refers to is itself
935                // stable. We only emit errors for unstable path segments if the item is stable
936                // or allowed because stability is often inherited, so the most common case is that
937                // both the segments and the item are unstable behind the same feature flag.
938                //
939                // We check here rather than in `visit_path_segment` to prevent visiting the last
940                // path segment twice
941                //
942                // We include special cases via #[rustc_allowed_through_unstable_modules] for items
943                // that were accidentally stabilized through unstable paths before this check was
944                // added, such as `core::intrinsics::transmute`
945                let parents = path.segments.iter().rev().skip(1);
946                for path_segment in parents {
947                    if let Some(def_id) = path_segment.res.opt_def_id() {
948                        match is_allowed_through_unstable_modules {
949                            None => {
950                                // Emit a hard stability error if this path is not stable.
951
952                                // use `None` for id to prevent deprecation check
953                                self.tcx.check_stability_allow_unstable(
954                                    def_id,
955                                    None,
956                                    path.span,
957                                    None,
958                                    if is_unstable_reexport(self.tcx, id) {
959                                        AllowUnstable::Yes
960                                    } else {
961                                        AllowUnstable::No
962                                    },
963                                );
964                            }
965                            Some(deprecation) => {
966                                // Call the stability check directly so that we can control which
967                                // diagnostic is emitted.
968                                let eval_result = self.tcx.eval_stability_allow_unstable(
969                                    def_id,
970                                    None,
971                                    path.span,
972                                    None,
973                                    if is_unstable_reexport(self.tcx, id) {
974                                        AllowUnstable::Yes
975                                    } else {
976                                        AllowUnstable::No
977                                    },
978                                );
979                                let is_allowed = matches!(eval_result, EvalResult::Allow);
980                                if !is_allowed {
981                                    // Calculating message for lint involves calling `self.def_path_str`,
982                                    // which will by default invoke the expensive `visible_parent_map` query.
983                                    // Skip all that work if the lint is allowed anyway.
984                                    if self.tcx.lint_level_at_node(DEPRECATED, id).level
985                                        == lint::Level::Allow
986                                    {
987                                        return;
988                                    }
989                                    // Show a deprecation message.
990                                    let def_path =
991                                        with_no_trimmed_paths!(self.tcx.def_path_str(def_id));
992                                    let def_kind = self.tcx.def_descr(def_id);
993                                    let diag = Deprecated {
994                                        sub: None,
995                                        kind: def_kind.to_owned(),
996                                        path: def_path,
997                                        note: Some(deprecation),
998                                        since_kind: lint::DeprecatedSinceKind::InEffect,
999                                    };
1000                                    self.tcx.emit_node_span_lint(
1001                                        DEPRECATED,
1002                                        id,
1003                                        method_span.unwrap_or(path.span),
1004                                        diag,
1005                                    );
1006                                }
1007                            }
1008                        }
1009                    }
1010                }
1011            }
1012        }
1013
1014        intravisit::walk_path(self, path)
1015    }
1016}
1017
1018/// Check whether a path is a `use` item that has been marked as unstable.
1019///
1020/// See issue #94972 for details on why this is a special case
1021fn is_unstable_reexport(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
1022    // Get the LocalDefId so we can lookup the item to check the kind.
1023    let Some(owner) = id.as_owner() else {
1024        return false;
1025    };
1026    let def_id = owner.def_id;
1027
1028    let Some(stab) = tcx.stability().local_stability(def_id) else {
1029        return false;
1030    };
1031
1032    if stab.level.is_stable() {
1033        // The re-export is not marked as unstable, don't override
1034        return false;
1035    }
1036
1037    // If this is a path that isn't a use, we don't need to do anything special
1038    if !matches!(tcx.hir_expect_item(def_id).kind, ItemKind::Use(..)) {
1039        return false;
1040    }
1041
1042    true
1043}
1044
1045struct CheckTraitImplStable<'tcx> {
1046    tcx: TyCtxt<'tcx>,
1047    fully_stable: bool,
1048}
1049
1050impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> {
1051    fn visit_path(&mut self, path: &hir::Path<'tcx>, _id: hir::HirId) {
1052        if let Some(def_id) = path.res.opt_def_id() {
1053            if let Some(stab) = self.tcx.lookup_stability(def_id) {
1054                self.fully_stable &= stab.level.is_stable();
1055            }
1056        }
1057        intravisit::walk_path(self, path)
1058    }
1059
1060    fn visit_trait_ref(&mut self, t: &'tcx TraitRef<'tcx>) {
1061        if let Res::Def(DefKind::Trait, trait_did) = t.path.res {
1062            if let Some(stab) = self.tcx.lookup_stability(trait_did) {
1063                self.fully_stable &= stab.level.is_stable();
1064            }
1065        }
1066        intravisit::walk_trait_ref(self, t)
1067    }
1068
1069    fn visit_ty(&mut self, t: &'tcx Ty<'tcx, AmbigArg>) {
1070        if let TyKind::Never = t.kind {
1071            self.fully_stable = false;
1072        }
1073        if let TyKind::BareFn(function) = t.kind {
1074            if extern_abi_stability(function.abi).is_err() {
1075                self.fully_stable = false;
1076            }
1077        }
1078        intravisit::walk_ty(self, t)
1079    }
1080
1081    fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) {
1082        for ty in fd.inputs {
1083            self.visit_ty_unambig(ty)
1084        }
1085        if let hir::FnRetTy::Return(output_ty) = fd.output {
1086            match output_ty.kind {
1087                TyKind::Never => {} // `-> !` is stable
1088                _ => self.visit_ty_unambig(output_ty),
1089            }
1090        }
1091    }
1092}
1093
1094/// Given the list of enabled features that were not language features (i.e., that
1095/// were expected to be library features), and the list of features used from
1096/// libraries, identify activated features that don't exist and error about them.
1097pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
1098    let is_staged_api =
1099        tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api();
1100    if is_staged_api {
1101        let effective_visibilities = &tcx.effective_visibilities(());
1102        let mut missing = MissingStabilityAnnotations { tcx, effective_visibilities };
1103        missing.check_missing_stability(CRATE_DEF_ID, tcx.hir_span(CRATE_HIR_ID));
1104        tcx.hir_walk_toplevel_module(&mut missing);
1105        tcx.hir_visit_all_item_likes_in_crate(&mut missing);
1106    }
1107
1108    let enabled_lang_features = tcx.features().enabled_lang_features();
1109    let mut lang_features = UnordSet::default();
1110    for EnabledLangFeature { gate_name, attr_sp, stable_since } in enabled_lang_features {
1111        if let Some(version) = stable_since {
1112            // Warn if the user has enabled an already-stable lang feature.
1113            unnecessary_stable_feature_lint(tcx, *attr_sp, *gate_name, *version);
1114        }
1115        if !lang_features.insert(gate_name) {
1116            // Warn if the user enables a lang feature multiple times.
1117            tcx.dcx().emit_err(errors::DuplicateFeatureErr { span: *attr_sp, feature: *gate_name });
1118        }
1119    }
1120
1121    let enabled_lib_features = tcx.features().enabled_lib_features();
1122    let mut remaining_lib_features = FxIndexMap::default();
1123    for EnabledLibFeature { gate_name, attr_sp } in enabled_lib_features {
1124        if remaining_lib_features.contains_key(gate_name) {
1125            // Warn if the user enables a lib feature multiple times.
1126            tcx.dcx().emit_err(errors::DuplicateFeatureErr { span: *attr_sp, feature: *gate_name });
1127        }
1128        remaining_lib_features.insert(*gate_name, *attr_sp);
1129    }
1130    // `stdbuild` has special handling for `libc`, so we need to
1131    // recognise the feature when building std.
1132    // Likewise, libtest is handled specially, so `test` isn't
1133    // available as we'd like it to be.
1134    // FIXME: only remove `libc` when `stdbuild` is enabled.
1135    // FIXME: remove special casing for `test`.
1136    // FIXME(#120456) - is `swap_remove` correct?
1137    remaining_lib_features.swap_remove(&sym::libc);
1138    remaining_lib_features.swap_remove(&sym::test);
1139
1140    /// For each feature in `defined_features`..
1141    ///
1142    /// - If it is in `remaining_lib_features` (those features with `#![feature(..)]` attributes in
1143    ///   the current crate), check if it is stable (or partially stable) and thus an unnecessary
1144    ///   attribute.
1145    /// - If it is in `remaining_implications` (a feature that is referenced by an `implied_by`
1146    ///   from the current crate), then remove it from the remaining implications.
1147    ///
1148    /// Once this function has been invoked for every feature (local crate and all extern crates),
1149    /// then..
1150    ///
1151    /// - If features remain in `remaining_lib_features`, then the user has enabled a feature that
1152    ///   does not exist.
1153    /// - If features remain in `remaining_implications`, the `implied_by` refers to a feature that
1154    ///   does not exist.
1155    ///
1156    /// By structuring the code in this way: checking the features defined from each crate one at a
1157    /// time, less loading from metadata is performed and thus compiler performance is improved.
1158    fn check_features<'tcx>(
1159        tcx: TyCtxt<'tcx>,
1160        remaining_lib_features: &mut FxIndexMap<Symbol, Span>,
1161        remaining_implications: &mut UnordMap<Symbol, Symbol>,
1162        defined_features: &LibFeatures,
1163        all_implications: &UnordMap<Symbol, Symbol>,
1164    ) {
1165        for (feature, stability) in defined_features.to_sorted_vec() {
1166            if let FeatureStability::AcceptedSince(since) = stability
1167                && let Some(span) = remaining_lib_features.get(&feature)
1168            {
1169                // Warn if the user has enabled an already-stable lib feature.
1170                if let Some(implies) = all_implications.get(&feature) {
1171                    unnecessary_partially_stable_feature_lint(tcx, *span, feature, *implies, since);
1172                } else {
1173                    unnecessary_stable_feature_lint(tcx, *span, feature, since);
1174                }
1175            }
1176            // FIXME(#120456) - is `swap_remove` correct?
1177            remaining_lib_features.swap_remove(&feature);
1178
1179            // `feature` is the feature doing the implying, but `implied_by` is the feature with
1180            // the attribute that establishes this relationship. `implied_by` is guaranteed to be a
1181            // feature defined in the local crate because `remaining_implications` is only the
1182            // implications from this crate.
1183            remaining_implications.remove(&feature);
1184
1185            if let FeatureStability::Unstable { old_name: Some(alias) } = stability {
1186                if let Some(span) = remaining_lib_features.swap_remove(&alias) {
1187                    tcx.dcx().emit_err(errors::RenamedFeature { span, feature, alias });
1188                }
1189            }
1190
1191            if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
1192                break;
1193            }
1194        }
1195    }
1196
1197    // All local crate implications need to have the feature that implies it confirmed to exist.
1198    let mut remaining_implications = tcx.stability_implications(LOCAL_CRATE).clone();
1199
1200    // We always collect the lib features enabled in the current crate, even if there are
1201    // no unknown features, because the collection also does feature attribute validation.
1202    let local_defined_features = tcx.lib_features(LOCAL_CRATE);
1203    if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() {
1204        // Loading the implications of all crates is unavoidable to be able to emit the partial
1205        // stabilization diagnostic, but it can be avoided when there are no
1206        // `remaining_lib_features`.
1207        let mut all_implications = remaining_implications.clone();
1208        for &cnum in tcx.crates(()) {
1209            all_implications
1210                .extend_unord(tcx.stability_implications(cnum).items().map(|(k, v)| (*k, *v)));
1211        }
1212
1213        check_features(
1214            tcx,
1215            &mut remaining_lib_features,
1216            &mut remaining_implications,
1217            local_defined_features,
1218            &all_implications,
1219        );
1220
1221        for &cnum in tcx.crates(()) {
1222            if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
1223                break;
1224            }
1225            check_features(
1226                tcx,
1227                &mut remaining_lib_features,
1228                &mut remaining_implications,
1229                tcx.lib_features(cnum),
1230                &all_implications,
1231            );
1232        }
1233    }
1234
1235    for (feature, span) in remaining_lib_features {
1236        tcx.dcx().emit_err(errors::UnknownFeature { span, feature });
1237    }
1238
1239    for (&implied_by, &feature) in remaining_implications.to_sorted_stable_ord() {
1240        let local_defined_features = tcx.lib_features(LOCAL_CRATE);
1241        let span = local_defined_features
1242            .stability
1243            .get(&feature)
1244            .expect("feature that implied another does not exist")
1245            .1;
1246        tcx.dcx().emit_err(errors::ImpliedFeatureNotExist { span, feature, implied_by });
1247    }
1248
1249    // FIXME(#44232): the `used_features` table no longer exists, so we
1250    // don't lint about unused features. We should re-enable this one day!
1251}
1252
1253fn unnecessary_partially_stable_feature_lint(
1254    tcx: TyCtxt<'_>,
1255    span: Span,
1256    feature: Symbol,
1257    implies: Symbol,
1258    since: Symbol,
1259) {
1260    tcx.emit_node_span_lint(
1261        lint::builtin::STABLE_FEATURES,
1262        hir::CRATE_HIR_ID,
1263        span,
1264        errors::UnnecessaryPartialStableFeature {
1265            span,
1266            line: tcx.sess.source_map().span_extend_to_line(span),
1267            feature,
1268            since,
1269            implies,
1270        },
1271    );
1272}
1273
1274fn unnecessary_stable_feature_lint(
1275    tcx: TyCtxt<'_>,
1276    span: Span,
1277    feature: Symbol,
1278    mut since: Symbol,
1279) {
1280    if since.as_str() == VERSION_PLACEHOLDER {
1281        since = sym::env_CFG_RELEASE;
1282    }
1283    tcx.emit_node_span_lint(
1284        lint::builtin::STABLE_FEATURES,
1285        hir::CRATE_HIR_ID,
1286        span,
1287        errors::UnnecessaryStableFeature { feature, since },
1288    );
1289}