]> git.lizzy.rs Git - rust.git/blob - src/librustc_ast_passes/feature_gate.rs
Rollup merge of #68084 - estebank:ice-68000, r=varkor
[rust.git] / src / librustc_ast_passes / feature_gate.rs
1 use rustc_error_codes::*;
2 use rustc_errors::{struct_span_err, Handler};
3 use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP};
4 use rustc_feature::{Features, GateIssue, UnstableFeatures};
5 use rustc_span::source_map::Spanned;
6 use rustc_span::symbol::sym;
7 use rustc_span::Span;
8 use syntax::ast::{self, AssocTyConstraint, AssocTyConstraintKind, NodeId};
9 use syntax::ast::{GenericParam, GenericParamKind, PatKind, RangeEnd, VariantData};
10 use syntax::attr;
11 use syntax::sess::{feature_err, leveled_feature_err, GateStrength, ParseSess};
12 use syntax::visit::{self, FnKind, Visitor};
13
14 use log::debug;
15
16 macro_rules! gate_feature_fn {
17     ($cx: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $level: expr) => {{
18         let (cx, has_feature, span, name, explain, level) =
19             (&*$cx, $has_feature, $span, $name, $explain, $level);
20         let has_feature: bool = has_feature(&$cx.features);
21         debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
22         if !has_feature && !span.allows_unstable($name) {
23             leveled_feature_err(cx.parse_sess, name, span, GateIssue::Language, explain, level)
24                 .emit();
25         }
26     }};
27 }
28
29 macro_rules! gate_feature {
30     ($cx: expr, $feature: ident, $span: expr, $explain: expr) => {
31         gate_feature_fn!(
32             $cx,
33             |x: &Features| x.$feature,
34             $span,
35             sym::$feature,
36             $explain,
37             GateStrength::Hard
38         )
39     };
40     ($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {
41         gate_feature_fn!($cx, |x: &Features| x.$feature, $span, sym::$feature, $explain, $level)
42     };
43 }
44
45 pub fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) {
46     PostExpansionVisitor { parse_sess, features }.visit_attribute(attr)
47 }
48
49 struct PostExpansionVisitor<'a> {
50     parse_sess: &'a ParseSess,
51     features: &'a Features,
52 }
53
54 macro_rules! gate_feature_post {
55     ($cx: expr, $feature: ident, $span: expr, $explain: expr) => {{
56         let (cx, span) = ($cx, $span);
57         if !span.allows_unstable(sym::$feature) {
58             gate_feature!(cx, $feature, span, $explain)
59         }
60     }};
61     ($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {{
62         let (cx, span) = ($cx, $span);
63         if !span.allows_unstable(sym::$feature) {
64             gate_feature!(cx, $feature, span, $explain, $level)
65         }
66     }};
67 }
68
69 impl<'a> PostExpansionVisitor<'a> {
70     fn check_abi(&self, abi: ast::StrLit) {
71         let ast::StrLit { symbol_unescaped, span, .. } = abi;
72
73         match &*symbol_unescaped.as_str() {
74             // Stable
75             "Rust" | "C" | "cdecl" | "stdcall" | "fastcall" | "aapcs" | "win64" | "sysv64"
76             | "system" => {}
77             "rust-intrinsic" => {
78                 gate_feature_post!(&self, intrinsics, span, "intrinsics are subject to change");
79             }
80             "platform-intrinsic" => {
81                 gate_feature_post!(
82                     &self,
83                     platform_intrinsics,
84                     span,
85                     "platform intrinsics are experimental and possibly buggy"
86                 );
87             }
88             "vectorcall" => {
89                 gate_feature_post!(
90                     &self,
91                     abi_vectorcall,
92                     span,
93                     "vectorcall is experimental and subject to change"
94                 );
95             }
96             "thiscall" => {
97                 gate_feature_post!(
98                     &self,
99                     abi_thiscall,
100                     span,
101                     "thiscall is experimental and subject to change"
102                 );
103             }
104             "rust-call" => {
105                 gate_feature_post!(
106                     &self,
107                     unboxed_closures,
108                     span,
109                     "rust-call ABI is subject to change"
110                 );
111             }
112             "ptx-kernel" => {
113                 gate_feature_post!(
114                     &self,
115                     abi_ptx,
116                     span,
117                     "PTX ABIs are experimental and subject to change"
118                 );
119             }
120             "unadjusted" => {
121                 gate_feature_post!(
122                     &self,
123                     abi_unadjusted,
124                     span,
125                     "unadjusted ABI is an implementation detail and perma-unstable"
126                 );
127             }
128             "msp430-interrupt" => {
129                 gate_feature_post!(
130                     &self,
131                     abi_msp430_interrupt,
132                     span,
133                     "msp430-interrupt ABI is experimental and subject to change"
134                 );
135             }
136             "x86-interrupt" => {
137                 gate_feature_post!(
138                     &self,
139                     abi_x86_interrupt,
140                     span,
141                     "x86-interrupt ABI is experimental and subject to change"
142                 );
143             }
144             "amdgpu-kernel" => {
145                 gate_feature_post!(
146                     &self,
147                     abi_amdgpu_kernel,
148                     span,
149                     "amdgpu-kernel ABI is experimental and subject to change"
150                 );
151             }
152             "efiapi" => {
153                 gate_feature_post!(
154                     &self,
155                     abi_efiapi,
156                     span,
157                     "efiapi ABI is experimental and subject to change"
158                 );
159             }
160             abi => self
161                 .parse_sess
162                 .span_diagnostic
163                 .delay_span_bug(span, &format!("unrecognized ABI not caught in lowering: {}", abi)),
164         }
165     }
166
167     fn check_extern(&self, ext: ast::Extern) {
168         if let ast::Extern::Explicit(abi) = ext {
169             self.check_abi(abi);
170         }
171     }
172
173     fn maybe_report_invalid_custom_discriminants(&self, variants: &[ast::Variant]) {
174         let has_fields = variants.iter().any(|variant| match variant.data {
175             VariantData::Tuple(..) | VariantData::Struct(..) => true,
176             VariantData::Unit(..) => false,
177         });
178
179         let discriminant_spans = variants
180             .iter()
181             .filter(|variant| match variant.data {
182                 VariantData::Tuple(..) | VariantData::Struct(..) => false,
183                 VariantData::Unit(..) => true,
184             })
185             .filter_map(|variant| variant.disr_expr.as_ref().map(|c| c.value.span))
186             .collect::<Vec<_>>();
187
188         if !discriminant_spans.is_empty() && has_fields {
189             let mut err = feature_err(
190                 self.parse_sess,
191                 sym::arbitrary_enum_discriminant,
192                 discriminant_spans.clone(),
193                 "custom discriminant values are not allowed in enums with tuple or struct variants",
194             );
195             for sp in discriminant_spans {
196                 err.span_label(sp, "disallowed custom discriminant");
197             }
198             for variant in variants.iter() {
199                 match &variant.data {
200                     VariantData::Struct(..) => {
201                         err.span_label(variant.span, "struct variant defined here");
202                     }
203                     VariantData::Tuple(..) => {
204                         err.span_label(variant.span, "tuple variant defined here");
205                     }
206                     VariantData::Unit(..) => {}
207                 }
208             }
209             err.emit();
210         }
211     }
212
213     fn check_gat(&self, generics: &ast::Generics, span: Span) {
214         if !generics.params.is_empty() {
215             gate_feature_post!(
216                 &self,
217                 generic_associated_types,
218                 span,
219                 "generic associated types are unstable"
220             );
221         }
222         if !generics.where_clause.predicates.is_empty() {
223             gate_feature_post!(
224                 &self,
225                 generic_associated_types,
226                 span,
227                 "where clauses on associated types are unstable"
228             );
229         }
230     }
231
232     /// Feature gate `impl Trait` inside `type Alias = $type_expr;`.
233     fn check_impl_trait(&self, ty: &ast::Ty) {
234         struct ImplTraitVisitor<'a> {
235             vis: &'a PostExpansionVisitor<'a>,
236         }
237         impl Visitor<'_> for ImplTraitVisitor<'_> {
238             fn visit_ty(&mut self, ty: &ast::Ty) {
239                 if let ast::TyKind::ImplTrait(..) = ty.kind {
240                     gate_feature_post!(
241                         &self.vis,
242                         type_alias_impl_trait,
243                         ty.span,
244                         "`impl Trait` in type aliases is unstable"
245                     );
246                 }
247                 visit::walk_ty(self, ty);
248             }
249         }
250         ImplTraitVisitor { vis: self }.visit_ty(ty);
251     }
252 }
253
254 impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
255     fn visit_attribute(&mut self, attr: &ast::Attribute) {
256         let attr_info =
257             attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a);
258         // Check feature gates for built-in attributes.
259         if let Some((.., AttributeGate::Gated(_, name, descr, has_feature))) = attr_info {
260             gate_feature_fn!(self, has_feature, attr.span, name, descr, GateStrength::Hard);
261         }
262         // Check unstable flavors of the `#[doc]` attribute.
263         if attr.check_name(sym::doc) {
264             for nested_meta in attr.meta_item_list().unwrap_or_default() {
265                 macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => {
266                     $(if nested_meta.check_name(sym::$name) {
267                         let msg = concat!("`#[doc(", stringify!($name), ")]` is experimental");
268                         gate_feature!(self, $feature, attr.span, msg);
269                     })*
270                 }}
271
272                 gate_doc!(
273                     include => external_doc
274                     cfg => doc_cfg
275                     masked => doc_masked
276                     spotlight => doc_spotlight
277                     alias => doc_alias
278                     keyword => doc_keyword
279                 );
280             }
281         }
282     }
283
284     fn visit_name(&mut self, sp: Span, name: ast::Name) {
285         if !name.as_str().is_ascii() {
286             gate_feature_post!(
287                 &self,
288                 non_ascii_idents,
289                 self.parse_sess.source_map().def_span(sp),
290                 "non-ascii idents are not fully supported"
291             );
292         }
293     }
294
295     fn visit_item(&mut self, i: &'a ast::Item) {
296         match i.kind {
297             ast::ItemKind::ForeignMod(ref foreign_module) => {
298                 if let Some(abi) = foreign_module.abi {
299                     self.check_abi(abi);
300                 }
301             }
302
303             ast::ItemKind::Fn(..) => {
304                 if attr::contains_name(&i.attrs[..], sym::plugin_registrar) {
305                     gate_feature_post!(
306                         &self,
307                         plugin_registrar,
308                         i.span,
309                         "compiler plugins are experimental and possibly buggy"
310                     );
311                 }
312                 if attr::contains_name(&i.attrs[..], sym::start) {
313                     gate_feature_post!(
314                         &self,
315                         start,
316                         i.span,
317                         "`#[start]` functions are experimental \
318                                        and their signature may change \
319                                        over time"
320                     );
321                 }
322                 if attr::contains_name(&i.attrs[..], sym::main) {
323                     gate_feature_post!(
324                         &self,
325                         main,
326                         i.span,
327                         "declaration of a non-standard `#[main]` \
328                                         function may change over time, for now \
329                                         a top-level `fn main()` is required"
330                     );
331                 }
332             }
333
334             ast::ItemKind::Struct(..) => {
335                 for attr in attr::filter_by_name(&i.attrs[..], sym::repr) {
336                     for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
337                         if item.check_name(sym::simd) {
338                             gate_feature_post!(
339                                 &self,
340                                 repr_simd,
341                                 attr.span,
342                                 "SIMD types are experimental and possibly buggy"
343                             );
344                         }
345                     }
346                 }
347             }
348
349             ast::ItemKind::Enum(ast::EnumDef { ref variants, .. }, ..) => {
350                 for variant in variants {
351                     match (&variant.data, &variant.disr_expr) {
352                         (ast::VariantData::Unit(..), _) => {}
353                         (_, Some(disr_expr)) => gate_feature_post!(
354                             &self,
355                             arbitrary_enum_discriminant,
356                             disr_expr.value.span,
357                             "discriminants on non-unit variants are experimental"
358                         ),
359                         _ => {}
360                     }
361                 }
362
363                 let has_feature = self.features.arbitrary_enum_discriminant;
364                 if !has_feature && !i.span.allows_unstable(sym::arbitrary_enum_discriminant) {
365                     self.maybe_report_invalid_custom_discriminants(&variants);
366                 }
367             }
368
369             ast::ItemKind::Impl(_, polarity, defaultness, ..) => {
370                 if polarity == ast::ImplPolarity::Negative {
371                     gate_feature_post!(
372                         &self,
373                         optin_builtin_traits,
374                         i.span,
375                         "negative trait bounds are not yet fully implemented; \
376                                         use marker types for now"
377                     );
378                 }
379
380                 if let ast::Defaultness::Default = defaultness {
381                     gate_feature_post!(&self, specialization, i.span, "specialization is unstable");
382                 }
383             }
384
385             ast::ItemKind::Trait(ast::IsAuto::Yes, ..) => {
386                 gate_feature_post!(
387                     &self,
388                     optin_builtin_traits,
389                     i.span,
390                     "auto traits are experimental and possibly buggy"
391                 );
392             }
393
394             ast::ItemKind::TraitAlias(..) => {
395                 gate_feature_post!(&self, trait_alias, i.span, "trait aliases are experimental");
396             }
397
398             ast::ItemKind::MacroDef(ast::MacroDef { legacy: false, .. }) => {
399                 let msg = "`macro` is experimental";
400                 gate_feature_post!(&self, decl_macro, i.span, msg);
401             }
402
403             ast::ItemKind::TyAlias(ref ty, ..) => self.check_impl_trait(&ty),
404
405             _ => {}
406         }
407
408         visit::walk_item(self, i);
409     }
410
411     fn visit_foreign_item(&mut self, i: &'a ast::ForeignItem) {
412         match i.kind {
413             ast::ForeignItemKind::Fn(..) | ast::ForeignItemKind::Static(..) => {
414                 let link_name = attr::first_attr_value_str_by_name(&i.attrs, sym::link_name);
415                 let links_to_llvm = match link_name {
416                     Some(val) => val.as_str().starts_with("llvm."),
417                     _ => false,
418                 };
419                 if links_to_llvm {
420                     gate_feature_post!(
421                         &self,
422                         link_llvm_intrinsics,
423                         i.span,
424                         "linking to LLVM intrinsics is experimental"
425                     );
426                 }
427             }
428             ast::ForeignItemKind::Ty => {
429                 gate_feature_post!(&self, extern_types, i.span, "extern types are experimental");
430             }
431             ast::ForeignItemKind::Macro(..) => {}
432         }
433
434         visit::walk_foreign_item(self, i)
435     }
436
437     fn visit_ty(&mut self, ty: &'a ast::Ty) {
438         match ty.kind {
439             ast::TyKind::BareFn(ref bare_fn_ty) => {
440                 self.check_extern(bare_fn_ty.ext);
441             }
442             ast::TyKind::Never => {
443                 gate_feature_post!(&self, never_type, ty.span, "The `!` type is experimental");
444             }
445             _ => {}
446         }
447         visit::walk_ty(self, ty)
448     }
449
450     fn visit_fn_ret_ty(&mut self, ret_ty: &'a ast::FunctionRetTy) {
451         if let ast::FunctionRetTy::Ty(ref output_ty) = *ret_ty {
452             if let ast::TyKind::Never = output_ty.kind {
453                 // Do nothing.
454             } else {
455                 self.visit_ty(output_ty)
456             }
457         }
458     }
459
460     fn visit_expr(&mut self, e: &'a ast::Expr) {
461         match e.kind {
462             ast::ExprKind::Box(_) => {
463                 gate_feature_post!(
464                     &self,
465                     box_syntax,
466                     e.span,
467                     "box expression syntax is experimental; you can call `Box::new` instead"
468                 );
469             }
470             ast::ExprKind::Type(..) => {
471                 // To avoid noise about type ascription in common syntax errors, only emit if it
472                 // is the *only* error.
473                 if self.parse_sess.span_diagnostic.err_count() == 0 {
474                     gate_feature_post!(
475                         &self,
476                         type_ascription,
477                         e.span,
478                         "type ascription is experimental"
479                     );
480                 }
481             }
482             ast::ExprKind::TryBlock(_) => {
483                 gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
484             }
485             ast::ExprKind::Block(_, opt_label) => {
486                 if let Some(label) = opt_label {
487                     gate_feature_post!(
488                         &self,
489                         label_break_value,
490                         label.ident.span,
491                         "labels on blocks are unstable"
492                     );
493                 }
494             }
495             _ => {}
496         }
497         visit::walk_expr(self, e)
498     }
499
500     fn visit_arm(&mut self, arm: &'a ast::Arm) {
501         visit::walk_arm(self, arm)
502     }
503
504     fn visit_pat(&mut self, pattern: &'a ast::Pat) {
505         match &pattern.kind {
506             PatKind::Slice(pats) => {
507                 for pat in &*pats {
508                     let span = pat.span;
509                     let inner_pat = match &pat.kind {
510                         PatKind::Ident(.., Some(pat)) => pat,
511                         _ => pat,
512                     };
513                     if inner_pat.is_rest() {
514                         gate_feature_post!(
515                             &self,
516                             slice_patterns,
517                             span,
518                             "subslice patterns are unstable"
519                         );
520                     }
521                 }
522             }
523             PatKind::Box(..) => {
524                 gate_feature_post!(
525                     &self,
526                     box_patterns,
527                     pattern.span,
528                     "box pattern syntax is experimental"
529                 );
530             }
531             PatKind::Range(_, _, Spanned { node: RangeEnd::Excluded, .. }) => {
532                 gate_feature_post!(
533                     &self,
534                     exclusive_range_pattern,
535                     pattern.span,
536                     "exclusive range pattern syntax is experimental"
537                 );
538             }
539             _ => {}
540         }
541         visit::walk_pat(self, pattern)
542     }
543
544     fn visit_fn(
545         &mut self,
546         fn_kind: FnKind<'a>,
547         fn_decl: &'a ast::FnDecl,
548         span: Span,
549         _node_id: NodeId,
550     ) {
551         if let Some(header) = fn_kind.header() {
552             // Stability of const fn methods are covered in
553             // `visit_trait_item` and `visit_impl_item` below; this is
554             // because default methods don't pass through this point.
555             self.check_extern(header.ext);
556         }
557
558         if fn_decl.c_variadic() {
559             gate_feature_post!(&self, c_variadic, span, "C-variadic functions are unstable");
560         }
561
562         visit::walk_fn(self, fn_kind, fn_decl, span)
563     }
564
565     fn visit_generic_param(&mut self, param: &'a GenericParam) {
566         match param.kind {
567             GenericParamKind::Const { .. } => gate_feature_post!(
568                 &self,
569                 const_generics,
570                 param.ident.span,
571                 "const generics are unstable"
572             ),
573             _ => {}
574         }
575         visit::walk_generic_param(self, param)
576     }
577
578     fn visit_assoc_ty_constraint(&mut self, constraint: &'a AssocTyConstraint) {
579         match constraint.kind {
580             AssocTyConstraintKind::Bound { .. } => gate_feature_post!(
581                 &self,
582                 associated_type_bounds,
583                 constraint.span,
584                 "associated type bounds are unstable"
585             ),
586             _ => {}
587         }
588         visit::walk_assoc_ty_constraint(self, constraint)
589     }
590
591     fn visit_trait_item(&mut self, ti: &'a ast::AssocItem) {
592         match ti.kind {
593             ast::AssocItemKind::Fn(ref sig, ref block) => {
594                 if block.is_none() {
595                     self.check_extern(sig.header.ext);
596                 }
597                 if sig.header.constness.node == ast::Constness::Const {
598                     gate_feature_post!(&self, const_fn, ti.span, "const fn is unstable");
599                 }
600             }
601             ast::AssocItemKind::TyAlias(_, ref default) => {
602                 if let Some(_) = default {
603                     gate_feature_post!(
604                         &self,
605                         associated_type_defaults,
606                         ti.span,
607                         "associated type defaults are unstable"
608                     );
609                 }
610             }
611             _ => {}
612         }
613         visit::walk_trait_item(self, ti)
614     }
615
616     fn visit_assoc_item(&mut self, ii: &'a ast::AssocItem) {
617         if ii.defaultness == ast::Defaultness::Default {
618             gate_feature_post!(&self, specialization, ii.span, "specialization is unstable");
619         }
620
621         match ii.kind {
622             ast::AssocItemKind::Fn(ref sig, _) => {
623                 if sig.decl.c_variadic() {
624                     gate_feature_post!(
625                         &self,
626                         c_variadic,
627                         ii.span,
628                         "C-variadic functions are unstable"
629                     );
630                 }
631             }
632             ast::AssocItemKind::TyAlias(_, ref ty) => {
633                 if let Some(ty) = ty {
634                     self.check_impl_trait(ty);
635                 }
636                 self.check_gat(&ii.generics, ii.span);
637             }
638             _ => {}
639         }
640         visit::walk_assoc_item(self, ii)
641     }
642
643     fn visit_vis(&mut self, vis: &'a ast::Visibility) {
644         if let ast::VisibilityKind::Crate(ast::CrateSugar::JustCrate) = vis.node {
645             gate_feature_post!(
646                 &self,
647                 crate_visibility_modifier,
648                 vis.span,
649                 "`crate` visibility modifier is experimental"
650             );
651         }
652         visit::walk_vis(self, vis)
653     }
654 }
655
656 pub fn check_crate(
657     krate: &ast::Crate,
658     parse_sess: &ParseSess,
659     features: &Features,
660     unstable: UnstableFeatures,
661 ) {
662     maybe_stage_features(&parse_sess.span_diagnostic, krate, unstable);
663     let mut visitor = PostExpansionVisitor { parse_sess, features };
664
665     let spans = parse_sess.gated_spans.spans.borrow();
666     macro_rules! gate_all {
667         ($gate:ident, $msg:literal) => {
668             for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
669                 gate_feature!(&visitor, $gate, *span, $msg);
670             }
671         };
672     }
673     gate_all!(let_chains, "`let` expressions in this position are experimental");
674     gate_all!(async_closure, "async closures are unstable");
675     gate_all!(generators, "yield syntax is experimental");
676     gate_all!(or_patterns, "or-patterns syntax is experimental");
677     gate_all!(const_extern_fn, "`const extern fn` definitions are unstable");
678     gate_all!(raw_ref_op, "raw address of syntax is experimental");
679     gate_all!(const_trait_bound_opt_out, "`?const` on trait bounds is experimental");
680     gate_all!(const_trait_impl, "const trait impls are experimental");
681     gate_all!(half_open_range_patterns, "half-open range patterns are unstable");
682
683     // All uses of `gate_all!` below this point were added in #65742,
684     // and subsequently disabled (with the non-early gating readded).
685     macro_rules! gate_all {
686         ($gate:ident, $msg:literal) => {
687             // FIXME(eddyb) do something more useful than always
688             // disabling these uses of early feature-gatings.
689             if false {
690                 for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
691                     gate_feature!(&visitor, $gate, *span, $msg);
692                 }
693             }
694         };
695     }
696
697     gate_all!(trait_alias, "trait aliases are experimental");
698     gate_all!(associated_type_bounds, "associated type bounds are unstable");
699     gate_all!(crate_visibility_modifier, "`crate` visibility modifier is experimental");
700     gate_all!(const_generics, "const generics are unstable");
701     gate_all!(decl_macro, "`macro` is experimental");
702     gate_all!(box_patterns, "box pattern syntax is experimental");
703     gate_all!(exclusive_range_pattern, "exclusive range pattern syntax is experimental");
704     gate_all!(try_blocks, "`try` blocks are unstable");
705     gate_all!(label_break_value, "labels on blocks are unstable");
706     gate_all!(box_syntax, "box expression syntax is experimental; you can call `Box::new` instead");
707     // To avoid noise about type ascription in common syntax errors,
708     // only emit if it is the *only* error. (Also check it last.)
709     if parse_sess.span_diagnostic.err_count() == 0 {
710         gate_all!(type_ascription, "type ascription is experimental");
711     }
712
713     visit::walk_crate(&mut visitor, krate);
714 }
715
716 fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate, unstable: UnstableFeatures) {
717     if !unstable.is_nightly_build() {
718         for attr in krate.attrs.iter().filter(|attr| attr.check_name(sym::feature)) {
719             struct_span_err!(
720                 span_handler,
721                 attr.span,
722                 E0554,
723                 "`#![feature]` may not be used on the {} release channel",
724                 option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)")
725             )
726             .emit();
727         }
728     }
729 }