1 //! A pass that annotates every item and method with its stability level,
2 //! propagating default levels lexically from parent to children ast nodes.
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
;
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}
;
23 use rustc_trait_selection
::traits
::misc
::can_type_implement_copy
;
25 use std
::cmp
::Ordering
;
26 use std
::mem
::replace
;
27 use std
::num
::NonZeroU32
;
31 // Annotation is required if not inherited from unstable parents
33 // Annotation is useless, reject it
35 // Annotation itself is useless, but it can be propagated to children
39 // A private tree-walker for producing an Index.
40 struct Annotator
<'a
, '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
>,
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.
62 if !self.tcx
.features().staged_api
{
63 self.forbid_staged_api_attrs(hir_id
, attrs
, item_sp
, kind
, visit_children
);
67 // This crate explicitly wants staged API.
69 debug
!("annotate(id = {:?}, attrs = {:?})", hir_id
, attrs
);
70 if let Some(..) = attr
::find_deprecation(&self.tcx
.sess
.parse_sess
, attrs
, item_sp
) {
71 self.tcx
.sess
.span_err(
73 "`#[deprecated]` cannot be used in staged API; \
74 use `#[rustc_deprecated]` instead",
78 let (stab
, const_stab
) = attr
::find_stability(&self.tcx
.sess
.parse_sess
, attrs
, item_sp
);
80 let const_stab
= const_stab
.map(|const_stab
| {
81 let const_stab
= self.tcx
.intern_const_stability(const_stab
);
82 self.index
.const_stab_map
.insert(hir_id
, const_stab
);
86 if const_stab
.is_none() {
87 debug
!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab
);
88 if let Some(parent
) = self.parent_const_stab
{
89 if parent
.level
.is_unstable() {
90 self.index
.const_stab_map
.insert(hir_id
, parent
);
95 let stab
= stab
.map(|mut stab
| {
96 // Error if prohibited, or can't inherit anything from a container.
97 if kind
== AnnotationKind
::Prohibited
98 || (kind
== AnnotationKind
::Container
99 && stab
.level
.is_stable()
100 && stab
.rustc_depr
.is_none())
102 self.tcx
.sess
.span_err(item_sp
, "This stability annotation is useless");
105 debug
!("annotate: found {:?}", stab
);
106 // If parent is deprecated and we're not, inherit this by merging
107 // deprecated_since and its reason.
108 if let Some(parent_stab
) = self.parent_stab
{
109 if parent_stab
.rustc_depr
.is_some() && stab
.rustc_depr
.is_none() {
110 stab
.rustc_depr
= parent_stab
.rustc_depr
114 let stab
= self.tcx
.intern_stability(stab
);
116 // Check if deprecated_since < stable_since. If it is,
117 // this is *almost surely* an accident.
119 &Some(attr
::RustcDeprecation { since: dep_since, .. }
),
120 &attr
::Stable { since: stab_since }
,
121 ) = (&stab
.rustc_depr
, &stab
.level
)
123 // Explicit version of iter::order::lt to handle parse errors properly
124 for (dep_v
, stab_v
) in
125 dep_since
.as_str().split('
.'
).zip(stab_since
.as_str().split('
.'
))
127 if let (Ok(dep_v
), Ok(stab_v
)) = (dep_v
.parse
::<u64>(), stab_v
.parse()) {
128 match dep_v
.cmp(&stab_v
) {
130 self.tcx
.sess
.span_err(
132 "An API can't be stabilized \
133 after it is deprecated",
137 Ordering
::Equal
=> continue,
138 Ordering
::Greater
=> break,
141 // Act like it isn't less because the question is now nonsensical,
142 // and this makes us not do anything else interesting.
143 self.tcx
.sess
.span_err(
145 "Invalid stability or deprecation \
153 self.index
.stab_map
.insert(hir_id
, stab
);
158 debug
!("annotate: stab not found, parent = {:?}", self.parent_stab
);
159 if let Some(stab
) = self.parent_stab
{
160 if stab
.level
.is_unstable() {
161 self.index
.stab_map
.insert(hir_id
, stab
);
166 self.recurse_with_stability_attrs(stab
, const_stab
, visit_children
);
169 fn recurse_with_stability_attrs(
171 stab
: Option
<&'tcx Stability
>,
172 const_stab
: Option
<&'tcx ConstStability
>,
173 f
: impl FnOnce(&mut Self),
175 // These will be `Some` if this item changes the corresponding stability attribute.
176 let mut replaced_parent_stab
= None
;
177 let mut replaced_parent_const_stab
= None
;
179 if let Some(stab
) = stab
{
180 replaced_parent_stab
= Some(replace(&mut self.parent_stab
, Some(stab
)));
182 if let Some(const_stab
) = const_stab
{
183 replaced_parent_const_stab
=
184 Some(replace(&mut self.parent_const_stab
, Some(const_stab
)));
189 if let Some(orig_parent_stab
) = replaced_parent_stab
{
190 self.parent_stab
= orig_parent_stab
;
192 if let Some(orig_parent_const_stab
) = replaced_parent_const_stab
{
193 self.parent_const_stab
= orig_parent_const_stab
;
197 fn forbid_staged_api_attrs(
202 kind
: AnnotationKind
,
203 visit_children
: impl FnOnce(&mut Self),
205 // Emit errors for non-staged-api crates.
206 let unstable_attrs
= [
209 sym
::rustc_deprecated
,
210 sym
::rustc_const_unstable
,
211 sym
::rustc_const_stable
,
214 let name
= attr
.name_or_empty();
215 if unstable_attrs
.contains(&name
) {
216 attr
::mark_used(attr
);
221 "stability attributes may not be used outside of the standard library",
227 // Propagate unstability. This can happen even for non-staged-api crates in case
228 // -Zforce-unstable-if-unmarked is set.
229 if let Some(stab
) = self.parent_stab
{
230 if stab
.level
.is_unstable() {
231 self.index
.stab_map
.insert(hir_id
, stab
);
235 if let Some(depr
) = attr
::find_deprecation(&self.tcx
.sess
.parse_sess
, attrs
, item_sp
) {
236 if kind
== AnnotationKind
::Prohibited
{
237 self.tcx
.sess
.span_err(item_sp
, "This deprecation annotation is useless");
240 // `Deprecation` is just two pointers, no need to intern it
241 let depr_entry
= DeprecationEntry
::local(depr
, hir_id
);
242 self.index
.depr_map
.insert(hir_id
, depr_entry
.clone());
244 let orig_parent_depr
= replace(&mut self.parent_depr
, Some(depr_entry
));
245 visit_children(self);
246 self.parent_depr
= orig_parent_depr
;
247 } else if let Some(parent_depr
) = self.parent_depr
.clone() {
248 self.index
.depr_map
.insert(hir_id
, parent_depr
);
249 visit_children(self);
251 visit_children(self);
256 impl<'a
, 'tcx
> Visitor
<'tcx
> for Annotator
<'a
, 'tcx
> {
257 /// Because stability levels are scoped lexically, we want to walk
258 /// nested items in the context of the outer item, so enable
260 type Map
= Map
<'tcx
>;
262 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
263 NestedVisitorMap
::All(self.tcx
.hir())
266 fn visit_item(&mut self, i
: &'tcx Item
<'tcx
>) {
267 let orig_in_trait_impl
= self.in_trait_impl
;
268 let mut kind
= AnnotationKind
::Required
;
270 // Inherent impls and foreign modules serve only as containers for other items,
271 // they don't have their own stability. They still can be annotated as unstable
272 // and propagate this unstability to children, but this annotation is completely
273 // optional. They inherit stability from their parents when unannotated.
274 hir
::ItemKind
::Impl { of_trait: None, .. }
| hir
::ItemKind
::ForeignMod(..) => {
275 self.in_trait_impl
= false;
276 kind
= AnnotationKind
::Container
;
278 hir
::ItemKind
::Impl { of_trait: Some(_), .. }
=> {
279 self.in_trait_impl
= true;
281 hir
::ItemKind
::Struct(ref sd
, _
) => {
282 if let Some(ctor_hir_id
) = sd
.ctor_hir_id() {
283 self.annotate(ctor_hir_id
, &i
.attrs
, i
.span
, AnnotationKind
::Required
, |_
| {}
)
289 self.annotate(i
.hir_id
, &i
.attrs
, i
.span
, kind
, |v
| intravisit
::walk_item(v
, i
));
290 self.in_trait_impl
= orig_in_trait_impl
;
293 fn visit_trait_item(&mut self, ti
: &'tcx hir
::TraitItem
<'tcx
>) {
294 self.annotate(ti
.hir_id
, &ti
.attrs
, ti
.span
, AnnotationKind
::Required
, |v
| {
295 intravisit
::walk_trait_item(v
, ti
);
299 fn visit_impl_item(&mut self, ii
: &'tcx hir
::ImplItem
<'tcx
>) {
301 if self.in_trait_impl { AnnotationKind::Prohibited }
else { AnnotationKind::Required }
;
302 self.annotate(ii
.hir_id
, &ii
.attrs
, ii
.span
, kind
, |v
| {
303 intravisit
::walk_impl_item(v
, ii
);
307 fn visit_variant(&mut self, var
: &'tcx Variant
<'tcx
>, g
: &'tcx Generics
<'tcx
>, item_id
: HirId
) {
308 self.annotate(var
.id
, &var
.attrs
, var
.span
, AnnotationKind
::Required
, |v
| {
309 if let Some(ctor_hir_id
) = var
.data
.ctor_hir_id() {
310 v
.annotate(ctor_hir_id
, &var
.attrs
, var
.span
, AnnotationKind
::Required
, |_
| {}
);
313 intravisit
::walk_variant(v
, var
, g
, item_id
)
317 fn visit_struct_field(&mut self, s
: &'tcx StructField
<'tcx
>) {
318 self.annotate(s
.hir_id
, &s
.attrs
, s
.span
, AnnotationKind
::Required
, |v
| {
319 intravisit
::walk_struct_field(v
, s
);
323 fn visit_foreign_item(&mut self, i
: &'tcx hir
::ForeignItem
<'tcx
>) {
324 self.annotate(i
.hir_id
, &i
.attrs
, i
.span
, AnnotationKind
::Required
, |v
| {
325 intravisit
::walk_foreign_item(v
, i
);
329 fn visit_macro_def(&mut self, md
: &'tcx hir
::MacroDef
<'tcx
>) {
330 self.annotate(md
.hir_id
, &md
.attrs
, md
.span
, AnnotationKind
::Required
, |_
| {}
);
334 struct MissingStabilityAnnotations
<'tcx
> {
336 access_levels
: &'tcx AccessLevels
,
339 impl<'tcx
> MissingStabilityAnnotations
<'tcx
> {
340 fn check_missing_stability(&self, hir_id
: HirId
, span
: Span
) {
341 let stab
= self.tcx
.stability().local_stability(hir_id
);
343 !self.tcx
.sess
.opts
.test
&& stab
.is_none() && self.access_levels
.is_reachable(hir_id
);
345 let def_id
= self.tcx
.hir().local_def_id(hir_id
);
346 let descr
= self.tcx
.def_kind(def_id
).descr(def_id
.to_def_id());
347 self.tcx
.sess
.span_err(span
, &format
!("{} has missing stability attribute", descr
));
352 impl<'tcx
> Visitor
<'tcx
> for MissingStabilityAnnotations
<'tcx
> {
353 type Map
= Map
<'tcx
>;
355 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
356 NestedVisitorMap
::OnlyBodies(self.tcx
.hir())
359 fn visit_item(&mut self, i
: &'tcx Item
<'tcx
>) {
361 // Inherent impls and foreign modules serve only as containers for other items,
362 // they don't have their own stability. They still can be annotated as unstable
363 // and propagate this unstability to children, but this annotation is completely
364 // optional. They inherit stability from their parents when unannotated.
365 hir
::ItemKind
::Impl { of_trait: None, .. }
| hir
::ItemKind
::ForeignMod(..) => {}
367 _
=> self.check_missing_stability(i
.hir_id
, i
.span
),
370 intravisit
::walk_item(self, i
)
373 fn visit_trait_item(&mut self, ti
: &'tcx hir
::TraitItem
<'tcx
>) {
374 self.check_missing_stability(ti
.hir_id
, ti
.span
);
375 intravisit
::walk_trait_item(self, ti
);
378 fn visit_impl_item(&mut self, ii
: &'tcx hir
::ImplItem
<'tcx
>) {
379 let impl_def_id
= self.tcx
.hir().local_def_id(self.tcx
.hir().get_parent_item(ii
.hir_id
));
380 if self.tcx
.impl_trait_ref(impl_def_id
).is_none() {
381 self.check_missing_stability(ii
.hir_id
, ii
.span
);
383 intravisit
::walk_impl_item(self, ii
);
386 fn visit_variant(&mut self, var
: &'tcx Variant
<'tcx
>, g
: &'tcx Generics
<'tcx
>, item_id
: HirId
) {
387 self.check_missing_stability(var
.id
, var
.span
);
388 intravisit
::walk_variant(self, var
, g
, item_id
);
391 fn visit_struct_field(&mut self, s
: &'tcx StructField
<'tcx
>) {
392 self.check_missing_stability(s
.hir_id
, s
.span
);
393 intravisit
::walk_struct_field(self, s
);
396 fn visit_foreign_item(&mut self, i
: &'tcx hir
::ForeignItem
<'tcx
>) {
397 self.check_missing_stability(i
.hir_id
, i
.span
);
398 intravisit
::walk_foreign_item(self, i
);
401 fn visit_macro_def(&mut self, md
: &'tcx hir
::MacroDef
<'tcx
>) {
402 self.check_missing_stability(md
.hir_id
, md
.span
);
406 fn new_index(tcx
: TyCtxt
<'tcx
>) -> Index
<'tcx
> {
408 tcx
.sess
.opts
.debugging_opts
.force_unstable_if_unmarked
|| tcx
.features().staged_api
;
409 let mut staged_api
= FxHashMap
::default();
410 staged_api
.insert(LOCAL_CRATE
, is_staged_api
);
411 let mut index
= Index
{
413 stab_map
: Default
::default(),
414 const_stab_map
: Default
::default(),
415 depr_map
: Default
::default(),
416 active_features
: Default
::default(),
419 let active_lib_features
= &tcx
.features().declared_lib_features
;
420 let active_lang_features
= &tcx
.features().declared_lang_features
;
422 // Put the active features into a map for quick lookup.
423 index
.active_features
= active_lib_features
426 .chain(active_lang_features
.iter().map(|&(s
, ..)| s
))
430 let krate
= tcx
.hir().krate();
431 let mut annotator
= Annotator
{
435 parent_const_stab
: None
,
437 in_trait_impl
: false,
440 // If the `-Z force-unstable-if-unmarked` flag is passed then we provide
441 // a parent stability annotation which indicates that this is private
442 // with the `rustc_private` feature. This is intended for use when
443 // compiling `librustc_*` crates themselves so we can leverage crates.io
444 // while maintaining the invariant that all sysroot crates are unstable
445 // by default and are unable to be used.
446 if tcx
.sess
.opts
.debugging_opts
.force_unstable_if_unmarked
{
447 let reason
= "this crate is being loaded from the sysroot, an \
448 unstable location; did you mean to load this crate \
449 from crates.io via `Cargo.toml` instead?";
450 let stability
= tcx
.intern_stability(Stability
{
451 level
: attr
::StabilityLevel
::Unstable
{
452 reason
: Some(Symbol
::intern(reason
)),
453 issue
: NonZeroU32
::new(27812),
456 feature
: sym
::rustc_private
,
459 annotator
.parent_stab
= Some(stability
);
466 AnnotationKind
::Required
,
467 |v
| intravisit
::walk_crate(v
, krate
),
473 /// Cross-references the feature names of unstable APIs with enabled
474 /// features and possibly prints errors.
475 fn check_mod_unstable_api_usage(tcx
: TyCtxt
<'_
>, module_def_id
: LocalDefId
) {
476 tcx
.hir().visit_item_likes_in_module(module_def_id
, &mut Checker { tcx }
.as_deep_visitor());
479 pub(crate) fn provide(providers
: &mut Providers
) {
480 *providers
= Providers { check_mod_unstable_api_usage, ..*providers }
;
481 providers
.stability_index
= |tcx
, cnum
| {
482 assert_eq
!(cnum
, LOCAL_CRATE
);
487 struct Checker
<'tcx
> {
491 impl Visitor
<'tcx
> for Checker
<'tcx
> {
492 type Map
= Map
<'tcx
>;
494 /// Because stability levels are scoped lexically, we want to walk
495 /// nested items in the context of the outer item, so enable
497 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
498 NestedVisitorMap
::OnlyBodies(self.tcx
.hir())
501 fn visit_item(&mut self, item
: &'tcx hir
::Item
<'tcx
>) {
503 hir
::ItemKind
::ExternCrate(_
) => {
504 // compiler-generated `extern crate` items have a dummy span.
505 if item
.span
.is_dummy() {
509 let def_id
= self.tcx
.hir().local_def_id(item
.hir_id
);
510 let cnum
= match self.tcx
.extern_mod_stmt_cnum(def_id
) {
514 let def_id
= DefId { krate: cnum, index: CRATE_DEF_INDEX }
;
515 self.tcx
.check_stability(def_id
, Some(item
.hir_id
), item
.span
);
518 // For implementations of traits, check the stability of each item
519 // individually as it's possible to have a stable trait with unstable
521 hir
::ItemKind
::Impl { of_trait: Some(ref t), items, .. }
=> {
522 if let Res
::Def(DefKind
::Trait
, trait_did
) = t
.path
.res
{
523 for impl_item_ref
in items
{
524 let impl_item
= self.tcx
.hir().impl_item(impl_item_ref
.id
);
525 let trait_item_def_id
= self
527 .associated_items(trait_did
)
528 .filter_by_name_unhygienic(impl_item
.ident
.name
)
530 .map(|item
| item
.def_id
);
531 if let Some(def_id
) = trait_item_def_id
{
532 // Pass `None` to skip deprecation warnings.
533 self.tcx
.check_stability(def_id
, None
, impl_item
.span
);
539 // There's no good place to insert stability check for non-Copy unions,
540 // so semi-randomly perform it here in stability.rs
541 hir
::ItemKind
::Union(..) if !self.tcx
.features().untagged_unions
=> {
542 let def_id
= self.tcx
.hir().local_def_id(item
.hir_id
);
543 let adt_def
= self.tcx
.adt_def(def_id
);
544 let ty
= self.tcx
.type_of(def_id
);
546 if adt_def
.has_dtor(self.tcx
) {
548 &self.tcx
.sess
.parse_sess
,
549 sym
::untagged_unions
,
551 "unions with `Drop` implementations are unstable",
555 let param_env
= self.tcx
.param_env(def_id
);
556 if can_type_implement_copy(self.tcx
, param_env
, ty
).is_err() {
558 &self.tcx
.sess
.parse_sess
,
559 sym
::untagged_unions
,
561 "unions with non-`Copy` fields are unstable",
570 intravisit
::walk_item(self, item
);
573 fn visit_path(&mut self, path
: &'tcx hir
::Path
<'tcx
>, id
: hir
::HirId
) {
574 if let Some(def_id
) = path
.res
.opt_def_id() {
575 self.tcx
.check_stability(def_id
, Some(id
), path
.span
)
577 intravisit
::walk_path(self, path
)
581 /// Given the list of enabled features that were not language features (i.e., that
582 /// were expected to be library features), and the list of features used from
583 /// libraries, identify activated features that don't exist and error about them.
584 pub fn check_unused_or_stable_features(tcx
: TyCtxt
<'_
>) {
585 let access_levels
= &tcx
.privacy_access_levels(LOCAL_CRATE
);
587 if tcx
.stability().staged_api
[&LOCAL_CRATE
] {
588 let krate
= tcx
.hir().krate();
589 let mut missing
= MissingStabilityAnnotations { tcx, access_levels }
;
590 missing
.check_missing_stability(hir
::CRATE_HIR_ID
, krate
.item
.span
);
591 intravisit
::walk_crate(&mut missing
, krate
);
592 krate
.visit_all_item_likes(&mut missing
.as_deep_visitor());
595 let declared_lang_features
= &tcx
.features().declared_lang_features
;
596 let mut lang_features
= FxHashSet
::default();
597 for &(feature
, span
, since
) in declared_lang_features
{
598 if let Some(since
) = since
{
599 // Warn if the user has enabled an already-stable lang feature.
600 unnecessary_stable_feature_lint(tcx
, span
, feature
, since
);
602 if !lang_features
.insert(feature
) {
603 // Warn if the user enables a lang feature multiple times.
604 duplicate_feature_err(tcx
.sess
, span
, feature
);
608 let declared_lib_features
= &tcx
.features().declared_lib_features
;
609 let mut remaining_lib_features
= FxHashMap
::default();
610 for (feature
, span
) in declared_lib_features
{
611 if remaining_lib_features
.contains_key(&feature
) {
612 // Warn if the user enables a lib feature multiple times.
613 duplicate_feature_err(tcx
.sess
, *span
, *feature
);
615 remaining_lib_features
.insert(feature
, *span
);
617 // `stdbuild` has special handling for `libc`, so we need to
618 // recognise the feature when building std.
619 // Likewise, libtest is handled specially, so `test` isn't
620 // available as we'd like it to be.
621 // FIXME: only remove `libc` when `stdbuild` is active.
622 // FIXME: remove special casing for `test`.
623 remaining_lib_features
.remove(&Symbol
::intern("libc"));
624 remaining_lib_features
.remove(&sym
::test
);
626 let check_features
= |remaining_lib_features
: &mut FxHashMap
<_
, _
>, defined_features
: &[_
]| {
627 for &(feature
, since
) in defined_features
{
628 if let Some(since
) = since
{
629 if let Some(span
) = remaining_lib_features
.get(&feature
) {
630 // Warn if the user has enabled an already-stable lib feature.
631 unnecessary_stable_feature_lint(tcx
, *span
, feature
, since
);
634 remaining_lib_features
.remove(&feature
);
635 if remaining_lib_features
.is_empty() {
641 // We always collect the lib features declared in the current crate, even if there are
642 // no unknown features, because the collection also does feature attribute validation.
643 let local_defined_features
= tcx
.lib_features().to_vec();
644 if !remaining_lib_features
.is_empty() {
645 check_features(&mut remaining_lib_features
, &local_defined_features
);
647 for &cnum
in &*tcx
.crates() {
648 if remaining_lib_features
.is_empty() {
651 check_features(&mut remaining_lib_features
, tcx
.defined_lib_features(cnum
));
655 for (feature
, span
) in remaining_lib_features
{
656 struct_span_err
!(tcx
.sess
, span
, E0635
, "unknown feature `{}`", feature
).emit();
659 // FIXME(#44232): the `used_features` table no longer exists, so we
660 // don't lint about unused features. We should re-enable this one day!
663 fn unnecessary_stable_feature_lint(tcx
: TyCtxt
<'_
>, span
: Span
, feature
: Symbol
, since
: Symbol
) {
664 tcx
.struct_span_lint_hir(lint
::builtin
::STABLE_FEATURES
, hir
::CRATE_HIR_ID
, span
, |lint
| {
666 "the feature `{}` has been stable since {} and no longer requires \
667 an attribute to enable",
674 fn duplicate_feature_err(sess
: &Session
, span
: Span
, feature
: Symbol
) {
675 struct_span_err
!(sess
, span
, E0636
, "the feature `{}` has already been declared", feature
)