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_parsing::{
9    self as attr, AttributeKind, ConstStability, DeprecatedSince, PartialConstStability, Stability,
10    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 = attr::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 = attr::find_attr!(attrs, AttributeKind::Stability { stability, span } => (*stability, *span));
178        let body_stab =
179            attr::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                &attr::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 = attr::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: attr::StabilityLevel::Unstable {
717                    reason: UnstableReason::Default,
718                    issue: NonZero::new(27812),
719                    is_soft: false,
720                    implied_by: None,
721                },
722                feature: sym::rustc_private,
723            };
724            annotator.parent_stab = Some(stability);
725        }
726
727        annotator.annotate(
728            CRATE_DEF_ID,
729            tcx.hir().span(CRATE_HIR_ID),
730            None,
731            AnnotationKind::Required,
732            InheritDeprecation::Yes,
733            InheritConstStability::No,
734            InheritStability::No,
735            |v| tcx.hir_walk_toplevel_module(v),
736        );
737    }
738    index
739}
740
741/// Cross-references the feature names of unstable APIs with enabled
742/// features and possibly prints errors.
743fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
744    tcx.hir_visit_item_likes_in_module(module_def_id, &mut Checker { tcx });
745}
746
747pub(crate) fn provide(providers: &mut Providers) {
748    *providers = Providers {
749        check_mod_unstable_api_usage,
750        stability_index,
751        stability_implications: |tcx, _| tcx.stability().implications.clone(),
752        lookup_stability: |tcx, id| tcx.stability().local_stability(id),
753        lookup_const_stability: |tcx, id| tcx.stability().local_const_stability(id),
754        lookup_default_body_stability: |tcx, id| tcx.stability().local_default_body_stability(id),
755        lookup_deprecation_entry: |tcx, id| tcx.stability().local_deprecation_entry(id),
756        ..*providers
757    };
758}
759
760struct Checker<'tcx> {
761    tcx: TyCtxt<'tcx>,
762}
763
764impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
765    type NestedFilter = nested_filter::OnlyBodies;
766
767    /// Because stability levels are scoped lexically, we want to walk
768    /// nested items in the context of the outer item, so enable
769    /// deep-walking.
770    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
771        self.tcx
772    }
773
774    fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
775        match item.kind {
776            hir::ItemKind::ExternCrate(_) => {
777                // compiler-generated `extern crate` items have a dummy span.
778                // `std` is still checked for the `restricted-std` feature.
779                if item.span.is_dummy() && item.ident.name != sym::std {
780                    return;
781                }
782
783                let Some(cnum) = self.tcx.extern_mod_stmt_cnum(item.owner_id.def_id) else {
784                    return;
785                };
786                let def_id = cnum.as_def_id();
787                self.tcx.check_stability(def_id, Some(item.hir_id()), item.span, None);
788            }
789
790            // For implementations of traits, check the stability of each item
791            // individually as it's possible to have a stable trait with unstable
792            // items.
793            hir::ItemKind::Impl(hir::Impl {
794                of_trait: Some(t), self_ty, items, constness, ..
795            }) => {
796                let features = self.tcx.features();
797                if features.staged_api() {
798                    let attrs = self.tcx.hir_attrs(item.hir_id());
799                    let stab = attr::find_attr!(attrs, AttributeKind::Stability{stability, span} => (*stability, *span));
800
801                    // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem
802                    let const_stab = attr::find_attr!(attrs, AttributeKind::ConstStability{stability, ..} => *stability);
803
804                    // If this impl block has an #[unstable] attribute, give an
805                    // error if all involved types and traits are stable, because
806                    // it will have no effect.
807                    // See: https://github.com/rust-lang/rust/issues/55436
808                    if let Some((
809                        Stability { level: attr::StabilityLevel::Unstable { .. }, .. },
810                        span,
811                    )) = stab
812                    {
813                        let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true };
814                        c.visit_ty_unambig(self_ty);
815                        c.visit_trait_ref(t);
816
817                        // do not lint when the trait isn't resolved, since resolution error should
818                        // be fixed first
819                        if t.path.res != Res::Err && c.fully_stable {
820                            self.tcx.emit_node_span_lint(
821                                INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
822                                item.hir_id(),
823                                span,
824                                errors::IneffectiveUnstableImpl,
825                            );
826                        }
827                    }
828
829                    if features.const_trait_impl()
830                        && let hir::Constness::Const = constness
831                    {
832                        let stable_or_implied_stable = match const_stab {
833                            None => true,
834                            Some(stab) if stab.is_const_stable() => {
835                                // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable
836                                // needs to have an error emitted.
837                                // Note: Remove this error once `const_trait_impl` is stabilized
838                                self.tcx
839                                    .dcx()
840                                    .emit_err(errors::TraitImplConstStable { span: item.span });
841                                true
842                            }
843                            Some(_) => false,
844                        };
845
846                        if let Some(trait_id) = t.trait_def_id()
847                            && let Some(const_stab) = self.tcx.lookup_const_stability(trait_id)
848                        {
849                            // the const stability of a trait impl must match the const stability on the trait.
850                            if const_stab.is_const_stable() != stable_or_implied_stable {
851                                let trait_span = self.tcx.def_ident_span(trait_id).unwrap();
852
853                                let impl_stability = if stable_or_implied_stable {
854                                    errors::ImplConstStability::Stable { span: item.span }
855                                } else {
856                                    errors::ImplConstStability::Unstable { span: item.span }
857                                };
858                                let trait_stability = if const_stab.is_const_stable() {
859                                    errors::TraitConstStability::Stable { span: trait_span }
860                                } else {
861                                    errors::TraitConstStability::Unstable { span: trait_span }
862                                };
863
864                                self.tcx.dcx().emit_err(errors::TraitImplConstStabilityMismatch {
865                                    span: item.span,
866                                    impl_stability,
867                                    trait_stability,
868                                });
869                            }
870                        }
871                    }
872                }
873
874                if let hir::Constness::Const = constness
875                    && let Some(def_id) = t.trait_def_id()
876                {
877                    // FIXME(const_trait_impl): Improve the span here.
878                    self.tcx.check_const_stability(def_id, t.path.span, t.path.span);
879                }
880
881                for impl_item_ref in *items {
882                    let impl_item = self.tcx.associated_item(impl_item_ref.id.owner_id);
883
884                    if let Some(def_id) = impl_item.trait_item_def_id {
885                        // Pass `None` to skip deprecation warnings.
886                        self.tcx.check_stability(def_id, None, impl_item_ref.span, None);
887                    }
888                }
889            }
890
891            _ => (/* pass */),
892        }
893        intravisit::walk_item(self, item);
894    }
895
896    fn visit_poly_trait_ref(&mut self, t: &'tcx hir::PolyTraitRef<'tcx>) {
897        match t.modifiers.constness {
898            hir::BoundConstness::Always(span) | hir::BoundConstness::Maybe(span) => {
899                if let Some(def_id) = t.trait_ref.trait_def_id() {
900                    self.tcx.check_const_stability(def_id, t.trait_ref.path.span, span);
901                }
902            }
903            hir::BoundConstness::Never => {}
904        }
905        intravisit::walk_poly_trait_ref(self, t);
906    }
907
908    fn visit_path(&mut self, path: &hir::Path<'tcx>, id: hir::HirId) {
909        if let Some(def_id) = path.res.opt_def_id() {
910            let method_span = path.segments.last().map(|s| s.ident.span);
911            let item_is_allowed = self.tcx.check_stability_allow_unstable(
912                def_id,
913                Some(id),
914                path.span,
915                method_span,
916                if is_unstable_reexport(self.tcx, id) {
917                    AllowUnstable::Yes
918                } else {
919                    AllowUnstable::No
920                },
921            );
922
923            if item_is_allowed {
924                // The item itself is allowed; check whether the path there is also allowed.
925                let is_allowed_through_unstable_modules: Option<Symbol> =
926                    self.tcx.lookup_stability(def_id).and_then(|stab| match stab.level {
927                        StabilityLevel::Stable { allowed_through_unstable_modules, .. } => {
928                            allowed_through_unstable_modules
929                        }
930                        _ => None,
931                    });
932
933                // Check parent modules stability as well if the item the path refers to is itself
934                // stable. We only emit errors for unstable path segments if the item is stable
935                // or allowed because stability is often inherited, so the most common case is that
936                // both the segments and the item are unstable behind the same feature flag.
937                //
938                // We check here rather than in `visit_path_segment` to prevent visiting the last
939                // path segment twice
940                //
941                // We include special cases via #[rustc_allowed_through_unstable_modules] for items
942                // that were accidentally stabilized through unstable paths before this check was
943                // added, such as `core::intrinsics::transmute`
944                let parents = path.segments.iter().rev().skip(1);
945                for path_segment in parents {
946                    if let Some(def_id) = path_segment.res.opt_def_id() {
947                        match is_allowed_through_unstable_modules {
948                            None => {
949                                // Emit a hard stability error if this path is not stable.
950
951                                // use `None` for id to prevent deprecation check
952                                self.tcx.check_stability_allow_unstable(
953                                    def_id,
954                                    None,
955                                    path.span,
956                                    None,
957                                    if is_unstable_reexport(self.tcx, id) {
958                                        AllowUnstable::Yes
959                                    } else {
960                                        AllowUnstable::No
961                                    },
962                                );
963                            }
964                            Some(deprecation) => {
965                                // Call the stability check directly so that we can control which
966                                // diagnostic is emitted.
967                                let eval_result = self.tcx.eval_stability_allow_unstable(
968                                    def_id,
969                                    None,
970                                    path.span,
971                                    None,
972                                    if is_unstable_reexport(self.tcx, id) {
973                                        AllowUnstable::Yes
974                                    } else {
975                                        AllowUnstable::No
976                                    },
977                                );
978                                let is_allowed = matches!(eval_result, EvalResult::Allow);
979                                if !is_allowed {
980                                    // Calculating message for lint involves calling `self.def_path_str`,
981                                    // which will by default invoke the expensive `visible_parent_map` query.
982                                    // Skip all that work if the lint is allowed anyway.
983                                    if self.tcx.lint_level_at_node(DEPRECATED, id).0
984                                        == lint::Level::Allow
985                                    {
986                                        return;
987                                    }
988                                    // Show a deprecation message.
989                                    let def_path =
990                                        with_no_trimmed_paths!(self.tcx.def_path_str(def_id));
991                                    let def_kind = self.tcx.def_descr(def_id);
992                                    let diag = Deprecated {
993                                        sub: None,
994                                        kind: def_kind.to_owned(),
995                                        path: def_path,
996                                        note: Some(deprecation),
997                                        since_kind: lint::DeprecatedSinceKind::InEffect,
998                                    };
999                                    self.tcx.emit_node_span_lint(
1000                                        DEPRECATED,
1001                                        id,
1002                                        method_span.unwrap_or(path.span),
1003                                        diag,
1004                                    );
1005                                }
1006                            }
1007                        }
1008                    }
1009                }
1010            }
1011        }
1012
1013        intravisit::walk_path(self, path)
1014    }
1015}
1016
1017/// Check whether a path is a `use` item that has been marked as unstable.
1018///
1019/// See issue #94972 for details on why this is a special case
1020fn is_unstable_reexport(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
1021    // Get the LocalDefId so we can lookup the item to check the kind.
1022    let Some(owner) = id.as_owner() else {
1023        return false;
1024    };
1025    let def_id = owner.def_id;
1026
1027    let Some(stab) = tcx.stability().local_stability(def_id) else {
1028        return false;
1029    };
1030
1031    if stab.level.is_stable() {
1032        // The re-export is not marked as unstable, don't override
1033        return false;
1034    }
1035
1036    // If this is a path that isn't a use, we don't need to do anything special
1037    if !matches!(tcx.hir_expect_item(def_id).kind, ItemKind::Use(..)) {
1038        return false;
1039    }
1040
1041    true
1042}
1043
1044struct CheckTraitImplStable<'tcx> {
1045    tcx: TyCtxt<'tcx>,
1046    fully_stable: bool,
1047}
1048
1049impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> {
1050    fn visit_path(&mut self, path: &hir::Path<'tcx>, _id: hir::HirId) {
1051        if let Some(def_id) = path.res.opt_def_id() {
1052            if let Some(stab) = self.tcx.lookup_stability(def_id) {
1053                self.fully_stable &= stab.level.is_stable();
1054            }
1055        }
1056        intravisit::walk_path(self, path)
1057    }
1058
1059    fn visit_trait_ref(&mut self, t: &'tcx TraitRef<'tcx>) {
1060        if let Res::Def(DefKind::Trait, trait_did) = t.path.res {
1061            if let Some(stab) = self.tcx.lookup_stability(trait_did) {
1062                self.fully_stable &= stab.level.is_stable();
1063            }
1064        }
1065        intravisit::walk_trait_ref(self, t)
1066    }
1067
1068    fn visit_ty(&mut self, t: &'tcx Ty<'tcx, AmbigArg>) {
1069        if let TyKind::Never = t.kind {
1070            self.fully_stable = false;
1071        }
1072        if let TyKind::BareFn(function) = t.kind {
1073            if extern_abi_stability(function.abi).is_err() {
1074                self.fully_stable = false;
1075            }
1076        }
1077        intravisit::walk_ty(self, t)
1078    }
1079
1080    fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) {
1081        for ty in fd.inputs {
1082            self.visit_ty_unambig(ty)
1083        }
1084        if let hir::FnRetTy::Return(output_ty) = fd.output {
1085            match output_ty.kind {
1086                TyKind::Never => {} // `-> !` is stable
1087                _ => self.visit_ty_unambig(output_ty),
1088            }
1089        }
1090    }
1091}
1092
1093/// Given the list of enabled features that were not language features (i.e., that
1094/// were expected to be library features), and the list of features used from
1095/// libraries, identify activated features that don't exist and error about them.
1096pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
1097    let is_staged_api =
1098        tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api();
1099    if is_staged_api {
1100        let effective_visibilities = &tcx.effective_visibilities(());
1101        let mut missing = MissingStabilityAnnotations { tcx, effective_visibilities };
1102        missing.check_missing_stability(CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID));
1103        tcx.hir_walk_toplevel_module(&mut missing);
1104        tcx.hir_visit_all_item_likes_in_crate(&mut missing);
1105    }
1106
1107    let enabled_lang_features = tcx.features().enabled_lang_features();
1108    let mut lang_features = UnordSet::default();
1109    for EnabledLangFeature { gate_name, attr_sp, stable_since } in enabled_lang_features {
1110        if let Some(version) = stable_since {
1111            // Warn if the user has enabled an already-stable lang feature.
1112            unnecessary_stable_feature_lint(tcx, *attr_sp, *gate_name, *version);
1113        }
1114        if !lang_features.insert(gate_name) {
1115            // Warn if the user enables a lang feature multiple times.
1116            tcx.dcx().emit_err(errors::DuplicateFeatureErr { span: *attr_sp, feature: *gate_name });
1117        }
1118    }
1119
1120    let enabled_lib_features = tcx.features().enabled_lib_features();
1121    let mut remaining_lib_features = FxIndexMap::default();
1122    for EnabledLibFeature { gate_name, attr_sp } in enabled_lib_features {
1123        if remaining_lib_features.contains_key(gate_name) {
1124            // Warn if the user enables a lib feature multiple times.
1125            tcx.dcx().emit_err(errors::DuplicateFeatureErr { span: *attr_sp, feature: *gate_name });
1126        }
1127        remaining_lib_features.insert(*gate_name, *attr_sp);
1128    }
1129    // `stdbuild` has special handling for `libc`, so we need to
1130    // recognise the feature when building std.
1131    // Likewise, libtest is handled specially, so `test` isn't
1132    // available as we'd like it to be.
1133    // FIXME: only remove `libc` when `stdbuild` is enabled.
1134    // FIXME: remove special casing for `test`.
1135    // FIXME(#120456) - is `swap_remove` correct?
1136    remaining_lib_features.swap_remove(&sym::libc);
1137    remaining_lib_features.swap_remove(&sym::test);
1138
1139    /// For each feature in `defined_features`..
1140    ///
1141    /// - If it is in `remaining_lib_features` (those features with `#![feature(..)]` attributes in
1142    ///   the current crate), check if it is stable (or partially stable) and thus an unnecessary
1143    ///   attribute.
1144    /// - If it is in `remaining_implications` (a feature that is referenced by an `implied_by`
1145    ///   from the current crate), then remove it from the remaining implications.
1146    ///
1147    /// Once this function has been invoked for every feature (local crate and all extern crates),
1148    /// then..
1149    ///
1150    /// - If features remain in `remaining_lib_features`, then the user has enabled a feature that
1151    ///   does not exist.
1152    /// - If features remain in `remaining_implications`, the `implied_by` refers to a feature that
1153    ///   does not exist.
1154    ///
1155    /// By structuring the code in this way: checking the features defined from each crate one at a
1156    /// time, less loading from metadata is performed and thus compiler performance is improved.
1157    fn check_features<'tcx>(
1158        tcx: TyCtxt<'tcx>,
1159        remaining_lib_features: &mut FxIndexMap<Symbol, Span>,
1160        remaining_implications: &mut UnordMap<Symbol, Symbol>,
1161        defined_features: &LibFeatures,
1162        all_implications: &UnordMap<Symbol, Symbol>,
1163    ) {
1164        for (feature, since) in defined_features.to_sorted_vec() {
1165            if let FeatureStability::AcceptedSince(since) = since
1166                && let Some(span) = remaining_lib_features.get(&feature)
1167            {
1168                // Warn if the user has enabled an already-stable lib feature.
1169                if let Some(implies) = all_implications.get(&feature) {
1170                    unnecessary_partially_stable_feature_lint(tcx, *span, feature, *implies, since);
1171                } else {
1172                    unnecessary_stable_feature_lint(tcx, *span, feature, since);
1173                }
1174            }
1175            // FIXME(#120456) - is `swap_remove` correct?
1176            remaining_lib_features.swap_remove(&feature);
1177
1178            // `feature` is the feature doing the implying, but `implied_by` is the feature with
1179            // the attribute that establishes this relationship. `implied_by` is guaranteed to be a
1180            // feature defined in the local crate because `remaining_implications` is only the
1181            // implications from this crate.
1182            remaining_implications.remove(&feature);
1183
1184            if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
1185                break;
1186            }
1187        }
1188    }
1189
1190    // All local crate implications need to have the feature that implies it confirmed to exist.
1191    let mut remaining_implications = tcx.stability_implications(LOCAL_CRATE).clone();
1192
1193    // We always collect the lib features enabled in the current crate, even if there are
1194    // no unknown features, because the collection also does feature attribute validation.
1195    let local_defined_features = tcx.lib_features(LOCAL_CRATE);
1196    if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() {
1197        // Loading the implications of all crates is unavoidable to be able to emit the partial
1198        // stabilization diagnostic, but it can be avoided when there are no
1199        // `remaining_lib_features`.
1200        let mut all_implications = remaining_implications.clone();
1201        for &cnum in tcx.crates(()) {
1202            all_implications
1203                .extend_unord(tcx.stability_implications(cnum).items().map(|(k, v)| (*k, *v)));
1204        }
1205
1206        check_features(
1207            tcx,
1208            &mut remaining_lib_features,
1209            &mut remaining_implications,
1210            local_defined_features,
1211            &all_implications,
1212        );
1213
1214        for &cnum in tcx.crates(()) {
1215            if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
1216                break;
1217            }
1218            check_features(
1219                tcx,
1220                &mut remaining_lib_features,
1221                &mut remaining_implications,
1222                tcx.lib_features(cnum),
1223                &all_implications,
1224            );
1225        }
1226    }
1227
1228    for (feature, span) in remaining_lib_features {
1229        tcx.dcx().emit_err(errors::UnknownFeature { span, feature });
1230    }
1231
1232    for (&implied_by, &feature) in remaining_implications.to_sorted_stable_ord() {
1233        let local_defined_features = tcx.lib_features(LOCAL_CRATE);
1234        let span = local_defined_features
1235            .stability
1236            .get(&feature)
1237            .expect("feature that implied another does not exist")
1238            .1;
1239        tcx.dcx().emit_err(errors::ImpliedFeatureNotExist { span, feature, implied_by });
1240    }
1241
1242    // FIXME(#44232): the `used_features` table no longer exists, so we
1243    // don't lint about unused features. We should re-enable this one day!
1244}
1245
1246fn unnecessary_partially_stable_feature_lint(
1247    tcx: TyCtxt<'_>,
1248    span: Span,
1249    feature: Symbol,
1250    implies: Symbol,
1251    since: Symbol,
1252) {
1253    tcx.emit_node_span_lint(
1254        lint::builtin::STABLE_FEATURES,
1255        hir::CRATE_HIR_ID,
1256        span,
1257        errors::UnnecessaryPartialStableFeature {
1258            span,
1259            line: tcx.sess.source_map().span_extend_to_line(span),
1260            feature,
1261            since,
1262            implies,
1263        },
1264    );
1265}
1266
1267fn unnecessary_stable_feature_lint(
1268    tcx: TyCtxt<'_>,
1269    span: Span,
1270    feature: Symbol,
1271    mut since: Symbol,
1272) {
1273    if since.as_str() == VERSION_PLACEHOLDER {
1274        since = sym::env_CFG_RELEASE;
1275    }
1276    tcx.emit_node_span_lint(
1277        lint::builtin::STABLE_FEATURES,
1278        hir::CRATE_HIR_ID,
1279        span,
1280        errors::UnnecessaryStableFeature { feature, since },
1281    );
1282}