]> git.lizzy.rs Git - rust.git/blob - src/librustc_passes/stability.rs
Update cargo
[rust.git] / src / librustc_passes / stability.rs
1 //! A pass that annotates every item and method with its stability level,
2 //! propagating default levels lexically from parent to children ast nodes.
3
4 use rustc_ast::ast::Attribute;
5 use rustc_attr::{self as attr, ConstStability, Stability};
6 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
7 use rustc_errors::struct_span_err;
8 use rustc_hir as hir;
9 use rustc_hir::def::{DefKind, Res};
10 use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
11 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
12 use rustc_hir::{Generics, HirId, Item, StructField, Variant};
13 use rustc_middle::hir::map::Map;
14 use rustc_middle::middle::privacy::AccessLevels;
15 use rustc_middle::middle::stability::{DeprecationEntry, Index};
16 use rustc_middle::ty::query::Providers;
17 use rustc_middle::ty::TyCtxt;
18 use rustc_session::lint;
19 use rustc_session::parse::feature_err;
20 use rustc_session::Session;
21 use rustc_span::symbol::{sym, Symbol};
22 use rustc_span::Span;
23 use rustc_trait_selection::traits::misc::can_type_implement_copy;
24
25 use std::cmp::Ordering;
26 use std::mem::replace;
27 use std::num::NonZeroU32;
28
29 #[derive(PartialEq)]
30 enum AnnotationKind {
31     // Annotation is required if not inherited from unstable parents
32     Required,
33     // Annotation is useless, reject it
34     Prohibited,
35     // Annotation itself is useless, but it can be propagated to children
36     Container,
37 }
38
39 // A private tree-walker for producing an Index.
40 struct Annotator<'a, 'tcx> {
41     tcx: TyCtxt<'tcx>,
42     index: &'a mut Index<'tcx>,
43     parent_stab: Option<&'tcx Stability>,
44     parent_const_stab: Option<&'tcx ConstStability>,
45     parent_depr: Option<DeprecationEntry>,
46     in_trait_impl: bool,
47 }
48
49 impl<'a, 'tcx> Annotator<'a, 'tcx> {
50     // Determine the stability for a node based on its attributes and inherited
51     // stability. The stability is recorded in the index and used as the parent.
52     fn annotate<F>(
53         &mut self,
54         hir_id: HirId,
55         attrs: &[Attribute],
56         item_sp: Span,
57         kind: AnnotationKind,
58         visit_children: F,
59     ) where
60         F: FnOnce(&mut Self),
61     {
62         debug!("annotate(id = {:?}, attrs = {:?})", hir_id, attrs);
63         let mut did_error = false;
64         if !self.tcx.features().staged_api {
65             did_error = self.forbid_staged_api_attrs(hir_id, attrs);
66         }
67
68         let depr = if did_error {
69             None
70         } else {
71             attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp)
72         };
73         let mut is_deprecated = false;
74         if let Some(depr) = &depr {
75             is_deprecated = true;
76
77             if kind == AnnotationKind::Prohibited {
78                 self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless");
79             }
80
81             // `Deprecation` is just two pointers, no need to intern it
82             let depr_entry = DeprecationEntry::local(depr.clone(), hir_id);
83             self.index.depr_map.insert(hir_id, depr_entry);
84         } else if let Some(parent_depr) = self.parent_depr.clone() {
85             is_deprecated = true;
86             info!("tagging child {:?} as deprecated from parent", hir_id);
87             self.index.depr_map.insert(hir_id, parent_depr);
88         }
89
90         if self.tcx.features().staged_api {
91             if let Some(..) = attrs.iter().find(|a| a.check_name(sym::deprecated)) {
92                 self.tcx.sess.span_err(
93                     item_sp,
94                     "`#[deprecated]` cannot be used in staged API; \
95                                                 use `#[rustc_deprecated]` instead",
96                 );
97             }
98         } else {
99             self.recurse_with_stability_attrs(
100                 depr.map(|d| DeprecationEntry::local(d, hir_id)),
101                 None,
102                 None,
103                 visit_children,
104             );
105             return;
106         }
107
108         let (stab, const_stab) = attr::find_stability(&self.tcx.sess.parse_sess, attrs, item_sp);
109
110         let const_stab = const_stab.map(|const_stab| {
111             let const_stab = self.tcx.intern_const_stability(const_stab);
112             self.index.const_stab_map.insert(hir_id, const_stab);
113             const_stab
114         });
115
116         if const_stab.is_none() {
117             debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab);
118             if let Some(parent) = self.parent_const_stab {
119                 if parent.level.is_unstable() {
120                     self.index.const_stab_map.insert(hir_id, parent);
121                 }
122             }
123         }
124
125         if depr.as_ref().map_or(false, |d| d.is_since_rustc_version) {
126             if stab.is_none() {
127                 struct_span_err!(
128                     self.tcx.sess,
129                     item_sp,
130                     E0549,
131                     "rustc_deprecated attribute must be paired with \
132                     either stable or unstable attribute"
133                 )
134                 .emit();
135             }
136         }
137
138         let stab = stab.map(|stab| {
139             // Error if prohibited, or can't inherit anything from a container.
140             if kind == AnnotationKind::Prohibited
141                 || (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated)
142             {
143                 self.tcx.sess.span_err(item_sp, "This stability annotation is useless");
144             }
145
146             debug!("annotate: found {:?}", stab);
147             let stab = self.tcx.intern_stability(stab);
148
149             // Check if deprecated_since < stable_since. If it is,
150             // this is *almost surely* an accident.
151             if let (&Some(dep_since), &attr::Stable { since: stab_since }) =
152                 (&depr.as_ref().and_then(|d| d.since), &stab.level)
153             {
154                 // Explicit version of iter::order::lt to handle parse errors properly
155                 for (dep_v, stab_v) in
156                     dep_since.as_str().split('.').zip(stab_since.as_str().split('.'))
157                 {
158                     if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::<u64>(), stab_v.parse()) {
159                         match dep_v.cmp(&stab_v) {
160                             Ordering::Less => {
161                                 self.tcx.sess.span_err(
162                                     item_sp,
163                                     "An API can't be stabilized \
164                                                                  after it is deprecated",
165                                 );
166                                 break;
167                             }
168                             Ordering::Equal => continue,
169                             Ordering::Greater => break,
170                         }
171                     } else {
172                         // Act like it isn't less because the question is now nonsensical,
173                         // and this makes us not do anything else interesting.
174                         self.tcx.sess.span_err(
175                             item_sp,
176                             "Invalid stability or deprecation \
177                                                          version found",
178                         );
179                         break;
180                     }
181                 }
182             }
183
184             self.index.stab_map.insert(hir_id, stab);
185             stab
186         });
187
188         if stab.is_none() {
189             debug!("annotate: stab not found, parent = {:?}", self.parent_stab);
190             if let Some(stab) = self.parent_stab {
191                 if stab.level.is_unstable() {
192                     self.index.stab_map.insert(hir_id, stab);
193                 }
194             }
195         }
196
197         self.recurse_with_stability_attrs(
198             depr.map(|d| DeprecationEntry::local(d, hir_id)),
199             stab,
200             const_stab,
201             visit_children,
202         );
203     }
204
205     fn recurse_with_stability_attrs(
206         &mut self,
207         depr: Option<DeprecationEntry>,
208         stab: Option<&'tcx Stability>,
209         const_stab: Option<&'tcx ConstStability>,
210         f: impl FnOnce(&mut Self),
211     ) {
212         // These will be `Some` if this item changes the corresponding stability attribute.
213         let mut replaced_parent_depr = None;
214         let mut replaced_parent_stab = None;
215         let mut replaced_parent_const_stab = None;
216
217         if let Some(depr) = depr {
218             replaced_parent_depr = Some(replace(&mut self.parent_depr, Some(depr)));
219         }
220         if let Some(stab) = stab {
221             replaced_parent_stab = Some(replace(&mut self.parent_stab, Some(stab)));
222         }
223         if let Some(const_stab) = const_stab {
224             replaced_parent_const_stab =
225                 Some(replace(&mut self.parent_const_stab, Some(const_stab)));
226         }
227
228         f(self);
229
230         if let Some(orig_parent_depr) = replaced_parent_depr {
231             self.parent_depr = orig_parent_depr;
232         }
233         if let Some(orig_parent_stab) = replaced_parent_stab {
234             self.parent_stab = orig_parent_stab;
235         }
236         if let Some(orig_parent_const_stab) = replaced_parent_const_stab {
237             self.parent_const_stab = orig_parent_const_stab;
238         }
239     }
240
241     // returns true if an error occurred, used to suppress some spurious errors
242     fn forbid_staged_api_attrs(&mut self, hir_id: HirId, attrs: &[Attribute]) -> bool {
243         // Emit errors for non-staged-api crates.
244         let unstable_attrs = [
245             sym::unstable,
246             sym::stable,
247             sym::rustc_deprecated,
248             sym::rustc_const_unstable,
249             sym::rustc_const_stable,
250         ];
251         let mut has_error = false;
252         for attr in attrs {
253             let name = attr.name_or_empty();
254             if unstable_attrs.contains(&name) {
255                 attr::mark_used(attr);
256                 struct_span_err!(
257                     self.tcx.sess,
258                     attr.span,
259                     E0734,
260                     "stability attributes may not be used outside of the standard library",
261                 )
262                 .emit();
263                 has_error = true;
264             }
265         }
266
267         // Propagate unstability.  This can happen even for non-staged-api crates in case
268         // -Zforce-unstable-if-unmarked is set.
269         if let Some(stab) = self.parent_stab {
270             if stab.level.is_unstable() {
271                 self.index.stab_map.insert(hir_id, stab);
272             }
273         }
274
275         has_error
276     }
277 }
278
279 impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
280     /// Because stability levels are scoped lexically, we want to walk
281     /// nested items in the context of the outer item, so enable
282     /// deep-walking.
283     type Map = Map<'tcx>;
284
285     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
286         NestedVisitorMap::All(self.tcx.hir())
287     }
288
289     fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
290         let orig_in_trait_impl = self.in_trait_impl;
291         let mut kind = AnnotationKind::Required;
292         match i.kind {
293             // Inherent impls and foreign modules serve only as containers for other items,
294             // they don't have their own stability. They still can be annotated as unstable
295             // and propagate this unstability to children, but this annotation is completely
296             // optional. They inherit stability from their parents when unannotated.
297             hir::ItemKind::Impl { of_trait: None, .. } | hir::ItemKind::ForeignMod(..) => {
298                 self.in_trait_impl = false;
299                 kind = AnnotationKind::Container;
300             }
301             hir::ItemKind::Impl { of_trait: Some(_), .. } => {
302                 self.in_trait_impl = true;
303             }
304             hir::ItemKind::Struct(ref sd, _) => {
305                 if let Some(ctor_hir_id) = sd.ctor_hir_id() {
306                     self.annotate(ctor_hir_id, &i.attrs, i.span, AnnotationKind::Required, |_| {})
307                 }
308             }
309             _ => {}
310         }
311
312         self.annotate(i.hir_id, &i.attrs, i.span, kind, |v| intravisit::walk_item(v, i));
313         self.in_trait_impl = orig_in_trait_impl;
314     }
315
316     fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
317         self.annotate(ti.hir_id, &ti.attrs, ti.span, AnnotationKind::Required, |v| {
318             intravisit::walk_trait_item(v, ti);
319         });
320     }
321
322     fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
323         let kind =
324             if self.in_trait_impl { AnnotationKind::Prohibited } else { AnnotationKind::Required };
325         self.annotate(ii.hir_id, &ii.attrs, ii.span, kind, |v| {
326             intravisit::walk_impl_item(v, ii);
327         });
328     }
329
330     fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) {
331         self.annotate(var.id, &var.attrs, var.span, AnnotationKind::Required, |v| {
332             if let Some(ctor_hir_id) = var.data.ctor_hir_id() {
333                 v.annotate(ctor_hir_id, &var.attrs, var.span, AnnotationKind::Required, |_| {});
334             }
335
336             intravisit::walk_variant(v, var, g, item_id)
337         })
338     }
339
340     fn visit_struct_field(&mut self, s: &'tcx StructField<'tcx>) {
341         self.annotate(s.hir_id, &s.attrs, s.span, AnnotationKind::Required, |v| {
342             intravisit::walk_struct_field(v, s);
343         });
344     }
345
346     fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
347         self.annotate(i.hir_id, &i.attrs, i.span, AnnotationKind::Required, |v| {
348             intravisit::walk_foreign_item(v, i);
349         });
350     }
351
352     fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
353         self.annotate(md.hir_id, &md.attrs, md.span, AnnotationKind::Required, |_| {});
354     }
355 }
356
357 struct MissingStabilityAnnotations<'tcx> {
358     tcx: TyCtxt<'tcx>,
359     access_levels: &'tcx AccessLevels,
360 }
361
362 impl<'tcx> MissingStabilityAnnotations<'tcx> {
363     fn check_missing_stability(&self, hir_id: HirId, span: Span) {
364         let stab = self.tcx.stability().local_stability(hir_id);
365         let is_error =
366             !self.tcx.sess.opts.test && stab.is_none() && self.access_levels.is_reachable(hir_id);
367         if is_error {
368             let def_id = self.tcx.hir().local_def_id(hir_id);
369             let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
370             self.tcx.sess.span_err(span, &format!("{} has missing stability attribute", descr));
371         }
372     }
373 }
374
375 impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
376     type Map = Map<'tcx>;
377
378     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
379         NestedVisitorMap::OnlyBodies(self.tcx.hir())
380     }
381
382     fn visit_item(&mut self, i: &'tcx Item<'tcx>) {
383         match i.kind {
384             // Inherent impls and foreign modules serve only as containers for other items,
385             // they don't have their own stability. They still can be annotated as unstable
386             // and propagate this unstability to children, but this annotation is completely
387             // optional. They inherit stability from their parents when unannotated.
388             hir::ItemKind::Impl { of_trait: None, .. } | hir::ItemKind::ForeignMod(..) => {}
389
390             _ => self.check_missing_stability(i.hir_id, i.span),
391         }
392
393         intravisit::walk_item(self, i)
394     }
395
396     fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
397         self.check_missing_stability(ti.hir_id, ti.span);
398         intravisit::walk_trait_item(self, ti);
399     }
400
401     fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
402         let impl_def_id = self.tcx.hir().local_def_id(self.tcx.hir().get_parent_item(ii.hir_id));
403         if self.tcx.impl_trait_ref(impl_def_id).is_none() {
404             self.check_missing_stability(ii.hir_id, ii.span);
405         }
406         intravisit::walk_impl_item(self, ii);
407     }
408
409     fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) {
410         self.check_missing_stability(var.id, var.span);
411         intravisit::walk_variant(self, var, g, item_id);
412     }
413
414     fn visit_struct_field(&mut self, s: &'tcx StructField<'tcx>) {
415         self.check_missing_stability(s.hir_id, s.span);
416         intravisit::walk_struct_field(self, s);
417     }
418
419     fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) {
420         self.check_missing_stability(i.hir_id, i.span);
421         intravisit::walk_foreign_item(self, i);
422     }
423
424     fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
425         self.check_missing_stability(md.hir_id, md.span);
426     }
427 }
428
429 fn new_index(tcx: TyCtxt<'tcx>) -> Index<'tcx> {
430     let is_staged_api =
431         tcx.sess.opts.debugging_opts.force_unstable_if_unmarked || tcx.features().staged_api;
432     let mut staged_api = FxHashMap::default();
433     staged_api.insert(LOCAL_CRATE, is_staged_api);
434     let mut index = Index {
435         staged_api,
436         stab_map: Default::default(),
437         const_stab_map: Default::default(),
438         depr_map: Default::default(),
439         active_features: Default::default(),
440     };
441
442     let active_lib_features = &tcx.features().declared_lib_features;
443     let active_lang_features = &tcx.features().declared_lang_features;
444
445     // Put the active features into a map for quick lookup.
446     index.active_features = active_lib_features
447         .iter()
448         .map(|&(s, ..)| s)
449         .chain(active_lang_features.iter().map(|&(s, ..)| s))
450         .collect();
451
452     {
453         let krate = tcx.hir().krate();
454         let mut annotator = Annotator {
455             tcx,
456             index: &mut index,
457             parent_stab: None,
458             parent_const_stab: None,
459             parent_depr: None,
460             in_trait_impl: false,
461         };
462
463         // If the `-Z force-unstable-if-unmarked` flag is passed then we provide
464         // a parent stability annotation which indicates that this is private
465         // with the `rustc_private` feature. This is intended for use when
466         // compiling `librustc_*` crates themselves so we can leverage crates.io
467         // while maintaining the invariant that all sysroot crates are unstable
468         // by default and are unable to be used.
469         if tcx.sess.opts.debugging_opts.force_unstable_if_unmarked {
470             let reason = "this crate is being loaded from the sysroot, an \
471                           unstable location; did you mean to load this crate \
472                           from crates.io via `Cargo.toml` instead?";
473             let stability = tcx.intern_stability(Stability {
474                 level: attr::StabilityLevel::Unstable {
475                     reason: Some(Symbol::intern(reason)),
476                     issue: NonZeroU32::new(27812),
477                     is_soft: false,
478                 },
479                 feature: sym::rustc_private,
480             });
481             annotator.parent_stab = Some(stability);
482         }
483
484         annotator.annotate(
485             hir::CRATE_HIR_ID,
486             &krate.item.attrs,
487             krate.item.span,
488             AnnotationKind::Required,
489             |v| intravisit::walk_crate(v, krate),
490         );
491     }
492     index
493 }
494
495 /// Cross-references the feature names of unstable APIs with enabled
496 /// features and possibly prints errors.
497 fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
498     tcx.hir().visit_item_likes_in_module(module_def_id, &mut Checker { tcx }.as_deep_visitor());
499 }
500
501 pub(crate) fn provide(providers: &mut Providers) {
502     *providers = Providers { check_mod_unstable_api_usage, ..*providers };
503     providers.stability_index = |tcx, cnum| {
504         assert_eq!(cnum, LOCAL_CRATE);
505         new_index(tcx)
506     };
507 }
508
509 struct Checker<'tcx> {
510     tcx: TyCtxt<'tcx>,
511 }
512
513 impl Visitor<'tcx> for Checker<'tcx> {
514     type Map = Map<'tcx>;
515
516     /// Because stability levels are scoped lexically, we want to walk
517     /// nested items in the context of the outer item, so enable
518     /// deep-walking.
519     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
520         NestedVisitorMap::OnlyBodies(self.tcx.hir())
521     }
522
523     fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
524         match item.kind {
525             hir::ItemKind::ExternCrate(_) => {
526                 // compiler-generated `extern crate` items have a dummy span.
527                 // `std` is still checked for the `restricted-std` feature.
528                 if item.span.is_dummy() && item.ident.as_str() != "std" {
529                     return;
530                 }
531
532                 let def_id = self.tcx.hir().local_def_id(item.hir_id);
533                 let cnum = match self.tcx.extern_mod_stmt_cnum(def_id) {
534                     Some(cnum) => cnum,
535                     None => return,
536                 };
537                 let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX };
538                 self.tcx.check_stability(def_id, Some(item.hir_id), item.span);
539             }
540
541             // For implementations of traits, check the stability of each item
542             // individually as it's possible to have a stable trait with unstable
543             // items.
544             hir::ItemKind::Impl { of_trait: Some(ref t), items, .. } => {
545                 if let Res::Def(DefKind::Trait, trait_did) = t.path.res {
546                     for impl_item_ref in items {
547                         let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
548                         let trait_item_def_id = self
549                             .tcx
550                             .associated_items(trait_did)
551                             .filter_by_name_unhygienic(impl_item.ident.name)
552                             .next()
553                             .map(|item| item.def_id);
554                         if let Some(def_id) = trait_item_def_id {
555                             // Pass `None` to skip deprecation warnings.
556                             self.tcx.check_stability(def_id, None, impl_item.span);
557                         }
558                     }
559                 }
560             }
561
562             // There's no good place to insert stability check for non-Copy unions,
563             // so semi-randomly perform it here in stability.rs
564             hir::ItemKind::Union(..) if !self.tcx.features().untagged_unions => {
565                 let def_id = self.tcx.hir().local_def_id(item.hir_id);
566                 let adt_def = self.tcx.adt_def(def_id);
567                 let ty = self.tcx.type_of(def_id);
568
569                 if adt_def.has_dtor(self.tcx) {
570                     feature_err(
571                         &self.tcx.sess.parse_sess,
572                         sym::untagged_unions,
573                         item.span,
574                         "unions with `Drop` implementations are unstable",
575                     )
576                     .emit();
577                 } else {
578                     let param_env = self.tcx.param_env(def_id);
579                     if can_type_implement_copy(self.tcx, param_env, ty).is_err() {
580                         feature_err(
581                             &self.tcx.sess.parse_sess,
582                             sym::untagged_unions,
583                             item.span,
584                             "unions with non-`Copy` fields are unstable",
585                         )
586                         .emit();
587                     }
588                 }
589             }
590
591             _ => (/* pass */),
592         }
593         intravisit::walk_item(self, item);
594     }
595
596     fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, id: hir::HirId) {
597         if let Some(def_id) = path.res.opt_def_id() {
598             self.tcx.check_stability(def_id, Some(id), path.span)
599         }
600         intravisit::walk_path(self, path)
601     }
602 }
603
604 /// Given the list of enabled features that were not language features (i.e., that
605 /// were expected to be library features), and the list of features used from
606 /// libraries, identify activated features that don't exist and error about them.
607 pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
608     let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE);
609
610     if tcx.stability().staged_api[&LOCAL_CRATE] {
611         let krate = tcx.hir().krate();
612         let mut missing = MissingStabilityAnnotations { tcx, access_levels };
613         missing.check_missing_stability(hir::CRATE_HIR_ID, krate.item.span);
614         intravisit::walk_crate(&mut missing, krate);
615         krate.visit_all_item_likes(&mut missing.as_deep_visitor());
616     }
617
618     let declared_lang_features = &tcx.features().declared_lang_features;
619     let mut lang_features = FxHashSet::default();
620     for &(feature, span, since) in declared_lang_features {
621         if let Some(since) = since {
622             // Warn if the user has enabled an already-stable lang feature.
623             unnecessary_stable_feature_lint(tcx, span, feature, since);
624         }
625         if !lang_features.insert(feature) {
626             // Warn if the user enables a lang feature multiple times.
627             duplicate_feature_err(tcx.sess, span, feature);
628         }
629     }
630
631     let declared_lib_features = &tcx.features().declared_lib_features;
632     let mut remaining_lib_features = FxHashMap::default();
633     for (feature, span) in declared_lib_features {
634         if remaining_lib_features.contains_key(&feature) {
635             // Warn if the user enables a lib feature multiple times.
636             duplicate_feature_err(tcx.sess, *span, *feature);
637         }
638         remaining_lib_features.insert(feature, *span);
639     }
640     // `stdbuild` has special handling for `libc`, so we need to
641     // recognise the feature when building std.
642     // Likewise, libtest is handled specially, so `test` isn't
643     // available as we'd like it to be.
644     // FIXME: only remove `libc` when `stdbuild` is active.
645     // FIXME: remove special casing for `test`.
646     remaining_lib_features.remove(&sym::libc);
647     remaining_lib_features.remove(&sym::test);
648
649     let check_features = |remaining_lib_features: &mut FxHashMap<_, _>, defined_features: &[_]| {
650         for &(feature, since) in defined_features {
651             if let Some(since) = since {
652                 if let Some(span) = remaining_lib_features.get(&feature) {
653                     // Warn if the user has enabled an already-stable lib feature.
654                     unnecessary_stable_feature_lint(tcx, *span, feature, since);
655                 }
656             }
657             remaining_lib_features.remove(&feature);
658             if remaining_lib_features.is_empty() {
659                 break;
660             }
661         }
662     };
663
664     // We always collect the lib features declared in the current crate, even if there are
665     // no unknown features, because the collection also does feature attribute validation.
666     let local_defined_features = tcx.lib_features().to_vec();
667     if !remaining_lib_features.is_empty() {
668         check_features(&mut remaining_lib_features, &local_defined_features);
669
670         for &cnum in &*tcx.crates() {
671             if remaining_lib_features.is_empty() {
672                 break;
673             }
674             check_features(&mut remaining_lib_features, tcx.defined_lib_features(cnum));
675         }
676     }
677
678     for (feature, span) in remaining_lib_features {
679         struct_span_err!(tcx.sess, span, E0635, "unknown feature `{}`", feature).emit();
680     }
681
682     // FIXME(#44232): the `used_features` table no longer exists, so we
683     // don't lint about unused features. We should re-enable this one day!
684 }
685
686 fn unnecessary_stable_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol, since: Symbol) {
687     tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| {
688         lint.build(&format!(
689             "the feature `{}` has been stable since {} and no longer requires \
690                       an attribute to enable",
691             feature, since
692         ))
693         .emit();
694     });
695 }
696
697 fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) {
698     struct_span_err!(sess, span, E0636, "the feature `{}` has already been declared", feature)
699         .emit();
700 }