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