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