]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/feature_gate.rs
Rollup merge of #30737 - Ms2ger:MutateMode, r=sanxiyn
[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 // (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     /// spans of #![feature] attrs for stable language features. for error reporting
552     pub declared_stable_lang_features: Vec<Span>,
553     /// #![feature] attrs for non-language (library) features
554     pub declared_lib_features: Vec<(InternedString, Span)>,
555     pub const_fn: bool,
556     pub const_indexing: bool,
557     pub static_recursion: bool,
558     pub default_type_parameter_fallback: bool,
559     pub type_macros: bool,
560     pub cfg_target_feature: bool,
561     pub cfg_target_vendor: bool,
562     pub cfg_target_thread_local: bool,
563     pub augmented_assignments: bool,
564     pub braced_empty_structs: bool,
565     pub staged_api: bool,
566     pub stmt_expr_attributes: bool,
567     pub deprecated: bool,
568 }
569
570 impl Features {
571     pub fn new() -> Features {
572         Features {
573             unboxed_closures: false,
574             rustc_diagnostic_macros: false,
575             allow_quote: false,
576             allow_asm: false,
577             allow_log_syntax: false,
578             allow_concat_idents: false,
579             allow_trace_macros: false,
580             allow_internal_unstable: false,
581             allow_custom_derive: false,
582             allow_placement_in: false,
583             allow_box: false,
584             allow_pushpop_unsafe: false,
585             simd_ffi: false,
586             unmarked_api: false,
587             declared_stable_lang_features: Vec::new(),
588             declared_lib_features: Vec::new(),
589             const_fn: false,
590             const_indexing: false,
591             static_recursion: false,
592             default_type_parameter_fallback: false,
593             type_macros: false,
594             cfg_target_feature: false,
595             cfg_target_vendor: false,
596             cfg_target_thread_local: false,
597             augmented_assignments: false,
598             braced_empty_structs: false,
599             staged_api: false,
600             stmt_expr_attributes: false,
601             deprecated: false,
602         }
603     }
604 }
605
606 const EXPLAIN_BOX_SYNTAX: &'static str =
607     "box expression syntax is experimental; you can call `Box::new` instead.";
608
609 const EXPLAIN_PLACEMENT_IN: &'static str =
610     "placement-in expression syntax is experimental and subject to change.";
611
612 const EXPLAIN_PUSHPOP_UNSAFE: &'static str =
613     "push/pop_unsafe macros are experimental and subject to change.";
614
615 const EXPLAIN_STMT_ATTR_SYNTAX: &'static str =
616     "attributes on non-item statements and expressions are experimental.";
617
618 pub fn check_for_box_syntax(f: Option<&Features>, diag: &Handler, span: Span) {
619     if let Some(&Features { allow_box: true, .. }) = f {
620         return;
621     }
622     emit_feature_err(diag, "box_syntax", span, GateIssue::Language, EXPLAIN_BOX_SYNTAX);
623 }
624
625 pub fn check_for_placement_in(f: Option<&Features>, diag: &Handler, span: Span) {
626     if let Some(&Features { allow_placement_in: true, .. }) = f {
627         return;
628     }
629     emit_feature_err(diag, "placement_in_syntax", span, GateIssue::Language, EXPLAIN_PLACEMENT_IN);
630 }
631
632 pub fn check_for_pushpop_syntax(f: Option<&Features>, diag: &Handler, span: Span) {
633     if let Some(&Features { allow_pushpop_unsafe: true, .. }) = f {
634         return;
635     }
636     emit_feature_err(diag, "pushpop_unsafe", span, GateIssue::Language, EXPLAIN_PUSHPOP_UNSAFE);
637 }
638
639 struct Context<'a> {
640     features: Vec<&'static str>,
641     span_handler: &'a Handler,
642     cm: &'a CodeMap,
643     plugin_attributes: &'a [(String, AttributeType)],
644 }
645
646 impl<'a> Context<'a> {
647     fn enable_feature(&mut self, feature: &'static str) {
648         debug!("enabling feature: {}", feature);
649         self.features.push(feature);
650     }
651
652     fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
653         let has_feature = self.has_feature(feature);
654         debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", feature, span, has_feature);
655         if !has_feature {
656             emit_feature_err(self.span_handler, feature, span, GateIssue::Language, explain);
657         }
658     }
659     fn has_feature(&self, feature: &str) -> bool {
660         self.features.iter().any(|&n| n == feature)
661     }
662
663     fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) {
664         debug!("check_attribute(attr = {:?})", attr);
665         let name = &*attr.name();
666         for &(n, ty, gateage) in KNOWN_ATTRIBUTES {
667             if n == name {
668                 if let Gated(gate, desc) = gateage {
669                     self.gate_feature(gate, attr.span, desc);
670                 }
671                 debug!("check_attribute: {:?} is known, {:?}, {:?}", name, ty, gateage);
672                 return;
673             }
674         }
675         for &(ref n, ref ty) in self.plugin_attributes {
676             if &*n == name {
677                 // Plugins can't gate attributes, so we don't check for it
678                 // unlike the code above; we only use this loop to
679                 // short-circuit to avoid the checks below
680                 debug!("check_attribute: {:?} is registered by a plugin, {:?}", name, ty);
681                 return;
682             }
683         }
684         if name.starts_with("rustc_") {
685             self.gate_feature("rustc_attrs", attr.span,
686                               "unless otherwise specified, attributes \
687                                with the prefix `rustc_` \
688                                are reserved for internal compiler diagnostics");
689         } else if name.starts_with("derive_") {
690             self.gate_feature("custom_derive", attr.span,
691                               "attributes of the form `#[derive_*]` are reserved \
692                                for the compiler");
693         } else {
694             // Only run the custom attribute lint during regular
695             // feature gate checking. Macro gating runs
696             // before the plugin attributes are registered
697             // so we skip this then
698             if !is_macro {
699                 self.gate_feature("custom_attribute", attr.span,
700                            &format!("The attribute `{}` is currently \
701                                     unknown to the compiler and \
702                                     may have meaning \
703                                     added to it in the future",
704                                     name));
705             }
706         }
707     }
708 }
709
710 fn find_lang_feature_issue(feature: &str) -> Option<u32> {
711     let info = KNOWN_FEATURES.iter()
712                               .find(|t| t.0 == feature)
713                               .unwrap();
714     let issue = info.2;
715     if let Active = info.3 {
716         // FIXME (#28244): enforce that active features have issue numbers
717         // assert!(issue.is_some())
718     }
719     issue
720 }
721
722 pub enum GateIssue {
723     Language,
724     Library(Option<u32>)
725 }
726
727 pub fn emit_feature_err(diag: &Handler, feature: &str, span: Span, issue: GateIssue,
728                         explain: &str) {
729     let issue = match issue {
730         GateIssue::Language => find_lang_feature_issue(feature),
731         GateIssue::Library(lib) => lib,
732     };
733
734     let mut err = if let Some(n) = issue {
735         diag.struct_span_err(span, &format!("{} (see issue #{})", explain, n))
736     } else {
737         diag.struct_span_err(span, explain)
738     };
739
740     // #23973: do not suggest `#![feature(...)]` if we are in beta/stable
741     if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some() {
742         err.emit();
743         return;
744     }
745     err.fileline_help(span, &format!("add #![feature({})] to the \
746                                       crate attributes to enable",
747                                      feature));
748     err.emit();
749 }
750
751 pub const EXPLAIN_ASM: &'static str =
752     "inline assembly is not stable enough for use and is subject to change";
753
754 pub const EXPLAIN_LOG_SYNTAX: &'static str =
755     "`log_syntax!` is not stable enough for use and is subject to change";
756
757 pub const EXPLAIN_CONCAT_IDENTS: &'static str =
758     "`concat_idents` is not stable enough for use and is subject to change";
759
760 pub const EXPLAIN_TRACE_MACROS: &'static str =
761     "`trace_macros` is not stable enough for use and is subject to change";
762 pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &'static str =
763     "allow_internal_unstable side-steps feature gating and stability checks";
764
765 pub const EXPLAIN_CUSTOM_DERIVE: &'static str =
766     "`#[derive]` for custom traits is not stable enough for use and is subject to change";
767
768 struct MacroVisitor<'a> {
769     context: &'a Context<'a>
770 }
771
772 impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
773     fn visit_mac(&mut self, mac: &ast::Mac) {
774         let path = &mac.node.path;
775         let name = path.segments.last().unwrap().identifier.name.as_str();
776
777         // Issue 22234: If you add a new case here, make sure to also
778         // add code to catch the macro during or after expansion.
779         //
780         // We still keep this MacroVisitor (rather than *solely*
781         // relying on catching cases during or after expansion) to
782         // catch uses of these macros within conditionally-compiled
783         // code, e.g. `#[cfg]`-guarded functions.
784
785         if name == "asm" {
786             self.context.gate_feature("asm", path.span, EXPLAIN_ASM);
787         }
788
789         else if name == "log_syntax" {
790             self.context.gate_feature("log_syntax", path.span, EXPLAIN_LOG_SYNTAX);
791         }
792
793         else if name == "trace_macros" {
794             self.context.gate_feature("trace_macros", path.span, EXPLAIN_TRACE_MACROS);
795         }
796
797         else if name == "concat_idents" {
798             self.context.gate_feature("concat_idents", path.span, EXPLAIN_CONCAT_IDENTS);
799         }
800     }
801
802     fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
803         self.context.check_attribute(attr, true);
804     }
805
806     fn visit_expr(&mut self, e: &ast::Expr) {
807         // Issue 22181: overloaded-`box` and placement-`in` are
808         // implemented via a desugaring expansion, so their feature
809         // gates go into MacroVisitor since that works pre-expansion.
810         //
811         // Issue 22234: we also check during expansion as well.
812         // But we keep these checks as a pre-expansion check to catch
813         // uses in e.g. conditionalized code.
814
815         if let ast::ExprBox(_) = e.node {
816             self.context.gate_feature("box_syntax", e.span, EXPLAIN_BOX_SYNTAX);
817         }
818
819         if let ast::ExprInPlace(..) = e.node {
820             self.context.gate_feature("placement_in_syntax", e.span, EXPLAIN_PLACEMENT_IN);
821         }
822
823         visit::walk_expr(self, e);
824     }
825 }
826
827 struct PostExpansionVisitor<'a> {
828     context: &'a Context<'a>,
829 }
830
831 impl<'a> PostExpansionVisitor<'a> {
832     fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
833         if !self.context.cm.span_allows_unstable(span) {
834             self.context.gate_feature(feature, span, explain)
835         }
836     }
837 }
838
839 impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
840     fn visit_attribute(&mut self, attr: &ast::Attribute) {
841         if !self.context.cm.span_allows_unstable(attr.span) {
842             self.context.check_attribute(attr, false);
843         }
844     }
845
846     fn visit_name(&mut self, sp: Span, name: ast::Name) {
847         if !name.as_str().is_ascii() {
848             self.gate_feature("non_ascii_idents", sp,
849                               "non-ascii idents are not fully supported.");
850         }
851     }
852
853     fn visit_item(&mut self, i: &ast::Item) {
854         match i.node {
855             ast::ItemExternCrate(_) => {
856                 if attr::contains_name(&i.attrs[..], "macro_reexport") {
857                     self.gate_feature("macro_reexport", i.span,
858                                       "macros reexports are experimental \
859                                        and possibly buggy");
860                 }
861             }
862
863             ast::ItemForeignMod(ref foreign_module) => {
864                 if attr::contains_name(&i.attrs[..], "link_args") {
865                     self.gate_feature("link_args", i.span,
866                                       "the `link_args` attribute is not portable \
867                                        across platforms, it is recommended to \
868                                        use `#[link(name = \"foo\")]` instead")
869                 }
870                 let maybe_feature = match foreign_module.abi {
871                     Abi::RustIntrinsic => Some(("intrinsics", "intrinsics are subject to change")),
872                     Abi::PlatformIntrinsic => {
873                         Some(("platform_intrinsics",
874                               "platform intrinsics are experimental and possibly buggy"))
875                     }
876                     _ => None
877                 };
878                 if let Some((feature, msg)) = maybe_feature {
879                     self.gate_feature(feature, i.span, msg)
880                 }
881             }
882
883             ast::ItemFn(..) => {
884                 if attr::contains_name(&i.attrs[..], "plugin_registrar") {
885                     self.gate_feature("plugin_registrar", i.span,
886                                       "compiler plugins are experimental and possibly buggy");
887                 }
888                 if attr::contains_name(&i.attrs[..], "start") {
889                     self.gate_feature("start", i.span,
890                                       "a #[start] function is an experimental \
891                                        feature whose signature may change \
892                                        over time");
893                 }
894                 if attr::contains_name(&i.attrs[..], "main") {
895                     self.gate_feature("main", i.span,
896                                       "declaration of a nonstandard #[main] \
897                                        function may change over time, for now \
898                                        a top-level `fn main()` is required");
899                 }
900             }
901
902             ast::ItemStruct(..) => {
903                 if attr::contains_name(&i.attrs[..], "simd") {
904                     self.gate_feature("simd", i.span,
905                                       "SIMD types are experimental and possibly buggy");
906                     self.context.span_handler.span_warn(i.span,
907                                                         "the `#[simd]` attribute is deprecated, \
908                                                          use `#[repr(simd)]` instead");
909                 }
910                 for attr in &i.attrs {
911                     if attr.name() == "repr" {
912                         for item in attr.meta_item_list().unwrap_or(&[]) {
913                             if item.name() == "simd" {
914                                 self.gate_feature("repr_simd", i.span,
915                                                   "SIMD types are experimental and possibly buggy");
916
917                             }
918                         }
919                     }
920                 }
921             }
922
923             ast::ItemDefaultImpl(..) => {
924                 self.gate_feature("optin_builtin_traits",
925                                   i.span,
926                                   "default trait implementations are experimental \
927                                    and possibly buggy");
928             }
929
930             ast::ItemImpl(_, polarity, _, _, _, _) => {
931                 match polarity {
932                     ast::ImplPolarity::Negative => {
933                         self.gate_feature("optin_builtin_traits",
934                                           i.span,
935                                           "negative trait bounds are not yet fully implemented; \
936                                           use marker types for now");
937                     },
938                     _ => {}
939                 }
940             }
941
942             _ => {}
943         }
944
945         visit::walk_item(self, i);
946     }
947
948     fn visit_variant_data(&mut self, s: &'v ast::VariantData, _: ast::Ident,
949                         _: &'v ast::Generics, _: ast::NodeId, span: Span) {
950         if s.fields().is_empty() {
951             if s.is_struct() {
952                 self.gate_feature("braced_empty_structs", span,
953                                   "empty structs and enum variants with braces are unstable");
954             } else if s.is_tuple() {
955                 self.context.span_handler.struct_span_err(span, "empty tuple structs and enum \
956                                                                  variants are not allowed, use \
957                                                                  unit structs and enum variants \
958                                                                  instead")
959                                          .span_help(span, "remove trailing `()` to make a unit \
960                                                            struct or unit enum variant")
961                                          .emit();
962             }
963         }
964         visit::walk_struct_def(self, s)
965     }
966
967     fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
968         let links_to_llvm = match attr::first_attr_value_str_by_name(&i.attrs,
969                                                                      "link_name") {
970             Some(val) => val.starts_with("llvm."),
971             _ => false
972         };
973         if links_to_llvm {
974             self.gate_feature("link_llvm_intrinsics", i.span,
975                               "linking to LLVM intrinsics is experimental");
976         }
977
978         visit::walk_foreign_item(self, i)
979     }
980
981     fn visit_expr(&mut self, e: &ast::Expr) {
982         match e.node {
983             ast::ExprBox(_) => {
984                 self.gate_feature("box_syntax",
985                                   e.span,
986                                   "box expression syntax is experimental; \
987                                    you can call `Box::new` instead.");
988             }
989             ast::ExprType(..) => {
990                 self.gate_feature("type_ascription", e.span,
991                                   "type ascription is experimental");
992             }
993             _ => {}
994         }
995         visit::walk_expr(self, e);
996     }
997
998     fn visit_pat(&mut self, pattern: &ast::Pat) {
999         match pattern.node {
1000             ast::PatVec(_, Some(_), ref last) if !last.is_empty() => {
1001                 self.gate_feature("advanced_slice_patterns",
1002                                   pattern.span,
1003                                   "multiple-element slice matches anywhere \
1004                                    but at the end of a slice (e.g. \
1005                                    `[0, ..xs, 0]`) are experimental")
1006             }
1007             ast::PatVec(..) => {
1008                 self.gate_feature("slice_patterns",
1009                                   pattern.span,
1010                                   "slice pattern syntax is experimental");
1011             }
1012             ast::PatBox(..) => {
1013                 self.gate_feature("box_patterns",
1014                                   pattern.span,
1015                                   "box pattern syntax is experimental");
1016             }
1017             _ => {}
1018         }
1019         visit::walk_pat(self, pattern)
1020     }
1021
1022     fn visit_fn(&mut self,
1023                 fn_kind: FnKind<'v>,
1024                 fn_decl: &'v ast::FnDecl,
1025                 block: &'v ast::Block,
1026                 span: Span,
1027                 _node_id: NodeId) {
1028         // check for const fn declarations
1029         match fn_kind {
1030             FnKind::ItemFn(_, _, _, ast::Constness::Const, _, _) => {
1031                 self.gate_feature("const_fn", span, "const fn is unstable");
1032             }
1033             _ => {
1034                 // stability of const fn methods are covered in
1035                 // visit_trait_item and visit_impl_item below; this is
1036                 // because default methods don't pass through this
1037                 // point.
1038             }
1039         }
1040
1041         match fn_kind {
1042             FnKind::ItemFn(_, _, _, _, abi, _) if abi == Abi::RustIntrinsic => {
1043                 self.gate_feature("intrinsics",
1044                                   span,
1045                                   "intrinsics are subject to change")
1046             }
1047             FnKind::ItemFn(_, _, _, _, abi, _) |
1048             FnKind::Method(_, &ast::MethodSig { abi, .. }, _) if abi == Abi::RustCall => {
1049                 self.gate_feature("unboxed_closures",
1050                                   span,
1051                                   "rust-call ABI is subject to change")
1052             }
1053             _ => {}
1054         }
1055         visit::walk_fn(self, fn_kind, fn_decl, block, span);
1056     }
1057
1058     fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) {
1059         match ti.node {
1060             ast::ConstTraitItem(..) => {
1061                 self.gate_feature("associated_consts",
1062                                   ti.span,
1063                                   "associated constants are experimental")
1064             }
1065             ast::MethodTraitItem(ref sig, _) => {
1066                 if sig.constness == ast::Constness::Const {
1067                     self.gate_feature("const_fn", ti.span, "const fn is unstable");
1068                 }
1069             }
1070             ast::TypeTraitItem(_, Some(_)) => {
1071                 self.gate_feature("associated_type_defaults", ti.span,
1072                                   "associated type defaults are unstable");
1073             }
1074             _ => {}
1075         }
1076         visit::walk_trait_item(self, ti);
1077     }
1078
1079     fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
1080         match ii.node {
1081             ast::ImplItemKind::Const(..) => {
1082                 self.gate_feature("associated_consts",
1083                                   ii.span,
1084                                   "associated constants are experimental")
1085             }
1086             ast::ImplItemKind::Method(ref sig, _) => {
1087                 if sig.constness == ast::Constness::Const {
1088                     self.gate_feature("const_fn", ii.span, "const fn is unstable");
1089                 }
1090             }
1091             _ => {}
1092         }
1093         visit::walk_impl_item(self, ii);
1094     }
1095 }
1096
1097 fn check_crate_inner<F>(cm: &CodeMap, span_handler: &Handler,
1098                         krate: &ast::Crate,
1099                         plugin_attributes: &[(String, AttributeType)],
1100                         check: F)
1101                        -> Features
1102     where F: FnOnce(&mut Context, &ast::Crate)
1103 {
1104     let mut cx = Context {
1105         features: Vec::new(),
1106         span_handler: span_handler,
1107         cm: cm,
1108         plugin_attributes: plugin_attributes,
1109     };
1110
1111     let mut accepted_features = Vec::new();
1112     let mut unknown_features = Vec::new();
1113
1114     for attr in &krate.attrs {
1115         if !attr.check_name("feature") {
1116             continue
1117         }
1118
1119         match attr.meta_item_list() {
1120             None => {
1121                 span_handler.span_err(attr.span, "malformed feature attribute, \
1122                                                   expected #![feature(...)]");
1123             }
1124             Some(list) => {
1125                 for mi in list {
1126                     let name = match mi.node {
1127                         ast::MetaWord(ref word) => (*word).clone(),
1128                         _ => {
1129                             span_handler.span_err(mi.span,
1130                                                   "malformed feature, expected just \
1131                                                    one word");
1132                             continue
1133                         }
1134                     };
1135                     match KNOWN_FEATURES.iter()
1136                                         .find(|& &(n, _, _, _)| name == n) {
1137                         Some(&(name, _, _, Active)) => {
1138                             cx.enable_feature(name);
1139                         }
1140                         Some(&(_, _, _, Removed)) => {
1141                             span_handler.span_err(mi.span, "feature has been removed");
1142                         }
1143                         Some(&(_, _, _, Accepted)) => {
1144                             accepted_features.push(mi.span);
1145                         }
1146                         None => {
1147                             unknown_features.push((name, mi.span));
1148                         }
1149                     }
1150                 }
1151             }
1152         }
1153     }
1154
1155     check(&mut cx, krate);
1156
1157     // FIXME (pnkfelix): Before adding the 99th entry below, change it
1158     // to a single-pass (instead of N calls to `.has_feature`).
1159
1160     Features {
1161         unboxed_closures: cx.has_feature("unboxed_closures"),
1162         rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
1163         allow_quote: cx.has_feature("quote"),
1164         allow_asm: cx.has_feature("asm"),
1165         allow_log_syntax: cx.has_feature("log_syntax"),
1166         allow_concat_idents: cx.has_feature("concat_idents"),
1167         allow_trace_macros: cx.has_feature("trace_macros"),
1168         allow_internal_unstable: cx.has_feature("allow_internal_unstable"),
1169         allow_custom_derive: cx.has_feature("custom_derive"),
1170         allow_placement_in: cx.has_feature("placement_in_syntax"),
1171         allow_box: cx.has_feature("box_syntax"),
1172         allow_pushpop_unsafe: cx.has_feature("pushpop_unsafe"),
1173         simd_ffi: cx.has_feature("simd_ffi"),
1174         unmarked_api: cx.has_feature("unmarked_api"),
1175         declared_stable_lang_features: accepted_features,
1176         declared_lib_features: unknown_features,
1177         const_fn: cx.has_feature("const_fn"),
1178         const_indexing: cx.has_feature("const_indexing"),
1179         static_recursion: cx.has_feature("static_recursion"),
1180         default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"),
1181         type_macros: cx.has_feature("type_macros"),
1182         cfg_target_feature: cx.has_feature("cfg_target_feature"),
1183         cfg_target_vendor: cx.has_feature("cfg_target_vendor"),
1184         cfg_target_thread_local: cx.has_feature("cfg_target_thread_local"),
1185         augmented_assignments: cx.has_feature("augmented_assignments"),
1186         braced_empty_structs: cx.has_feature("braced_empty_structs"),
1187         staged_api: cx.has_feature("staged_api"),
1188         stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"),
1189         deprecated: cx.has_feature("deprecated"),
1190     }
1191 }
1192
1193 pub fn check_crate_macros(cm: &CodeMap, span_handler: &Handler, krate: &ast::Crate)
1194 -> Features {
1195     check_crate_inner(cm, span_handler, krate, &[] as &'static [_],
1196                       |ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
1197 }
1198
1199 pub fn check_crate(cm: &CodeMap, span_handler: &Handler, krate: &ast::Crate,
1200                    plugin_attributes: &[(String, AttributeType)],
1201                    unstable: UnstableFeatures) -> Features
1202 {
1203     maybe_stage_features(span_handler, krate, unstable);
1204
1205     check_crate_inner(cm, span_handler, krate, plugin_attributes,
1206                       |ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx },
1207                                                      krate))
1208 }
1209
1210 #[derive(Clone, Copy)]
1211 pub enum UnstableFeatures {
1212     /// Hard errors for unstable features are active, as on
1213     /// beta/stable channels.
1214     Disallow,
1215     /// Allow features to me activated, as on nightly.
1216     Allow,
1217     /// Errors are bypassed for bootstrapping. This is required any time
1218     /// during the build that feature-related lints are set to warn or above
1219     /// because the build turns on warnings-as-errors and uses lots of unstable
1220     /// features. As a result, this is always required for building Rust itself.
1221     Cheat
1222 }
1223
1224 fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate,
1225                         unstable: UnstableFeatures) {
1226     let allow_features = match unstable {
1227         UnstableFeatures::Allow => true,
1228         UnstableFeatures::Disallow => false,
1229         UnstableFeatures::Cheat => true
1230     };
1231     if !allow_features {
1232         for attr in &krate.attrs {
1233             if attr.check_name("feature") {
1234                 let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)");
1235                 let ref msg = format!("#[feature] may not be used on the {} release channel",
1236                                       release_channel);
1237                 span_handler.span_err(attr.span, msg);
1238             }
1239         }
1240     }
1241 }