]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/feature_gate.rs
55087c257033756c01e2707ec32d69f066a8c5b3
[rust.git] / src / libsyntax / feature_gate.rs
1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Feature gating
12 //!
13 //! This module implements the gating necessary for preventing certain compiler
14 //! features from being used by default. This module will crawl a pre-expanded
15 //! AST to ensure that there are no features which are used that are not
16 //! enabled.
17 //!
18 //! Features are enabled in programs via the crate-level attributes of
19 //! `#![feature(...)]` with a comma-separated list of features.
20 //!
21 //! For the purpose of future feature-tracking, once code for detection of feature
22 //! gate usage is added, *do not remove it again* even once the feature
23 //! becomes stable.
24
25 use self::Status::*;
26 use self::AttributeType::*;
27 use self::AttributeGate::*;
28
29 use abi::Abi;
30 use ast::NodeId;
31 use ast;
32 use attr;
33 use attr::AttrMetaMethods;
34 use codemap::{CodeMap, Span};
35 use errors::Handler;
36 use visit;
37 use visit::{FnKind, Visitor};
38 use parse::token::InternedString;
39
40 use std::ascii::AsciiExt;
41 use std::cmp;
42
43 // If you change this list without updating src/doc/reference.md, @cmr will be sad
44 // Don't ever remove anything from this list; set them to 'Removed'.
45 // The version numbers here correspond to the version in which the current status
46 // was set. This is most important for knowing when a particular feature became
47 // stable (active).
48 // NB: The featureck.py script parses this information directly out of the source
49 // so take care when modifying it.
50 const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status)] = &[
51     ("globs", "1.0.0", None, Accepted),
52     ("macro_rules", "1.0.0", None, Accepted),
53     ("struct_variant", "1.0.0", None, Accepted),
54     ("asm", "1.0.0", Some(29722), Active),
55     ("managed_boxes", "1.0.0", None, Removed),
56     ("non_ascii_idents", "1.0.0", Some(28979), Active),
57     ("thread_local", "1.0.0", Some(29594), Active),
58     ("link_args", "1.0.0", Some(29596), Active),
59     ("plugin_registrar", "1.0.0", Some(29597), Active),
60     ("log_syntax", "1.0.0", Some(29598), Active),
61     ("trace_macros", "1.0.0", Some(29598), Active),
62     ("concat_idents", "1.0.0", Some(29599), Active),
63
64     // rustc internal, for now:
65     ("intrinsics", "1.0.0", None, Active),
66     ("lang_items", "1.0.0", None, Active),
67
68     ("simd", "1.0.0", Some(27731), Active),
69     ("default_type_params", "1.0.0", None, Accepted),
70     ("quote", "1.0.0", Some(29601), Active),
71     ("link_llvm_intrinsics", "1.0.0", Some(29602), Active),
72     ("linkage", "1.0.0", Some(29603), Active),
73     ("struct_inherit", "1.0.0", None, Removed),
74
75     ("quad_precision_float", "1.0.0", None, Removed),
76
77     // rustc internal
78     ("rustc_diagnostic_macros", "1.0.0", None, Active),
79     ("unboxed_closures", "1.0.0", Some(29625), Active),
80     ("reflect", "1.0.0", Some(27749), Active),
81     ("import_shadowing", "1.0.0", None, Removed),
82     ("advanced_slice_patterns", "1.0.0", Some(23121), Active),
83     ("tuple_indexing", "1.0.0", None, Accepted),
84     ("associated_types", "1.0.0", None, Accepted),
85     ("visible_private_types", "1.0.0", None, Removed),
86     ("slicing_syntax", "1.0.0", None, Accepted),
87     ("box_syntax", "1.0.0", Some(27779), Active),
88     ("placement_in_syntax", "1.0.0", Some(27779), Active),
89
90     // rustc internal.
91     ("pushpop_unsafe", "1.2.0", None, Active),
92
93     ("on_unimplemented", "1.0.0", Some(29628), Active),
94     ("simd_ffi", "1.0.0", Some(27731), Active),
95     ("allocator", "1.0.0", Some(27389), Active),
96     ("needs_allocator", "1.4.0", Some(27389), Active),
97     ("linked_from", "1.3.0", Some(29629), Active),
98
99     ("if_let", "1.0.0", None, Accepted),
100     ("while_let", "1.0.0", None, Accepted),
101
102     ("plugin", "1.0.0", Some(29597), Active),
103     ("start", "1.0.0", Some(29633), Active),
104     ("main", "1.0.0", Some(29634), Active),
105
106     ("fundamental", "1.0.0", Some(29635), Active),
107
108     // A temporary feature gate used to enable parser extensions needed
109     // to bootstrap fix for #5723.
110     ("issue_5723_bootstrap", "1.0.0", None, Accepted),
111
112     // A way to temporarily opt out of opt in copy. This will *never* be accepted.
113     ("opt_out_copy", "1.0.0", None, Removed),
114
115     // OIBIT specific features
116     ("optin_builtin_traits", "1.0.0", Some(13231), Active),
117
118     // macro reexport needs more discussion and stabilization
119     ("macro_reexport", "1.0.0", Some(29638), Active),
120
121     // These are used to test this portion of the compiler, they don't actually
122     // mean anything
123     ("test_accepted_feature", "1.0.0", None, Accepted),
124     ("test_removed_feature", "1.0.0", None, Removed),
125
126     // Allows use of #[staged_api]
127     // rustc internal
128     ("staged_api", "1.0.0", None, Active),
129
130     // Allows using items which are missing stability attributes
131     // rustc internal
132     ("unmarked_api", "1.0.0", None, Active),
133
134     // Allows using #![no_std]
135     ("no_std", "1.0.0", None, Accepted),
136
137     // Allows using #![no_core]
138     ("no_core", "1.3.0", Some(29639), Active),
139
140     // Allows using `box` in patterns; RFC 469
141     ("box_patterns", "1.0.0", Some(29641), Active),
142
143     // Allows using the unsafe_no_drop_flag attribute (unlikely to
144     // switch to Accepted; see RFC 320)
145     ("unsafe_no_drop_flag", "1.0.0", None, Active),
146
147     // Allows using the unsafe_destructor_blind_to_params attribute;
148     // RFC 1238
149     ("dropck_parametricity", "1.3.0", Some(28498), Active),
150
151     // Allows the use of custom attributes; RFC 572
152     ("custom_attribute", "1.0.0", Some(29642), Active),
153
154     // Allows the use of #[derive(Anything)] as sugar for
155     // #[derive_Anything].
156     ("custom_derive", "1.0.0", Some(29644), Active),
157
158     // Allows the use of rustc_* attributes; RFC 572
159     ("rustc_attrs", "1.0.0", Some(29642), Active),
160
161     // Allows the use of #[allow_internal_unstable]. This is an
162     // attribute on macro_rules! and can't use the attribute handling
163     // below (it has to be checked before expansion possibly makes
164     // macros disappear).
165     //
166     // rustc internal
167     ("allow_internal_unstable", "1.0.0", None, Active),
168
169     // #23121. Array patterns have some hazards yet.
170     ("slice_patterns", "1.0.0", Some(23121), Active),
171
172     // Allows use of unary negate on unsigned integers, e.g. -e for e: u8
173     ("negate_unsigned", "1.0.0", Some(29645), Removed),
174
175     // Allows the definition of associated constants in `trait` or `impl`
176     // blocks.
177     ("associated_consts", "1.0.0", Some(29646), Active),
178
179     // Allows the definition of `const fn` functions.
180     ("const_fn", "1.2.0", Some(24111), Active),
181
182     // Allows indexing into constant arrays.
183     ("const_indexing", "1.4.0", Some(29947), Active),
184
185     // Allows using #[prelude_import] on glob `use` items.
186     //
187     // rustc internal
188     ("prelude_import", "1.2.0", None, Active),
189
190     // Allows the definition recursive static items.
191     ("static_recursion", "1.3.0", Some(29719), Active),
192
193     // Allows default type parameters to influence type inference.
194     ("default_type_parameter_fallback", "1.3.0", Some(27336), Active),
195
196     // Allows associated type defaults
197     ("associated_type_defaults", "1.2.0", Some(29661), Active),
198
199     // Allows macros to appear in the type position.
200     ("type_macros", "1.3.0", Some(27336), Active),
201
202     // allow `repr(simd)`, and importing the various simd intrinsics
203     ("repr_simd", "1.4.0", Some(27731), Active),
204
205     // Allows cfg(target_feature = "...").
206     ("cfg_target_feature", "1.4.0", Some(29717), Active),
207
208     // allow `extern "platform-intrinsic" { ... }`
209     ("platform_intrinsics", "1.4.0", Some(27731), Active),
210
211     // allow `#[unwind]`
212     // rust runtime internal
213     ("unwind_attributes", "1.4.0", None, Active),
214
215     // allow empty structs and enum variants with braces
216     ("braced_empty_structs", "1.5.0", Some(29720), Active),
217
218     // allow overloading augmented assignment operations like `a += b`
219     ("augmented_assignments", "1.5.0", Some(28235), Active),
220
221     // allow `#[no_debug]`
222     ("no_debug", "1.5.0", Some(29721), Active),
223
224     // allow `#[omit_gdb_pretty_printer_section]`
225     // rustc internal.
226     ("omit_gdb_pretty_printer_section", "1.5.0", None, Active),
227
228     // Allows cfg(target_vendor = "...").
229     ("cfg_target_vendor", "1.5.0", Some(29718), Active),
230
231     // Allow attributes on expressions and non-item statements
232     ("stmt_expr_attributes", "1.6.0", Some(15701), Active),
233
234     // Allows `#[deprecated]` attribute
235     ("deprecated", "1.6.0", Some(29935), Active),
236
237     // allow using type ascription in expressions
238     ("type_ascription", "1.6.0", Some(23416), Active),
239
240     // Allows cfg(target_thread_local)
241     ("cfg_target_thread_local", "1.7.0", Some(29594), Active),
242
243     // rustc internal
244     ("abi_vectorcall", "1.7.0", None, Active)
245 ];
246 // (changing above list without updating src/doc/reference.md makes @cmr sad)
247
248 enum Status {
249     /// Represents an active feature that is currently being implemented or
250     /// currently being considered for addition/removal.
251     Active,
252
253     /// Represents a feature which has since been removed (it was once Active)
254     Removed,
255
256     /// This language feature has since been Accepted (it was once Active)
257     Accepted,
258 }
259
260 // Attributes that have a special meaning to rustc or rustdoc
261 pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGate)] = &[
262     // Normal attributes
263
264     ("warn", Normal, Ungated),
265     ("allow", Normal, Ungated),
266     ("forbid", Normal, Ungated),
267     ("deny", Normal, Ungated),
268
269     ("macro_reexport", Normal, Ungated),
270     ("macro_use", Normal, Ungated),
271     ("macro_export", Normal, Ungated),
272     ("plugin_registrar", Normal, Ungated),
273
274     ("cfg", Normal, Ungated),
275     ("cfg_attr", Normal, Ungated),
276     ("main", Normal, Ungated),
277     ("start", Normal, Ungated),
278     ("test", Normal, Ungated),
279     ("bench", Normal, Ungated),
280     ("simd", Normal, Ungated),
281     ("repr", Normal, Ungated),
282     ("path", Normal, Ungated),
283     ("abi", Normal, Ungated),
284     ("automatically_derived", Normal, Ungated),
285     ("no_mangle", Normal, Ungated),
286     ("no_link", Normal, Ungated),
287     ("derive", Normal, Ungated),
288     ("should_panic", Normal, Ungated),
289     ("ignore", Normal, Ungated),
290     ("no_implicit_prelude", Normal, Ungated),
291     ("reexport_test_harness_main", Normal, Ungated),
292     ("link_args", Normal, Ungated),
293     ("macro_escape", Normal, Ungated),
294
295     // Not used any more, but we can't feature gate it
296     ("no_stack_check", Normal, Ungated),
297
298     ("plugin", CrateLevel, Gated("plugin",
299                                  "compiler plugins are experimental \
300                                   and possibly buggy")),
301     ("no_std", CrateLevel, Ungated),
302     ("no_core", CrateLevel, Gated("no_core",
303                                   "no_core is experimental")),
304     ("lang", Normal, Gated("lang_items",
305                            "language items are subject to change")),
306     ("linkage", Whitelisted, Gated("linkage",
307                                    "the `linkage` attribute is experimental \
308                                     and not portable across platforms")),
309     ("thread_local", Whitelisted, Gated("thread_local",
310                                         "`#[thread_local]` is an experimental feature, and does \
311                                          not currently handle destructors. There is no \
312                                          corresponding `#[task_local]` mapping to the task \
313                                          model")),
314
315     ("rustc_on_unimplemented", Normal, Gated("on_unimplemented",
316                                              "the `#[rustc_on_unimplemented]` attribute \
317                                               is an experimental feature")),
318     ("allocator", Whitelisted, Gated("allocator",
319                                      "the `#[allocator]` attribute is an experimental feature")),
320     ("needs_allocator", Normal, Gated("needs_allocator",
321                                       "the `#[needs_allocator]` \
322                                        attribute is an experimental \
323                                        feature")),
324     ("rustc_variance", Normal, Gated("rustc_attrs",
325                                      "the `#[rustc_variance]` attribute \
326                                       is just used for rustc unit tests \
327                                       and will never be stable")),
328     ("rustc_error", Whitelisted, Gated("rustc_attrs",
329                                        "the `#[rustc_error]` attribute \
330                                         is just used for rustc unit tests \
331                                         and will never be stable")),
332     ("rustc_if_this_changed", Whitelisted, Gated("rustc_attrs",
333                                        "the `#[rustc_if_this_changed]` attribute \
334                                         is just used for rustc unit tests \
335                                         and will never be stable")),
336     ("rustc_then_this_would_need", Whitelisted, Gated("rustc_attrs",
337                                        "the `#[rustc_if_this_changed]` attribute \
338                                         is just used for rustc unit tests \
339                                         and will never be stable")),
340     ("rustc_move_fragments", Normal, Gated("rustc_attrs",
341                                            "the `#[rustc_move_fragments]` attribute \
342                                             is just used for rustc unit tests \
343                                             and will never be stable")),
344     ("rustc_mir", Normal, Gated("rustc_attrs",
345                                 "the `#[rustc_mir]` attribute \
346                                  is just used for rustc unit tests \
347                                  and will never be stable")),
348
349     ("allow_internal_unstable", Normal, Gated("allow_internal_unstable",
350                                               EXPLAIN_ALLOW_INTERNAL_UNSTABLE)),
351
352     ("fundamental", Whitelisted, Gated("fundamental",
353                                        "the `#[fundamental]` attribute \
354                                         is an experimental feature")),
355
356     ("linked_from", Normal, Gated("linked_from",
357                                   "the `#[linked_from]` attribute \
358                                    is an experimental feature")),
359
360     // FIXME: #14408 whitelist docs since rustdoc looks at them
361     ("doc", Whitelisted, Ungated),
362
363     // FIXME: #14406 these are processed in trans, which happens after the
364     // lint pass
365     ("cold", Whitelisted, Ungated),
366     ("export_name", Whitelisted, Ungated),
367     ("inline", Whitelisted, Ungated),
368     ("link", Whitelisted, Ungated),
369     ("link_name", Whitelisted, Ungated),
370     ("link_section", Whitelisted, Ungated),
371     ("no_builtins", Whitelisted, Ungated),
372     ("no_mangle", Whitelisted, Ungated),
373     ("no_debug", Whitelisted, Gated("no_debug",
374                                     "the `#[no_debug]` attribute \
375                                      is an experimental feature")),
376     ("omit_gdb_pretty_printer_section", Whitelisted, Gated("omit_gdb_pretty_printer_section",
377                                                        "the `#[omit_gdb_pretty_printer_section]` \
378                                                         attribute is just used for the Rust test \
379                                                         suite")),
380     ("unsafe_no_drop_flag", Whitelisted, Gated("unsafe_no_drop_flag",
381                                                "unsafe_no_drop_flag has unstable semantics \
382                                                 and may be removed in the future")),
383     ("unsafe_destructor_blind_to_params",
384      Normal,
385      Gated("dropck_parametricity",
386            "unsafe_destructor_blind_to_params has unstable semantics \
387             and may be removed in the future")),
388     ("unwind", Whitelisted, Gated("unwind_attributes", "#[unwind] is experimental")),
389
390     // used in resolve
391     ("prelude_import", Whitelisted, Gated("prelude_import",
392                                           "`#[prelude_import]` is for use by rustc only")),
393
394     // FIXME: #14407 these are only looked at on-demand so we can't
395     // guarantee they'll have already been checked
396     ("rustc_deprecated", Whitelisted, Ungated),
397     ("must_use", Whitelisted, Ungated),
398     ("stable", Whitelisted, Ungated),
399     ("unstable", Whitelisted, Ungated),
400     ("deprecated", Normal, Gated("deprecated", "`#[deprecated]` attribute is unstable")),
401
402     ("rustc_paren_sugar", Normal, Gated("unboxed_closures",
403                                         "unboxed_closures are still evolving")),
404     ("rustc_reflect_like", Whitelisted, Gated("reflect",
405                                               "defining reflective traits is still evolving")),
406
407     // Crate level attributes
408     ("crate_name", CrateLevel, Ungated),
409     ("crate_type", CrateLevel, Ungated),
410     ("crate_id", CrateLevel, Ungated),
411     ("feature", CrateLevel, Ungated),
412     ("no_start", CrateLevel, Ungated),
413     ("no_main", CrateLevel, Ungated),
414     ("no_builtins", CrateLevel, Ungated),
415     ("recursion_limit", CrateLevel, Ungated),
416 ];
417
418 macro_rules! cfg_fn {
419     (|$x: ident| $e: expr) => {{
420         fn f($x: &Features) -> bool {
421             $e
422         }
423         f as fn(&Features) -> bool
424     }}
425 }
426 // cfg(...)'s that are feature gated
427 const GATED_CFGS: &'static [(&'static str, &'static str, fn(&Features) -> bool)] = &[
428     // (name in cfg, feature, function to check if the feature is enabled)
429     ("target_feature", "cfg_target_feature", cfg_fn!(|x| x.cfg_target_feature)),
430     ("target_vendor", "cfg_target_vendor", cfg_fn!(|x| x.cfg_target_vendor)),
431     ("target_thread_local", "cfg_target_thread_local",
432      cfg_fn!(|x| x.cfg_target_thread_local)),
433 ];
434
435 #[derive(Debug, Eq, PartialEq)]
436 pub enum GatedCfgAttr {
437     GatedCfg(GatedCfg),
438     GatedAttr(Span),
439 }
440
441 #[derive(Debug, Eq, PartialEq)]
442 pub struct GatedCfg {
443     span: Span,
444     index: usize,
445 }
446
447 impl Ord for GatedCfgAttr {
448     fn cmp(&self, other: &GatedCfgAttr) -> cmp::Ordering {
449         let to_tup = |s: &GatedCfgAttr| match *s {
450             GatedCfgAttr::GatedCfg(ref gated_cfg) => {
451                 (gated_cfg.span.lo.0, gated_cfg.span.hi.0, gated_cfg.index)
452             }
453             GatedCfgAttr::GatedAttr(ref span) => {
454                 (span.lo.0, span.hi.0, GATED_CFGS.len())
455             }
456         };
457         to_tup(self).cmp(&to_tup(other))
458     }
459 }
460
461 impl PartialOrd for GatedCfgAttr {
462     fn partial_cmp(&self, other: &GatedCfgAttr) -> Option<cmp::Ordering> {
463         Some(self.cmp(other))
464     }
465 }
466
467 impl GatedCfgAttr {
468     pub fn check_and_emit(&self,
469                           diagnostic: &Handler,
470                           features: &Features,
471                           codemap: &CodeMap) {
472         match *self {
473             GatedCfgAttr::GatedCfg(ref cfg) => {
474                 cfg.check_and_emit(diagnostic, features, codemap);
475             }
476             GatedCfgAttr::GatedAttr(span) => {
477                 if !features.stmt_expr_attributes {
478                     emit_feature_err(diagnostic,
479                                      "stmt_expr_attributes",
480                                      span,
481                                      GateIssue::Language,
482                                      EXPLAIN_STMT_ATTR_SYNTAX);
483                 }
484             }
485         }
486     }
487 }
488
489 impl GatedCfg {
490     pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> {
491         let name = cfg.name();
492         GATED_CFGS.iter()
493                   .position(|info| info.0 == name)
494                   .map(|idx| {
495                       GatedCfg {
496                           span: cfg.span,
497                           index: idx
498                       }
499                   })
500     }
501     fn check_and_emit(&self,
502                       diagnostic: &Handler,
503                       features: &Features,
504                       codemap: &CodeMap) {
505         let (cfg, feature, has_feature) = GATED_CFGS[self.index];
506         if !has_feature(features) && !codemap.span_allows_unstable(self.span) {
507             let explain = format!("`cfg({})` is experimental and subject to change", cfg);
508             emit_feature_err(diagnostic, feature, self.span, GateIssue::Language, &explain);
509         }
510     }
511 }
512
513
514 #[derive(PartialEq, Copy, Clone, Debug)]
515 pub enum AttributeType {
516     /// Normal, builtin attribute that is consumed
517     /// by the compiler before the unused_attribute check
518     Normal,
519
520     /// Builtin attribute that may not be consumed by the compiler
521     /// before the unused_attribute check. These attributes
522     /// will be ignored by the unused_attribute lint
523     Whitelisted,
524
525     /// Builtin attribute that is only allowed at the crate level
526     CrateLevel,
527 }
528
529 #[derive(PartialEq, Copy, Clone, Debug)]
530 pub enum AttributeGate {
531     /// Is gated by a given feature gate and reason
532     Gated(&'static str, &'static str),
533
534     /// Ungated attribute, can be used on all release channels
535     Ungated,
536 }
537
538 /// A set of features to be used by later passes.
539 pub struct Features {
540     pub unboxed_closures: bool,
541     pub rustc_diagnostic_macros: bool,
542     pub allow_quote: bool,
543     pub allow_asm: bool,
544     pub allow_log_syntax: bool,
545     pub allow_concat_idents: bool,
546     pub allow_trace_macros: bool,
547     pub allow_internal_unstable: bool,
548     pub allow_custom_derive: bool,
549     pub allow_placement_in: bool,
550     pub allow_box: bool,
551     pub allow_pushpop_unsafe: bool,
552     pub simd_ffi: bool,
553     pub unmarked_api: bool,
554     /// spans of #![feature] attrs for stable language features. for error reporting
555     pub declared_stable_lang_features: Vec<Span>,
556     /// #![feature] attrs for non-language (library) features
557     pub declared_lib_features: Vec<(InternedString, Span)>,
558     pub const_fn: bool,
559     pub const_indexing: bool,
560     pub static_recursion: bool,
561     pub default_type_parameter_fallback: bool,
562     pub type_macros: bool,
563     pub cfg_target_feature: bool,
564     pub cfg_target_vendor: bool,
565     pub cfg_target_thread_local: bool,
566     pub augmented_assignments: bool,
567     pub braced_empty_structs: bool,
568     pub staged_api: bool,
569     pub stmt_expr_attributes: bool,
570     pub deprecated: bool,
571 }
572
573 impl Features {
574     pub fn new() -> Features {
575         Features {
576             unboxed_closures: false,
577             rustc_diagnostic_macros: false,
578             allow_quote: false,
579             allow_asm: false,
580             allow_log_syntax: false,
581             allow_concat_idents: false,
582             allow_trace_macros: false,
583             allow_internal_unstable: false,
584             allow_custom_derive: false,
585             allow_placement_in: false,
586             allow_box: false,
587             allow_pushpop_unsafe: false,
588             simd_ffi: false,
589             unmarked_api: false,
590             declared_stable_lang_features: Vec::new(),
591             declared_lib_features: Vec::new(),
592             const_fn: false,
593             const_indexing: false,
594             static_recursion: false,
595             default_type_parameter_fallback: false,
596             type_macros: false,
597             cfg_target_feature: false,
598             cfg_target_vendor: false,
599             cfg_target_thread_local: false,
600             augmented_assignments: false,
601             braced_empty_structs: false,
602             staged_api: false,
603             stmt_expr_attributes: false,
604             deprecated: false,
605         }
606     }
607 }
608
609 const EXPLAIN_BOX_SYNTAX: &'static str =
610     "box expression syntax is experimental; you can call `Box::new` instead.";
611
612 const EXPLAIN_PLACEMENT_IN: &'static str =
613     "placement-in expression syntax is experimental and subject to change.";
614
615 const EXPLAIN_PUSHPOP_UNSAFE: &'static str =
616     "push/pop_unsafe macros are experimental and subject to change.";
617
618 const EXPLAIN_STMT_ATTR_SYNTAX: &'static str =
619     "attributes on non-item statements and expressions are experimental.";
620
621 pub fn check_for_box_syntax(f: Option<&Features>, diag: &Handler, span: Span) {
622     if let Some(&Features { allow_box: true, .. }) = f {
623         return;
624     }
625     emit_feature_err(diag, "box_syntax", span, GateIssue::Language, EXPLAIN_BOX_SYNTAX);
626 }
627
628 pub fn check_for_placement_in(f: Option<&Features>, diag: &Handler, span: Span) {
629     if let Some(&Features { allow_placement_in: true, .. }) = f {
630         return;
631     }
632     emit_feature_err(diag, "placement_in_syntax", span, GateIssue::Language, EXPLAIN_PLACEMENT_IN);
633 }
634
635 pub fn check_for_pushpop_syntax(f: Option<&Features>, diag: &Handler, span: Span) {
636     if let Some(&Features { allow_pushpop_unsafe: true, .. }) = f {
637         return;
638     }
639     emit_feature_err(diag, "pushpop_unsafe", span, GateIssue::Language, EXPLAIN_PUSHPOP_UNSAFE);
640 }
641
642 struct Context<'a> {
643     features: Vec<&'static str>,
644     span_handler: &'a Handler,
645     cm: &'a CodeMap,
646     plugin_attributes: &'a [(String, AttributeType)],
647 }
648
649 impl<'a> Context<'a> {
650     fn enable_feature(&mut self, feature: &'static str) {
651         debug!("enabling feature: {}", feature);
652         self.features.push(feature);
653     }
654
655     fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
656         let has_feature = self.has_feature(feature);
657         debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", feature, span, has_feature);
658         if !has_feature {
659             emit_feature_err(self.span_handler, feature, span, GateIssue::Language, explain);
660         }
661     }
662     fn has_feature(&self, feature: &str) -> bool {
663         self.features.iter().any(|&n| n == feature)
664     }
665
666     fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) {
667         debug!("check_attribute(attr = {:?})", attr);
668         let name = &*attr.name();
669         for &(n, ty, gateage) in KNOWN_ATTRIBUTES {
670             if n == name {
671                 if let Gated(gate, desc) = gateage {
672                     self.gate_feature(gate, attr.span, desc);
673                 }
674                 debug!("check_attribute: {:?} is known, {:?}, {:?}", name, ty, gateage);
675                 return;
676             }
677         }
678         for &(ref n, ref ty) in self.plugin_attributes {
679             if &*n == name {
680                 // Plugins can't gate attributes, so we don't check for it
681                 // unlike the code above; we only use this loop to
682                 // short-circuit to avoid the checks below
683                 debug!("check_attribute: {:?} is registered by a plugin, {:?}", name, ty);
684                 return;
685             }
686         }
687         if name.starts_with("rustc_") {
688             self.gate_feature("rustc_attrs", attr.span,
689                               "unless otherwise specified, attributes \
690                                with the prefix `rustc_` \
691                                are reserved for internal compiler diagnostics");
692         } else if name.starts_with("derive_") {
693             self.gate_feature("custom_derive", attr.span,
694                               "attributes of the form `#[derive_*]` are reserved \
695                                for the compiler");
696         } else {
697             // Only run the custom attribute lint during regular
698             // feature gate checking. Macro gating runs
699             // before the plugin attributes are registered
700             // so we skip this then
701             if !is_macro {
702                 self.gate_feature("custom_attribute", attr.span,
703                            &format!("The attribute `{}` is currently \
704                                     unknown to the compiler and \
705                                     may have meaning \
706                                     added to it in the future",
707                                     name));
708             }
709         }
710     }
711 }
712
713 fn find_lang_feature_issue(feature: &str) -> Option<u32> {
714     let info = KNOWN_FEATURES.iter()
715                               .find(|t| t.0 == feature)
716                               .unwrap();
717     let issue = info.2;
718     if let Active = info.3 {
719         // FIXME (#28244): enforce that active features have issue numbers
720         // assert!(issue.is_some())
721     }
722     issue
723 }
724
725 pub enum GateIssue {
726     Language,
727     Library(Option<u32>)
728 }
729
730 pub fn emit_feature_err(diag: &Handler, feature: &str, span: Span, issue: GateIssue,
731                         explain: &str) {
732     let issue = match issue {
733         GateIssue::Language => find_lang_feature_issue(feature),
734         GateIssue::Library(lib) => lib,
735     };
736
737     let mut err = if let Some(n) = issue {
738         diag.struct_span_err(span, &format!("{} (see issue #{})", explain, n))
739     } else {
740         diag.struct_span_err(span, explain)
741     };
742
743     // #23973: do not suggest `#![feature(...)]` if we are in beta/stable
744     if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some() {
745         err.emit();
746         return;
747     }
748     err.fileline_help(span, &format!("add #![feature({})] to the \
749                                       crate attributes to enable",
750                                      feature));
751     err.emit();
752 }
753
754 pub const EXPLAIN_ASM: &'static str =
755     "inline assembly is not stable enough for use and is subject to change";
756
757 pub const EXPLAIN_LOG_SYNTAX: &'static str =
758     "`log_syntax!` is not stable enough for use and is subject to change";
759
760 pub const EXPLAIN_CONCAT_IDENTS: &'static str =
761     "`concat_idents` is not stable enough for use and is subject to change";
762
763 pub const EXPLAIN_TRACE_MACROS: &'static str =
764     "`trace_macros` is not stable enough for use and is subject to change";
765 pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &'static str =
766     "allow_internal_unstable side-steps feature gating and stability checks";
767
768 pub const EXPLAIN_CUSTOM_DERIVE: &'static str =
769     "`#[derive]` for custom traits is not stable enough for use and is subject to change";
770
771 struct MacroVisitor<'a> {
772     context: &'a Context<'a>
773 }
774
775 impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
776     fn visit_mac(&mut self, mac: &ast::Mac) {
777         let path = &mac.node.path;
778         let name = path.segments.last().unwrap().identifier.name.as_str();
779
780         // Issue 22234: If you add a new case here, make sure to also
781         // add code to catch the macro during or after expansion.
782         //
783         // We still keep this MacroVisitor (rather than *solely*
784         // relying on catching cases during or after expansion) to
785         // catch uses of these macros within conditionally-compiled
786         // code, e.g. `#[cfg]`-guarded functions.
787
788         if name == "asm" {
789             self.context.gate_feature("asm", path.span, EXPLAIN_ASM);
790         }
791
792         else if name == "log_syntax" {
793             self.context.gate_feature("log_syntax", path.span, EXPLAIN_LOG_SYNTAX);
794         }
795
796         else if name == "trace_macros" {
797             self.context.gate_feature("trace_macros", path.span, EXPLAIN_TRACE_MACROS);
798         }
799
800         else if name == "concat_idents" {
801             self.context.gate_feature("concat_idents", path.span, EXPLAIN_CONCAT_IDENTS);
802         }
803     }
804
805     fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
806         self.context.check_attribute(attr, true);
807     }
808
809     fn visit_expr(&mut self, e: &ast::Expr) {
810         // Issue 22181: overloaded-`box` and placement-`in` are
811         // implemented via a desugaring expansion, so their feature
812         // gates go into MacroVisitor since that works pre-expansion.
813         //
814         // Issue 22234: we also check during expansion as well.
815         // But we keep these checks as a pre-expansion check to catch
816         // uses in e.g. conditionalized code.
817
818         if let ast::ExprBox(_) = e.node {
819             self.context.gate_feature("box_syntax", e.span, EXPLAIN_BOX_SYNTAX);
820         }
821
822         if let ast::ExprInPlace(..) = e.node {
823             self.context.gate_feature("placement_in_syntax", e.span, EXPLAIN_PLACEMENT_IN);
824         }
825
826         visit::walk_expr(self, e);
827     }
828 }
829
830 struct PostExpansionVisitor<'a> {
831     context: &'a Context<'a>,
832 }
833
834 impl<'a> PostExpansionVisitor<'a> {
835     fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
836         if !self.context.cm.span_allows_unstable(span) {
837             self.context.gate_feature(feature, span, explain)
838         }
839     }
840 }
841
842 impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
843     fn visit_attribute(&mut self, attr: &ast::Attribute) {
844         if !self.context.cm.span_allows_unstable(attr.span) {
845             self.context.check_attribute(attr, false);
846         }
847     }
848
849     fn visit_name(&mut self, sp: Span, name: ast::Name) {
850         if !name.as_str().is_ascii() {
851             self.gate_feature("non_ascii_idents", sp,
852                               "non-ascii idents are not fully supported.");
853         }
854     }
855
856     fn visit_item(&mut self, i: &ast::Item) {
857         match i.node {
858             ast::ItemExternCrate(_) => {
859                 if attr::contains_name(&i.attrs[..], "macro_reexport") {
860                     self.gate_feature("macro_reexport", i.span,
861                                       "macros reexports are experimental \
862                                        and possibly buggy");
863                 }
864             }
865
866             ast::ItemForeignMod(ref foreign_module) => {
867                 if attr::contains_name(&i.attrs[..], "link_args") {
868                     self.gate_feature("link_args", i.span,
869                                       "the `link_args` attribute is not portable \
870                                        across platforms, it is recommended to \
871                                        use `#[link(name = \"foo\")]` instead")
872                 }
873                 let maybe_feature = match foreign_module.abi {
874                     Abi::RustIntrinsic => Some(("intrinsics", "intrinsics are subject to change")),
875                     Abi::PlatformIntrinsic => {
876                         Some(("platform_intrinsics",
877                               "platform intrinsics are experimental and possibly buggy"))
878                     },
879                     Abi::Vectorcall => {
880                         Some(("abi_vectorcall",
881                             "vectorcall is experimental and subject to change"
882                         ))
883                     }
884                     _ => None
885                 };
886                 if let Some((feature, msg)) = maybe_feature {
887                     self.gate_feature(feature, i.span, msg)
888                 }
889             }
890
891             ast::ItemFn(..) => {
892                 if attr::contains_name(&i.attrs[..], "plugin_registrar") {
893                     self.gate_feature("plugin_registrar", i.span,
894                                       "compiler plugins are experimental and possibly buggy");
895                 }
896                 if attr::contains_name(&i.attrs[..], "start") {
897                     self.gate_feature("start", i.span,
898                                       "a #[start] function is an experimental \
899                                        feature whose signature may change \
900                                        over time");
901                 }
902                 if attr::contains_name(&i.attrs[..], "main") {
903                     self.gate_feature("main", i.span,
904                                       "declaration of a nonstandard #[main] \
905                                        function may change over time, for now \
906                                        a top-level `fn main()` is required");
907                 }
908             }
909
910             ast::ItemStruct(..) => {
911                 if attr::contains_name(&i.attrs[..], "simd") {
912                     self.gate_feature("simd", i.span,
913                                       "SIMD types are experimental and possibly buggy");
914                     self.context.span_handler.span_warn(i.span,
915                                                         "the `#[simd]` attribute is deprecated, \
916                                                          use `#[repr(simd)]` instead");
917                 }
918                 for attr in &i.attrs {
919                     if attr.name() == "repr" {
920                         for item in attr.meta_item_list().unwrap_or(&[]) {
921                             if item.name() == "simd" {
922                                 self.gate_feature("repr_simd", i.span,
923                                                   "SIMD types are experimental and possibly buggy");
924
925                             }
926                         }
927                     }
928                 }
929             }
930
931             ast::ItemDefaultImpl(..) => {
932                 self.gate_feature("optin_builtin_traits",
933                                   i.span,
934                                   "default trait implementations are experimental \
935                                    and possibly buggy");
936             }
937
938             ast::ItemImpl(_, polarity, _, _, _, _) => {
939                 match polarity {
940                     ast::ImplPolarity::Negative => {
941                         self.gate_feature("optin_builtin_traits",
942                                           i.span,
943                                           "negative trait bounds are not yet fully implemented; \
944                                           use marker types for now");
945                     },
946                     _ => {}
947                 }
948             }
949
950             _ => {}
951         }
952
953         visit::walk_item(self, i);
954     }
955
956     fn visit_variant_data(&mut self, s: &'v ast::VariantData, _: ast::Ident,
957                         _: &'v ast::Generics, _: ast::NodeId, span: Span) {
958         if s.fields().is_empty() {
959             if s.is_struct() {
960                 self.gate_feature("braced_empty_structs", span,
961                                   "empty structs and enum variants with braces are unstable");
962             } else if s.is_tuple() {
963                 self.context.span_handler.struct_span_err(span, "empty tuple structs and enum \
964                                                                  variants are not allowed, use \
965                                                                  unit structs and enum variants \
966                                                                  instead")
967                                          .span_help(span, "remove trailing `()` to make a unit \
968                                                            struct or unit enum variant")
969                                          .emit();
970             }
971         }
972         visit::walk_struct_def(self, s)
973     }
974
975     fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
976         let links_to_llvm = match attr::first_attr_value_str_by_name(&i.attrs,
977                                                                      "link_name") {
978             Some(val) => val.starts_with("llvm."),
979             _ => false
980         };
981         if links_to_llvm {
982             self.gate_feature("link_llvm_intrinsics", i.span,
983                               "linking to LLVM intrinsics is experimental");
984         }
985
986         visit::walk_foreign_item(self, i)
987     }
988
989     fn visit_expr(&mut self, e: &ast::Expr) {
990         match e.node {
991             ast::ExprBox(_) => {
992                 self.gate_feature("box_syntax",
993                                   e.span,
994                                   "box expression syntax is experimental; \
995                                    you can call `Box::new` instead.");
996             }
997             ast::ExprType(..) => {
998                 self.gate_feature("type_ascription", e.span,
999                                   "type ascription is experimental");
1000             }
1001             _ => {}
1002         }
1003         visit::walk_expr(self, e);
1004     }
1005
1006     fn visit_pat(&mut self, pattern: &ast::Pat) {
1007         match pattern.node {
1008             ast::PatVec(_, Some(_), ref last) if !last.is_empty() => {
1009                 self.gate_feature("advanced_slice_patterns",
1010                                   pattern.span,
1011                                   "multiple-element slice matches anywhere \
1012                                    but at the end of a slice (e.g. \
1013                                    `[0, ..xs, 0]`) are experimental")
1014             }
1015             ast::PatVec(..) => {
1016                 self.gate_feature("slice_patterns",
1017                                   pattern.span,
1018                                   "slice pattern syntax is experimental");
1019             }
1020             ast::PatBox(..) => {
1021                 self.gate_feature("box_patterns",
1022                                   pattern.span,
1023                                   "box pattern syntax is experimental");
1024             }
1025             _ => {}
1026         }
1027         visit::walk_pat(self, pattern)
1028     }
1029
1030     fn visit_fn(&mut self,
1031                 fn_kind: FnKind<'v>,
1032                 fn_decl: &'v ast::FnDecl,
1033                 block: &'v ast::Block,
1034                 span: Span,
1035                 _node_id: NodeId) {
1036         // check for const fn declarations
1037         match fn_kind {
1038             FnKind::ItemFn(_, _, _, ast::Constness::Const, _, _) => {
1039                 self.gate_feature("const_fn", span, "const fn is unstable");
1040             }
1041             _ => {
1042                 // stability of const fn methods are covered in
1043                 // visit_trait_item and visit_impl_item below; this is
1044                 // because default methods don't pass through this
1045                 // point.
1046             }
1047         }
1048
1049         match fn_kind {
1050             FnKind::ItemFn(_, _, _, _, abi, _) if abi == Abi::RustIntrinsic => {
1051                 self.gate_feature("intrinsics",
1052                                   span,
1053                                   "intrinsics are subject to change")
1054             }
1055             FnKind::ItemFn(_, _, _, _, abi, _) |
1056             FnKind::Method(_, &ast::MethodSig { abi, .. }, _) => match abi {
1057                 Abi::RustCall => {
1058                     self.gate_feature("unboxed_closures", span,
1059                         "rust-call ABI is subject to change");
1060                 },
1061                 Abi::Vectorcall => {
1062                     self.gate_feature("abi_vectorcall", span,
1063                         "vectorcall is experimental and subject to change");
1064                 },
1065                 _ => {}
1066             },
1067             _ => {}
1068         }
1069         visit::walk_fn(self, fn_kind, fn_decl, block, span);
1070     }
1071
1072     fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) {
1073         match ti.node {
1074             ast::ConstTraitItem(..) => {
1075                 self.gate_feature("associated_consts",
1076                                   ti.span,
1077                                   "associated constants are experimental")
1078             }
1079             ast::MethodTraitItem(ref sig, _) => {
1080                 if sig.constness == ast::Constness::Const {
1081                     self.gate_feature("const_fn", ti.span, "const fn is unstable");
1082                 }
1083             }
1084             ast::TypeTraitItem(_, Some(_)) => {
1085                 self.gate_feature("associated_type_defaults", ti.span,
1086                                   "associated type defaults are unstable");
1087             }
1088             _ => {}
1089         }
1090         visit::walk_trait_item(self, ti);
1091     }
1092
1093     fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
1094         match ii.node {
1095             ast::ImplItemKind::Const(..) => {
1096                 self.gate_feature("associated_consts",
1097                                   ii.span,
1098                                   "associated constants are experimental")
1099             }
1100             ast::ImplItemKind::Method(ref sig, _) => {
1101                 if sig.constness == ast::Constness::Const {
1102                     self.gate_feature("const_fn", ii.span, "const fn is unstable");
1103                 }
1104             }
1105             _ => {}
1106         }
1107         visit::walk_impl_item(self, ii);
1108     }
1109 }
1110
1111 fn check_crate_inner<F>(cm: &CodeMap, span_handler: &Handler,
1112                         krate: &ast::Crate,
1113                         plugin_attributes: &[(String, AttributeType)],
1114                         check: F)
1115                        -> Features
1116     where F: FnOnce(&mut Context, &ast::Crate)
1117 {
1118     let mut cx = Context {
1119         features: Vec::new(),
1120         span_handler: span_handler,
1121         cm: cm,
1122         plugin_attributes: plugin_attributes,
1123     };
1124
1125     let mut accepted_features = Vec::new();
1126     let mut unknown_features = Vec::new();
1127
1128     for attr in &krate.attrs {
1129         if !attr.check_name("feature") {
1130             continue
1131         }
1132
1133         match attr.meta_item_list() {
1134             None => {
1135                 span_handler.span_err(attr.span, "malformed feature attribute, \
1136                                                   expected #![feature(...)]");
1137             }
1138             Some(list) => {
1139                 for mi in list {
1140                     let name = match mi.node {
1141                         ast::MetaWord(ref word) => (*word).clone(),
1142                         _ => {
1143                             span_handler.span_err(mi.span,
1144                                                   "malformed feature, expected just \
1145                                                    one word");
1146                             continue
1147                         }
1148                     };
1149                     match KNOWN_FEATURES.iter()
1150                                         .find(|& &(n, _, _, _)| name == n) {
1151                         Some(&(name, _, _, Active)) => {
1152                             cx.enable_feature(name);
1153                         }
1154                         Some(&(_, _, _, Removed)) => {
1155                             span_handler.span_err(mi.span, "feature has been removed");
1156                         }
1157                         Some(&(_, _, _, Accepted)) => {
1158                             accepted_features.push(mi.span);
1159                         }
1160                         None => {
1161                             unknown_features.push((name, mi.span));
1162                         }
1163                     }
1164                 }
1165             }
1166         }
1167     }
1168
1169     check(&mut cx, krate);
1170
1171     // FIXME (pnkfelix): Before adding the 99th entry below, change it
1172     // to a single-pass (instead of N calls to `.has_feature`).
1173
1174     Features {
1175         unboxed_closures: cx.has_feature("unboxed_closures"),
1176         rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
1177         allow_quote: cx.has_feature("quote"),
1178         allow_asm: cx.has_feature("asm"),
1179         allow_log_syntax: cx.has_feature("log_syntax"),
1180         allow_concat_idents: cx.has_feature("concat_idents"),
1181         allow_trace_macros: cx.has_feature("trace_macros"),
1182         allow_internal_unstable: cx.has_feature("allow_internal_unstable"),
1183         allow_custom_derive: cx.has_feature("custom_derive"),
1184         allow_placement_in: cx.has_feature("placement_in_syntax"),
1185         allow_box: cx.has_feature("box_syntax"),
1186         allow_pushpop_unsafe: cx.has_feature("pushpop_unsafe"),
1187         simd_ffi: cx.has_feature("simd_ffi"),
1188         unmarked_api: cx.has_feature("unmarked_api"),
1189         declared_stable_lang_features: accepted_features,
1190         declared_lib_features: unknown_features,
1191         const_fn: cx.has_feature("const_fn"),
1192         const_indexing: cx.has_feature("const_indexing"),
1193         static_recursion: cx.has_feature("static_recursion"),
1194         default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"),
1195         type_macros: cx.has_feature("type_macros"),
1196         cfg_target_feature: cx.has_feature("cfg_target_feature"),
1197         cfg_target_vendor: cx.has_feature("cfg_target_vendor"),
1198         cfg_target_thread_local: cx.has_feature("cfg_target_thread_local"),
1199         augmented_assignments: cx.has_feature("augmented_assignments"),
1200         braced_empty_structs: cx.has_feature("braced_empty_structs"),
1201         staged_api: cx.has_feature("staged_api"),
1202         stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"),
1203         deprecated: cx.has_feature("deprecated"),
1204     }
1205 }
1206
1207 pub fn check_crate_macros(cm: &CodeMap, span_handler: &Handler, krate: &ast::Crate)
1208 -> Features {
1209     check_crate_inner(cm, span_handler, krate, &[] as &'static [_],
1210                       |ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
1211 }
1212
1213 pub fn check_crate(cm: &CodeMap, span_handler: &Handler, krate: &ast::Crate,
1214                    plugin_attributes: &[(String, AttributeType)],
1215                    unstable: UnstableFeatures) -> Features
1216 {
1217     maybe_stage_features(span_handler, krate, unstable);
1218
1219     check_crate_inner(cm, span_handler, krate, plugin_attributes,
1220                       |ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx },
1221                                                      krate))
1222 }
1223
1224 #[derive(Clone, Copy)]
1225 pub enum UnstableFeatures {
1226     /// Hard errors for unstable features are active, as on
1227     /// beta/stable channels.
1228     Disallow,
1229     /// Allow features to me activated, as on nightly.
1230     Allow,
1231     /// Errors are bypassed for bootstrapping. This is required any time
1232     /// during the build that feature-related lints are set to warn or above
1233     /// because the build turns on warnings-as-errors and uses lots of unstable
1234     /// features. As a result, this is always required for building Rust itself.
1235     Cheat
1236 }
1237
1238 fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate,
1239                         unstable: UnstableFeatures) {
1240     let allow_features = match unstable {
1241         UnstableFeatures::Allow => true,
1242         UnstableFeatures::Disallow => false,
1243         UnstableFeatures::Cheat => true
1244     };
1245     if !allow_features {
1246         for attr in &krate.attrs {
1247             if attr.check_name("feature") {
1248                 let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)");
1249                 let ref msg = format!("#[feature] may not be used on the {} release channel",
1250                                       release_channel);
1251                 span_handler.span_err(attr.span, msg);
1252             }
1253         }
1254     }
1255 }