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