]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/feature_gate.rs
Auto merge of #30723 - nrc:macro-err-bug, r=Manishearth
[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), Active),
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 // (changing above list without updating src/doc/reference.md makes @cmr sad)
244
245 enum Status {
246     /// Represents an active feature that is currently being implemented or
247     /// currently being considered for addition/removal.
248     Active,
249
250     /// Represents a feature which has since been removed (it was once Active)
251     Removed,
252
253     /// This language feature has since been Accepted (it was once Active)
254     Accepted,
255 }
256
257 // Attributes that have a special meaning to rustc or rustdoc
258 pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGate)] = &[
259     // Normal attributes
260
261     ("warn", Normal, Ungated),
262     ("allow", Normal, Ungated),
263     ("forbid", Normal, Ungated),
264     ("deny", Normal, Ungated),
265
266     ("macro_reexport", Normal, Ungated),
267     ("macro_use", Normal, Ungated),
268     ("macro_export", Normal, Ungated),
269     ("plugin_registrar", Normal, Ungated),
270
271     ("cfg", Normal, Ungated),
272     ("cfg_attr", Normal, Ungated),
273     ("main", Normal, Ungated),
274     ("start", Normal, Ungated),
275     ("test", Normal, Ungated),
276     ("bench", Normal, Ungated),
277     ("simd", Normal, Ungated),
278     ("repr", Normal, Ungated),
279     ("path", Normal, Ungated),
280     ("abi", Normal, Ungated),
281     ("automatically_derived", Normal, Ungated),
282     ("no_mangle", Normal, Ungated),
283     ("no_link", Normal, Ungated),
284     ("derive", Normal, Ungated),
285     ("should_panic", Normal, Ungated),
286     ("ignore", Normal, Ungated),
287     ("no_implicit_prelude", Normal, Ungated),
288     ("reexport_test_harness_main", Normal, Ungated),
289     ("link_args", Normal, Ungated),
290     ("macro_escape", Normal, Ungated),
291
292     // Not used any more, but we can't feature gate it
293     ("no_stack_check", Normal, Ungated),
294
295     ("plugin", CrateLevel, Gated("plugin",
296                                  "compiler plugins are experimental \
297                                   and possibly buggy")),
298     ("no_std", CrateLevel, Ungated),
299     ("no_core", CrateLevel, Gated("no_core",
300                                   "no_core is experimental")),
301     ("lang", Normal, Gated("lang_items",
302                            "language items are subject to change")),
303     ("linkage", Whitelisted, Gated("linkage",
304                                    "the `linkage` attribute is experimental \
305                                     and not portable across platforms")),
306     ("thread_local", Whitelisted, Gated("thread_local",
307                                         "`#[thread_local]` is an experimental feature, and does \
308                                          not currently handle destructors. There is no \
309                                          corresponding `#[task_local]` mapping to the task \
310                                          model")),
311
312     ("rustc_on_unimplemented", Normal, Gated("on_unimplemented",
313                                              "the `#[rustc_on_unimplemented]` attribute \
314                                               is an experimental feature")),
315     ("allocator", Whitelisted, Gated("allocator",
316                                      "the `#[allocator]` attribute is an experimental feature")),
317     ("needs_allocator", Normal, Gated("needs_allocator",
318                                       "the `#[needs_allocator]` \
319                                        attribute is an experimental \
320                                        feature")),
321     ("rustc_variance", Normal, Gated("rustc_attrs",
322                                      "the `#[rustc_variance]` attribute \
323                                       is just used for rustc unit tests \
324                                       and will never be stable")),
325     ("rustc_error", Whitelisted, Gated("rustc_attrs",
326                                        "the `#[rustc_error]` attribute \
327                                         is just used for rustc unit tests \
328                                         and will never be stable")),
329     ("rustc_if_this_changed", Whitelisted, Gated("rustc_attrs",
330                                        "the `#[rustc_if_this_changed]` attribute \
331                                         is just used for rustc unit tests \
332                                         and will never be stable")),
333     ("rustc_then_this_would_need", Whitelisted, Gated("rustc_attrs",
334                                        "the `#[rustc_if_this_changed]` attribute \
335                                         is just used for rustc unit tests \
336                                         and will never be stable")),
337     ("rustc_move_fragments", Normal, Gated("rustc_attrs",
338                                            "the `#[rustc_move_fragments]` attribute \
339                                             is just used for rustc unit tests \
340                                             and will never be stable")),
341     ("rustc_mir", Normal, Gated("rustc_attrs",
342                                 "the `#[rustc_mir]` attribute \
343                                  is just used for rustc unit tests \
344                                  and will never be stable")),
345
346     ("allow_internal_unstable", Normal, Gated("allow_internal_unstable",
347                                               EXPLAIN_ALLOW_INTERNAL_UNSTABLE)),
348
349     ("fundamental", Whitelisted, Gated("fundamental",
350                                        "the `#[fundamental]` attribute \
351                                         is an experimental feature")),
352
353     ("linked_from", Normal, Gated("linked_from",
354                                   "the `#[linked_from]` attribute \
355                                    is an experimental feature")),
356
357     // FIXME: #14408 whitelist docs since rustdoc looks at them
358     ("doc", Whitelisted, Ungated),
359
360     // FIXME: #14406 these are processed in trans, which happens after the
361     // lint pass
362     ("cold", Whitelisted, Ungated),
363     ("export_name", Whitelisted, Ungated),
364     ("inline", Whitelisted, Ungated),
365     ("link", Whitelisted, Ungated),
366     ("link_name", Whitelisted, Ungated),
367     ("link_section", Whitelisted, Ungated),
368     ("no_builtins", Whitelisted, Ungated),
369     ("no_mangle", Whitelisted, Ungated),
370     ("no_debug", Whitelisted, Gated("no_debug",
371                                     "the `#[no_debug]` attribute \
372                                      is an experimental feature")),
373     ("omit_gdb_pretty_printer_section", Whitelisted, Gated("omit_gdb_pretty_printer_section",
374                                                        "the `#[omit_gdb_pretty_printer_section]` \
375                                                         attribute is just used for the Rust test \
376                                                         suite")),
377     ("unsafe_no_drop_flag", Whitelisted, Gated("unsafe_no_drop_flag",
378                                                "unsafe_no_drop_flag has unstable semantics \
379                                                 and may be removed in the future")),
380     ("unsafe_destructor_blind_to_params",
381      Normal,
382      Gated("dropck_parametricity",
383            "unsafe_destructor_blind_to_params has unstable semantics \
384             and may be removed in the future")),
385     ("unwind", Whitelisted, Gated("unwind_attributes", "#[unwind] is experimental")),
386
387     // used in resolve
388     ("prelude_import", Whitelisted, Gated("prelude_import",
389                                           "`#[prelude_import]` is for use by rustc only")),
390
391     // FIXME: #14407 these are only looked at on-demand so we can't
392     // guarantee they'll have already been checked
393     ("rustc_deprecated", Whitelisted, Ungated),
394     ("must_use", Whitelisted, Ungated),
395     ("stable", Whitelisted, Ungated),
396     ("unstable", Whitelisted, Ungated),
397     ("deprecated", Normal, Gated("deprecated", "`#[deprecated]` attribute is unstable")),
398
399     ("rustc_paren_sugar", Normal, Gated("unboxed_closures",
400                                         "unboxed_closures are still evolving")),
401     ("rustc_reflect_like", Whitelisted, Gated("reflect",
402                                               "defining reflective traits is still evolving")),
403
404     // Crate level attributes
405     ("crate_name", CrateLevel, Ungated),
406     ("crate_type", CrateLevel, Ungated),
407     ("crate_id", CrateLevel, Ungated),
408     ("feature", CrateLevel, Ungated),
409     ("no_start", CrateLevel, Ungated),
410     ("no_main", CrateLevel, Ungated),
411     ("no_builtins", CrateLevel, Ungated),
412     ("recursion_limit", CrateLevel, Ungated),
413 ];
414
415 macro_rules! cfg_fn {
416     (|$x: ident| $e: expr) => {{
417         fn f($x: &Features) -> bool {
418             $e
419         }
420         f as fn(&Features) -> bool
421     }}
422 }
423 // cfg(...)'s that are feature gated
424 const GATED_CFGS: &'static [(&'static str, &'static str, fn(&Features) -> bool)] = &[
425     // (name in cfg, feature, function to check if the feature is enabled)
426     ("target_feature", "cfg_target_feature", cfg_fn!(|x| x.cfg_target_feature)),
427     ("target_vendor", "cfg_target_vendor", cfg_fn!(|x| x.cfg_target_vendor)),
428     ("target_thread_local", "cfg_target_thread_local",
429      cfg_fn!(|x| x.cfg_target_thread_local)),
430 ];
431
432 #[derive(Debug, Eq, PartialEq)]
433 pub enum GatedCfgAttr {
434     GatedCfg(GatedCfg),
435     GatedAttr(Span),
436 }
437
438 #[derive(Debug, Eq, PartialEq)]
439 pub struct GatedCfg {
440     span: Span,
441     index: usize,
442 }
443
444 impl Ord for GatedCfgAttr {
445     fn cmp(&self, other: &GatedCfgAttr) -> cmp::Ordering {
446         let to_tup = |s: &GatedCfgAttr| match *s {
447             GatedCfgAttr::GatedCfg(ref gated_cfg) => {
448                 (gated_cfg.span.lo.0, gated_cfg.span.hi.0, gated_cfg.index)
449             }
450             GatedCfgAttr::GatedAttr(ref span) => {
451                 (span.lo.0, span.hi.0, GATED_CFGS.len())
452             }
453         };
454         to_tup(self).cmp(&to_tup(other))
455     }
456 }
457
458 impl PartialOrd for GatedCfgAttr {
459     fn partial_cmp(&self, other: &GatedCfgAttr) -> Option<cmp::Ordering> {
460         Some(self.cmp(other))
461     }
462 }
463
464 impl GatedCfgAttr {
465     pub fn check_and_emit(&self,
466                           diagnostic: &Handler,
467                           features: &Features,
468                           codemap: &CodeMap) {
469         match *self {
470             GatedCfgAttr::GatedCfg(ref cfg) => {
471                 cfg.check_and_emit(diagnostic, features, codemap);
472             }
473             GatedCfgAttr::GatedAttr(span) => {
474                 if !features.stmt_expr_attributes {
475                     emit_feature_err(diagnostic,
476                                      "stmt_expr_attributes",
477                                      span,
478                                      GateIssue::Language,
479                                      EXPLAIN_STMT_ATTR_SYNTAX);
480                 }
481             }
482         }
483     }
484 }
485
486 impl GatedCfg {
487     pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> {
488         let name = cfg.name();
489         GATED_CFGS.iter()
490                   .position(|info| info.0 == name)
491                   .map(|idx| {
492                       GatedCfg {
493                           span: cfg.span,
494                           index: idx
495                       }
496                   })
497     }
498     fn check_and_emit(&self,
499                       diagnostic: &Handler,
500                       features: &Features,
501                       codemap: &CodeMap) {
502         let (cfg, feature, has_feature) = GATED_CFGS[self.index];
503         if !has_feature(features) && !codemap.span_allows_unstable(self.span) {
504             let explain = format!("`cfg({})` is experimental and subject to change", cfg);
505             emit_feature_err(diagnostic, feature, self.span, GateIssue::Language, &explain);
506         }
507     }
508 }
509
510
511 #[derive(PartialEq, Copy, Clone, Debug)]
512 pub enum AttributeType {
513     /// Normal, builtin attribute that is consumed
514     /// by the compiler before the unused_attribute check
515     Normal,
516
517     /// Builtin attribute that may not be consumed by the compiler
518     /// before the unused_attribute check. These attributes
519     /// will be ignored by the unused_attribute lint
520     Whitelisted,
521
522     /// Builtin attribute that is only allowed at the crate level
523     CrateLevel,
524 }
525
526 #[derive(PartialEq, Copy, Clone, Debug)]
527 pub enum AttributeGate {
528     /// Is gated by a given feature gate and reason
529     Gated(&'static str, &'static str),
530
531     /// Ungated attribute, can be used on all release channels
532     Ungated,
533 }
534
535 /// A set of features to be used by later passes.
536 pub struct Features {
537     pub unboxed_closures: bool,
538     pub rustc_diagnostic_macros: bool,
539     pub allow_quote: bool,
540     pub allow_asm: bool,
541     pub allow_log_syntax: bool,
542     pub allow_concat_idents: bool,
543     pub allow_trace_macros: bool,
544     pub allow_internal_unstable: bool,
545     pub allow_custom_derive: bool,
546     pub allow_placement_in: bool,
547     pub allow_box: bool,
548     pub allow_pushpop_unsafe: bool,
549     pub simd_ffi: bool,
550     pub unmarked_api: bool,
551     pub negate_unsigned: bool,
552     /// spans of #![feature] attrs for stable language features. for error reporting
553     pub declared_stable_lang_features: Vec<Span>,
554     /// #![feature] attrs for non-language (library) features
555     pub declared_lib_features: Vec<(InternedString, Span)>,
556     pub const_fn: bool,
557     pub const_indexing: bool,
558     pub static_recursion: bool,
559     pub default_type_parameter_fallback: bool,
560     pub type_macros: bool,
561     pub cfg_target_feature: bool,
562     pub cfg_target_vendor: bool,
563     pub cfg_target_thread_local: bool,
564     pub augmented_assignments: bool,
565     pub braced_empty_structs: bool,
566     pub staged_api: bool,
567     pub stmt_expr_attributes: bool,
568     pub deprecated: bool,
569 }
570
571 impl Features {
572     pub fn new() -> Features {
573         Features {
574             unboxed_closures: false,
575             rustc_diagnostic_macros: false,
576             allow_quote: false,
577             allow_asm: false,
578             allow_log_syntax: false,
579             allow_concat_idents: false,
580             allow_trace_macros: false,
581             allow_internal_unstable: false,
582             allow_custom_derive: false,
583             allow_placement_in: false,
584             allow_box: false,
585             allow_pushpop_unsafe: false,
586             simd_ffi: false,
587             unmarked_api: false,
588             negate_unsigned: false,
589             declared_stable_lang_features: Vec::new(),
590             declared_lib_features: Vec::new(),
591             const_fn: false,
592             const_indexing: false,
593             static_recursion: false,
594             default_type_parameter_fallback: false,
595             type_macros: false,
596             cfg_target_feature: false,
597             cfg_target_vendor: false,
598             cfg_target_thread_local: false,
599             augmented_assignments: false,
600             braced_empty_structs: false,
601             staged_api: false,
602             stmt_expr_attributes: false,
603             deprecated: false,
604         }
605     }
606 }
607
608 const EXPLAIN_BOX_SYNTAX: &'static str =
609     "box expression syntax is experimental; you can call `Box::new` instead.";
610
611 const EXPLAIN_PLACEMENT_IN: &'static str =
612     "placement-in expression syntax is experimental and subject to change.";
613
614 const EXPLAIN_PUSHPOP_UNSAFE: &'static str =
615     "push/pop_unsafe macros are experimental and subject to change.";
616
617 const EXPLAIN_STMT_ATTR_SYNTAX: &'static str =
618     "attributes on non-item statements and expressions are experimental.";
619
620 pub fn check_for_box_syntax(f: Option<&Features>, diag: &Handler, span: Span) {
621     if let Some(&Features { allow_box: true, .. }) = f {
622         return;
623     }
624     emit_feature_err(diag, "box_syntax", span, GateIssue::Language, EXPLAIN_BOX_SYNTAX);
625 }
626
627 pub fn check_for_placement_in(f: Option<&Features>, diag: &Handler, span: Span) {
628     if let Some(&Features { allow_placement_in: true, .. }) = f {
629         return;
630     }
631     emit_feature_err(diag, "placement_in_syntax", span, GateIssue::Language, EXPLAIN_PLACEMENT_IN);
632 }
633
634 pub fn check_for_pushpop_syntax(f: Option<&Features>, diag: &Handler, span: Span) {
635     if let Some(&Features { allow_pushpop_unsafe: true, .. }) = f {
636         return;
637     }
638     emit_feature_err(diag, "pushpop_unsafe", span, GateIssue::Language, EXPLAIN_PUSHPOP_UNSAFE);
639 }
640
641 struct Context<'a> {
642     features: Vec<&'static str>,
643     span_handler: &'a Handler,
644     cm: &'a CodeMap,
645     plugin_attributes: &'a [(String, AttributeType)],
646 }
647
648 impl<'a> Context<'a> {
649     fn enable_feature(&mut self, feature: &'static str) {
650         debug!("enabling feature: {}", feature);
651         self.features.push(feature);
652     }
653
654     fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
655         let has_feature = self.has_feature(feature);
656         debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", feature, span, has_feature);
657         if !has_feature {
658             emit_feature_err(self.span_handler, feature, span, GateIssue::Language, explain);
659         }
660     }
661     fn has_feature(&self, feature: &str) -> bool {
662         self.features.iter().any(|&n| n == feature)
663     }
664
665     fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) {
666         debug!("check_attribute(attr = {:?})", attr);
667         let name = &*attr.name();
668         for &(n, ty, gateage) in KNOWN_ATTRIBUTES {
669             if n == name {
670                 if let Gated(gate, desc) = gateage {
671                     self.gate_feature(gate, attr.span, desc);
672                 }
673                 debug!("check_attribute: {:?} is known, {:?}, {:?}", name, ty, gateage);
674                 return;
675             }
676         }
677         for &(ref n, ref ty) in self.plugin_attributes {
678             if &*n == name {
679                 // Plugins can't gate attributes, so we don't check for it
680                 // unlike the code above; we only use this loop to
681                 // short-circuit to avoid the checks below
682                 debug!("check_attribute: {:?} is registered by a plugin, {:?}", name, ty);
683                 return;
684             }
685         }
686         if name.starts_with("rustc_") {
687             self.gate_feature("rustc_attrs", attr.span,
688                               "unless otherwise specified, attributes \
689                                with the prefix `rustc_` \
690                                are reserved for internal compiler diagnostics");
691         } else if name.starts_with("derive_") {
692             self.gate_feature("custom_derive", attr.span,
693                               "attributes of the form `#[derive_*]` are reserved \
694                                for the compiler");
695         } else {
696             // Only run the custom attribute lint during regular
697             // feature gate checking. Macro gating runs
698             // before the plugin attributes are registered
699             // so we skip this then
700             if !is_macro {
701                 self.gate_feature("custom_attribute", attr.span,
702                            &format!("The attribute `{}` is currently \
703                                     unknown to the compiler and \
704                                     may have meaning \
705                                     added to it in the future",
706                                     name));
707             }
708         }
709     }
710 }
711
712 fn find_lang_feature_issue(feature: &str) -> Option<u32> {
713     let info = KNOWN_FEATURES.iter()
714                               .find(|t| t.0 == feature)
715                               .unwrap();
716     let issue = info.2;
717     if let Active = info.3 {
718         // FIXME (#28244): enforce that active features have issue numbers
719         // assert!(issue.is_some())
720     }
721     issue
722 }
723
724 pub enum GateIssue {
725     Language,
726     Library(Option<u32>)
727 }
728
729 pub fn emit_feature_err(diag: &Handler, feature: &str, span: Span, issue: GateIssue,
730                         explain: &str) {
731     let issue = match issue {
732         GateIssue::Language => find_lang_feature_issue(feature),
733         GateIssue::Library(lib) => lib,
734     };
735
736     let mut err = if let Some(n) = issue {
737         diag.struct_span_err(span, &format!("{} (see issue #{})", explain, n))
738     } else {
739         diag.struct_span_err(span, explain)
740     };
741
742     // #23973: do not suggest `#![feature(...)]` if we are in beta/stable
743     if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some() {
744         err.emit();
745         return;
746     }
747     err.fileline_help(span, &format!("add #![feature({})] to the \
748                                       crate attributes to enable",
749                                      feature));
750     err.emit();
751 }
752
753 pub const EXPLAIN_ASM: &'static str =
754     "inline assembly is not stable enough for use and is subject to change";
755
756 pub const EXPLAIN_LOG_SYNTAX: &'static str =
757     "`log_syntax!` is not stable enough for use and is subject to change";
758
759 pub const EXPLAIN_CONCAT_IDENTS: &'static str =
760     "`concat_idents` is not stable enough for use and is subject to change";
761
762 pub const EXPLAIN_TRACE_MACROS: &'static str =
763     "`trace_macros` is not stable enough for use and is subject to change";
764 pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &'static str =
765     "allow_internal_unstable side-steps feature gating and stability checks";
766
767 pub const EXPLAIN_CUSTOM_DERIVE: &'static str =
768     "`#[derive]` for custom traits is not stable enough for use and is subject to change";
769
770 struct MacroVisitor<'a> {
771     context: &'a Context<'a>
772 }
773
774 impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
775     fn visit_mac(&mut self, mac: &ast::Mac) {
776         let path = &mac.node.path;
777         let name = path.segments.last().unwrap().identifier.name.as_str();
778
779         // Issue 22234: If you add a new case here, make sure to also
780         // add code to catch the macro during or after expansion.
781         //
782         // We still keep this MacroVisitor (rather than *solely*
783         // relying on catching cases during or after expansion) to
784         // catch uses of these macros within conditionally-compiled
785         // code, e.g. `#[cfg]`-guarded functions.
786
787         if name == "asm" {
788             self.context.gate_feature("asm", path.span, EXPLAIN_ASM);
789         }
790
791         else if name == "log_syntax" {
792             self.context.gate_feature("log_syntax", path.span, EXPLAIN_LOG_SYNTAX);
793         }
794
795         else if name == "trace_macros" {
796             self.context.gate_feature("trace_macros", path.span, EXPLAIN_TRACE_MACROS);
797         }
798
799         else if name == "concat_idents" {
800             self.context.gate_feature("concat_idents", path.span, EXPLAIN_CONCAT_IDENTS);
801         }
802     }
803
804     fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
805         self.context.check_attribute(attr, true);
806     }
807
808     fn visit_expr(&mut self, e: &ast::Expr) {
809         // Issue 22181: overloaded-`box` and placement-`in` are
810         // implemented via a desugaring expansion, so their feature
811         // gates go into MacroVisitor since that works pre-expansion.
812         //
813         // Issue 22234: we also check during expansion as well.
814         // But we keep these checks as a pre-expansion check to catch
815         // uses in e.g. conditionalized code.
816
817         if let ast::ExprBox(_) = e.node {
818             self.context.gate_feature("box_syntax", e.span, EXPLAIN_BOX_SYNTAX);
819         }
820
821         if let ast::ExprInPlace(..) = e.node {
822             self.context.gate_feature("placement_in_syntax", e.span, EXPLAIN_PLACEMENT_IN);
823         }
824
825         visit::walk_expr(self, e);
826     }
827 }
828
829 struct PostExpansionVisitor<'a> {
830     context: &'a Context<'a>,
831 }
832
833 impl<'a> PostExpansionVisitor<'a> {
834     fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
835         if !self.context.cm.span_allows_unstable(span) {
836             self.context.gate_feature(feature, span, explain)
837         }
838     }
839 }
840
841 impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
842     fn visit_attribute(&mut self, attr: &ast::Attribute) {
843         if !self.context.cm.span_allows_unstable(attr.span) {
844             self.context.check_attribute(attr, false);
845         }
846     }
847
848     fn visit_name(&mut self, sp: Span, name: ast::Name) {
849         if !name.as_str().is_ascii() {
850             self.gate_feature("non_ascii_idents", sp,
851                               "non-ascii idents are not fully supported.");
852         }
853     }
854
855     fn visit_item(&mut self, i: &ast::Item) {
856         match i.node {
857             ast::ItemExternCrate(_) => {
858                 if attr::contains_name(&i.attrs[..], "macro_reexport") {
859                     self.gate_feature("macro_reexport", i.span,
860                                       "macros reexports are experimental \
861                                        and possibly buggy");
862                 }
863             }
864
865             ast::ItemForeignMod(ref foreign_module) => {
866                 if attr::contains_name(&i.attrs[..], "link_args") {
867                     self.gate_feature("link_args", i.span,
868                                       "the `link_args` attribute is not portable \
869                                        across platforms, it is recommended to \
870                                        use `#[link(name = \"foo\")]` instead")
871                 }
872                 let maybe_feature = match foreign_module.abi {
873                     Abi::RustIntrinsic => Some(("intrinsics", "intrinsics are subject to change")),
874                     Abi::PlatformIntrinsic => {
875                         Some(("platform_intrinsics",
876                               "platform intrinsics are experimental and possibly buggy"))
877                     }
878                     _ => None
879                 };
880                 if let Some((feature, msg)) = maybe_feature {
881                     self.gate_feature(feature, i.span, msg)
882                 }
883             }
884
885             ast::ItemFn(..) => {
886                 if attr::contains_name(&i.attrs[..], "plugin_registrar") {
887                     self.gate_feature("plugin_registrar", i.span,
888                                       "compiler plugins are experimental and possibly buggy");
889                 }
890                 if attr::contains_name(&i.attrs[..], "start") {
891                     self.gate_feature("start", i.span,
892                                       "a #[start] function is an experimental \
893                                        feature whose signature may change \
894                                        over time");
895                 }
896                 if attr::contains_name(&i.attrs[..], "main") {
897                     self.gate_feature("main", i.span,
898                                       "declaration of a nonstandard #[main] \
899                                        function may change over time, for now \
900                                        a top-level `fn main()` is required");
901                 }
902             }
903
904             ast::ItemStruct(..) => {
905                 if attr::contains_name(&i.attrs[..], "simd") {
906                     self.gate_feature("simd", i.span,
907                                       "SIMD types are experimental and possibly buggy");
908                     self.context.span_handler.span_warn(i.span,
909                                                         "the `#[simd]` attribute is deprecated, \
910                                                          use `#[repr(simd)]` instead");
911                 }
912                 for attr in &i.attrs {
913                     if attr.name() == "repr" {
914                         for item in attr.meta_item_list().unwrap_or(&[]) {
915                             if item.name() == "simd" {
916                                 self.gate_feature("repr_simd", i.span,
917                                                   "SIMD types are experimental and possibly buggy");
918
919                             }
920                         }
921                     }
922                 }
923             }
924
925             ast::ItemDefaultImpl(..) => {
926                 self.gate_feature("optin_builtin_traits",
927                                   i.span,
928                                   "default trait implementations are experimental \
929                                    and possibly buggy");
930             }
931
932             ast::ItemImpl(_, polarity, _, _, _, _) => {
933                 match polarity {
934                     ast::ImplPolarity::Negative => {
935                         self.gate_feature("optin_builtin_traits",
936                                           i.span,
937                                           "negative trait bounds are not yet fully implemented; \
938                                           use marker types for now");
939                     },
940                     _ => {}
941                 }
942             }
943
944             _ => {}
945         }
946
947         visit::walk_item(self, i);
948     }
949
950     fn visit_variant_data(&mut self, s: &'v ast::VariantData, _: ast::Ident,
951                         _: &'v ast::Generics, _: ast::NodeId, span: Span) {
952         if s.fields().is_empty() {
953             if s.is_struct() {
954                 self.gate_feature("braced_empty_structs", span,
955                                   "empty structs and enum variants with braces are unstable");
956             } else if s.is_tuple() {
957                 self.context.span_handler.struct_span_err(span, "empty tuple structs and enum \
958                                                                  variants are not allowed, use \
959                                                                  unit structs and enum variants \
960                                                                  instead")
961                                          .span_help(span, "remove trailing `()` to make a unit \
962                                                            struct or unit enum variant")
963                                          .emit();
964             }
965         }
966         visit::walk_struct_def(self, s)
967     }
968
969     fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
970         let links_to_llvm = match attr::first_attr_value_str_by_name(&i.attrs,
971                                                                      "link_name") {
972             Some(val) => val.starts_with("llvm."),
973             _ => false
974         };
975         if links_to_llvm {
976             self.gate_feature("link_llvm_intrinsics", i.span,
977                               "linking to LLVM intrinsics is experimental");
978         }
979
980         visit::walk_foreign_item(self, i)
981     }
982
983     fn visit_expr(&mut self, e: &ast::Expr) {
984         match e.node {
985             ast::ExprBox(_) => {
986                 self.gate_feature("box_syntax",
987                                   e.span,
988                                   "box expression syntax is experimental; \
989                                    you can call `Box::new` instead.");
990             }
991             ast::ExprType(..) => {
992                 self.gate_feature("type_ascription", e.span,
993                                   "type ascription is experimental");
994             }
995             _ => {}
996         }
997         visit::walk_expr(self, e);
998     }
999
1000     fn visit_pat(&mut self, pattern: &ast::Pat) {
1001         match pattern.node {
1002             ast::PatVec(_, Some(_), ref last) if !last.is_empty() => {
1003                 self.gate_feature("advanced_slice_patterns",
1004                                   pattern.span,
1005                                   "multiple-element slice matches anywhere \
1006                                    but at the end of a slice (e.g. \
1007                                    `[0, ..xs, 0]`) are experimental")
1008             }
1009             ast::PatVec(..) => {
1010                 self.gate_feature("slice_patterns",
1011                                   pattern.span,
1012                                   "slice pattern syntax is experimental");
1013             }
1014             ast::PatBox(..) => {
1015                 self.gate_feature("box_patterns",
1016                                   pattern.span,
1017                                   "box pattern syntax is experimental");
1018             }
1019             _ => {}
1020         }
1021         visit::walk_pat(self, pattern)
1022     }
1023
1024     fn visit_fn(&mut self,
1025                 fn_kind: FnKind<'v>,
1026                 fn_decl: &'v ast::FnDecl,
1027                 block: &'v ast::Block,
1028                 span: Span,
1029                 _node_id: NodeId) {
1030         // check for const fn declarations
1031         match fn_kind {
1032             FnKind::ItemFn(_, _, _, ast::Constness::Const, _, _) => {
1033                 self.gate_feature("const_fn", span, "const fn is unstable");
1034             }
1035             _ => {
1036                 // stability of const fn methods are covered in
1037                 // visit_trait_item and visit_impl_item below; this is
1038                 // because default methods don't pass through this
1039                 // point.
1040             }
1041         }
1042
1043         match fn_kind {
1044             FnKind::ItemFn(_, _, _, _, abi, _) if abi == Abi::RustIntrinsic => {
1045                 self.gate_feature("intrinsics",
1046                                   span,
1047                                   "intrinsics are subject to change")
1048             }
1049             FnKind::ItemFn(_, _, _, _, abi, _) |
1050             FnKind::Method(_, &ast::MethodSig { abi, .. }, _) if abi == Abi::RustCall => {
1051                 self.gate_feature("unboxed_closures",
1052                                   span,
1053                                   "rust-call ABI is subject to change")
1054             }
1055             _ => {}
1056         }
1057         visit::walk_fn(self, fn_kind, fn_decl, block, span);
1058     }
1059
1060     fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) {
1061         match ti.node {
1062             ast::ConstTraitItem(..) => {
1063                 self.gate_feature("associated_consts",
1064                                   ti.span,
1065                                   "associated constants are experimental")
1066             }
1067             ast::MethodTraitItem(ref sig, _) => {
1068                 if sig.constness == ast::Constness::Const {
1069                     self.gate_feature("const_fn", ti.span, "const fn is unstable");
1070                 }
1071             }
1072             ast::TypeTraitItem(_, Some(_)) => {
1073                 self.gate_feature("associated_type_defaults", ti.span,
1074                                   "associated type defaults are unstable");
1075             }
1076             _ => {}
1077         }
1078         visit::walk_trait_item(self, ti);
1079     }
1080
1081     fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
1082         match ii.node {
1083             ast::ImplItemKind::Const(..) => {
1084                 self.gate_feature("associated_consts",
1085                                   ii.span,
1086                                   "associated constants are experimental")
1087             }
1088             ast::ImplItemKind::Method(ref sig, _) => {
1089                 if sig.constness == ast::Constness::Const {
1090                     self.gate_feature("const_fn", ii.span, "const fn is unstable");
1091                 }
1092             }
1093             _ => {}
1094         }
1095         visit::walk_impl_item(self, ii);
1096     }
1097 }
1098
1099 fn check_crate_inner<F>(cm: &CodeMap, span_handler: &Handler,
1100                         krate: &ast::Crate,
1101                         plugin_attributes: &[(String, AttributeType)],
1102                         check: F)
1103                        -> Features
1104     where F: FnOnce(&mut Context, &ast::Crate)
1105 {
1106     let mut cx = Context {
1107         features: Vec::new(),
1108         span_handler: span_handler,
1109         cm: cm,
1110         plugin_attributes: plugin_attributes,
1111     };
1112
1113     let mut accepted_features = Vec::new();
1114     let mut unknown_features = Vec::new();
1115
1116     for attr in &krate.attrs {
1117         if !attr.check_name("feature") {
1118             continue
1119         }
1120
1121         match attr.meta_item_list() {
1122             None => {
1123                 span_handler.span_err(attr.span, "malformed feature attribute, \
1124                                                   expected #![feature(...)]");
1125             }
1126             Some(list) => {
1127                 for mi in list {
1128                     let name = match mi.node {
1129                         ast::MetaWord(ref word) => (*word).clone(),
1130                         _ => {
1131                             span_handler.span_err(mi.span,
1132                                                   "malformed feature, expected just \
1133                                                    one word");
1134                             continue
1135                         }
1136                     };
1137                     match KNOWN_FEATURES.iter()
1138                                         .find(|& &(n, _, _, _)| name == n) {
1139                         Some(&(name, _, _, Active)) => {
1140                             cx.enable_feature(name);
1141                         }
1142                         Some(&(_, _, _, Removed)) => {
1143                             span_handler.span_err(mi.span, "feature has been removed");
1144                         }
1145                         Some(&(_, _, _, Accepted)) => {
1146                             accepted_features.push(mi.span);
1147                         }
1148                         None => {
1149                             unknown_features.push((name, mi.span));
1150                         }
1151                     }
1152                 }
1153             }
1154         }
1155     }
1156
1157     check(&mut cx, krate);
1158
1159     // FIXME (pnkfelix): Before adding the 99th entry below, change it
1160     // to a single-pass (instead of N calls to `.has_feature`).
1161
1162     Features {
1163         unboxed_closures: cx.has_feature("unboxed_closures"),
1164         rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
1165         allow_quote: cx.has_feature("quote"),
1166         allow_asm: cx.has_feature("asm"),
1167         allow_log_syntax: cx.has_feature("log_syntax"),
1168         allow_concat_idents: cx.has_feature("concat_idents"),
1169         allow_trace_macros: cx.has_feature("trace_macros"),
1170         allow_internal_unstable: cx.has_feature("allow_internal_unstable"),
1171         allow_custom_derive: cx.has_feature("custom_derive"),
1172         allow_placement_in: cx.has_feature("placement_in_syntax"),
1173         allow_box: cx.has_feature("box_syntax"),
1174         allow_pushpop_unsafe: cx.has_feature("pushpop_unsafe"),
1175         simd_ffi: cx.has_feature("simd_ffi"),
1176         unmarked_api: cx.has_feature("unmarked_api"),
1177         negate_unsigned: cx.has_feature("negate_unsigned"),
1178         declared_stable_lang_features: accepted_features,
1179         declared_lib_features: unknown_features,
1180         const_fn: cx.has_feature("const_fn"),
1181         const_indexing: cx.has_feature("const_indexing"),
1182         static_recursion: cx.has_feature("static_recursion"),
1183         default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"),
1184         type_macros: cx.has_feature("type_macros"),
1185         cfg_target_feature: cx.has_feature("cfg_target_feature"),
1186         cfg_target_vendor: cx.has_feature("cfg_target_vendor"),
1187         cfg_target_thread_local: cx.has_feature("cfg_target_thread_local"),
1188         augmented_assignments: cx.has_feature("augmented_assignments"),
1189         braced_empty_structs: cx.has_feature("braced_empty_structs"),
1190         staged_api: cx.has_feature("staged_api"),
1191         stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"),
1192         deprecated: cx.has_feature("deprecated"),
1193     }
1194 }
1195
1196 pub fn check_crate_macros(cm: &CodeMap, span_handler: &Handler, krate: &ast::Crate)
1197 -> Features {
1198     check_crate_inner(cm, span_handler, krate, &[] as &'static [_],
1199                       |ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
1200 }
1201
1202 pub fn check_crate(cm: &CodeMap, span_handler: &Handler, krate: &ast::Crate,
1203                    plugin_attributes: &[(String, AttributeType)],
1204                    unstable: UnstableFeatures) -> Features
1205 {
1206     maybe_stage_features(span_handler, krate, unstable);
1207
1208     check_crate_inner(cm, span_handler, krate, plugin_attributes,
1209                       |ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx },
1210                                                      krate))
1211 }
1212
1213 #[derive(Clone, Copy)]
1214 pub enum UnstableFeatures {
1215     /// Hard errors for unstable features are active, as on
1216     /// beta/stable channels.
1217     Disallow,
1218     /// Allow features to me activated, as on nightly.
1219     Allow,
1220     /// Errors are bypassed for bootstrapping. This is required any time
1221     /// during the build that feature-related lints are set to warn or above
1222     /// because the build turns on warnings-as-errors and uses lots of unstable
1223     /// features. As a result, this is always required for building Rust itself.
1224     Cheat
1225 }
1226
1227 fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate,
1228                         unstable: UnstableFeatures) {
1229     let allow_features = match unstable {
1230         UnstableFeatures::Allow => true,
1231         UnstableFeatures::Disallow => false,
1232         UnstableFeatures::Cheat => true
1233     };
1234     if !allow_features {
1235         for attr in &krate.attrs {
1236             if attr.check_name("feature") {
1237                 let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)");
1238                 let ref msg = format!("#[feature] may not be used on the {} release channel",
1239                                       release_channel);
1240                 span_handler.span_err(attr.span, msg);
1241             }
1242         }
1243     }
1244 }