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