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