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