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