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