]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/feature_gate.rs
a12291161f74f657d2bacc5288cabdab42cc6cc5
[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
28 use abi::Abi;
29 use ast::NodeId;
30 use ast;
31 use attr;
32 use attr::AttrMetaMethods;
33 use codemap::{CodeMap, Span};
34 use diagnostic::SpanHandler;
35 use visit;
36 use visit::Visitor;
37 use parse::token::{self, InternedString};
38
39 use std::ascii::AsciiExt;
40
41 // If you change this list without updating src/doc/reference.md, @cmr will be sad
42 // Don't ever remove anything from this list; set them to 'Removed'.
43 // The version numbers here correspond to the version in which the current status
44 // was set. This is most important for knowing when a particular feature became
45 // stable (active).
46 // NB: The featureck.py script parses this information directly out of the source
47 // so take care when modifying it.
48 const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
49     ("globs", "1.0.0", Accepted),
50     ("macro_rules", "1.0.0", Accepted),
51     ("struct_variant", "1.0.0", Accepted),
52     ("asm", "1.0.0", Active),
53     ("managed_boxes", "1.0.0", Removed),
54     ("non_ascii_idents", "1.0.0", Active),
55     ("thread_local", "1.0.0", Active),
56     ("link_args", "1.0.0", Active),
57     ("plugin_registrar", "1.0.0", Active),
58     ("log_syntax", "1.0.0", Active),
59     ("trace_macros", "1.0.0", Active),
60     ("concat_idents", "1.0.0", Active),
61     ("intrinsics", "1.0.0", Active),
62     ("lang_items", "1.0.0", Active),
63
64     ("simd", "1.0.0", Active),
65     ("default_type_params", "1.0.0", Accepted),
66     ("quote", "1.0.0", Active),
67     ("link_llvm_intrinsics", "1.0.0", Active),
68     ("linkage", "1.0.0", Active),
69     ("struct_inherit", "1.0.0", Removed),
70
71     ("quad_precision_float", "1.0.0", Removed),
72
73     ("rustc_diagnostic_macros", "1.0.0", Active),
74     ("unboxed_closures", "1.0.0", Active),
75     ("reflect", "1.0.0", Active),
76     ("import_shadowing", "1.0.0", Removed),
77     ("advanced_slice_patterns", "1.0.0", Active),
78     ("tuple_indexing", "1.0.0", Accepted),
79     ("associated_types", "1.0.0", Accepted),
80     ("visible_private_types", "1.0.0", Active),
81     ("slicing_syntax", "1.0.0", Accepted),
82     ("box_syntax", "1.0.0", Active),
83     ("placement_in_syntax", "1.0.0", Active),
84     ("pushpop_unsafe", "1.2.0", Active),
85     ("on_unimplemented", "1.0.0", Active),
86     ("simd_ffi", "1.0.0", Active),
87     ("allocator", "1.0.0", Active),
88     ("needs_allocator", "1.4.0", Active),
89     ("linked_from", "1.3.0", Active),
90
91     ("if_let", "1.0.0", Accepted),
92     ("while_let", "1.0.0", Accepted),
93
94     ("plugin", "1.0.0", Active),
95     ("start", "1.0.0", Active),
96     ("main", "1.0.0", Active),
97
98     ("fundamental", "1.0.0", Active),
99
100     // A temporary feature gate used to enable parser extensions needed
101     // to bootstrap fix for #5723.
102     ("issue_5723_bootstrap", "1.0.0", Accepted),
103
104     // A way to temporarily opt out of opt in copy. This will *never* be accepted.
105     ("opt_out_copy", "1.0.0", Removed),
106
107     // OIBIT specific features
108     ("optin_builtin_traits", "1.0.0", Active),
109
110     // macro reexport needs more discussion and stabilization
111     ("macro_reexport", "1.0.0", Active),
112
113     // These are used to test this portion of the compiler, they don't actually
114     // mean anything
115     ("test_accepted_feature", "1.0.0", Accepted),
116     ("test_removed_feature", "1.0.0", Removed),
117
118     // Allows use of #[staged_api]
119     ("staged_api", "1.0.0", Active),
120
121     // Allows using items which are missing stability attributes
122     ("unmarked_api", "1.0.0", Active),
123
124     // Allows using #![no_std]
125     ("no_std", "1.0.0", Active),
126
127     // Allows using #![no_core]
128     ("no_core", "1.3.0", Active),
129
130     // Allows using `box` in patterns; RFC 469
131     ("box_patterns", "1.0.0", Active),
132
133     // Allows using the unsafe_no_drop_flag attribute (unlikely to
134     // switch to Accepted; see RFC 320)
135     ("unsafe_no_drop_flag", "1.0.0", Active),
136
137     // Allows the use of custom attributes; RFC 572
138     ("custom_attribute", "1.0.0", Active),
139
140     // Allows the use of #[derive(Anything)] as sugar for
141     // #[derive_Anything].
142     ("custom_derive", "1.0.0", Active),
143
144     // Allows the use of rustc_* attributes; RFC 572
145     ("rustc_attrs", "1.0.0", Active),
146
147     // Allows the use of #[allow_internal_unstable]. This is an
148     // attribute on macro_rules! and can't use the attribute handling
149     // below (it has to be checked before expansion possibly makes
150     // macros disappear).
151     ("allow_internal_unstable", "1.0.0", Active),
152
153     // #23121. Array patterns have some hazards yet.
154     ("slice_patterns", "1.0.0", Active),
155
156     // Allows use of unary negate on unsigned integers, e.g. -e for e: u8
157     ("negate_unsigned", "1.0.0", Active),
158
159     // Allows the definition of associated constants in `trait` or `impl`
160     // blocks.
161     ("associated_consts", "1.0.0", Active),
162
163     // Allows the definition of `const fn` functions.
164     ("const_fn", "1.2.0", Active),
165
166     // Allows using #[prelude_import] on glob `use` items.
167     ("prelude_import", "1.2.0", Active),
168
169     // Allows the definition recursive static items.
170     ("static_recursion", "1.3.0", Active),
171
172     // Allows default type parameters to influence type inference.
173     ("default_type_parameter_fallback", "1.3.0", Active),
174
175     // Allows associated type defaults
176     ("associated_type_defaults", "1.2.0", Active),
177     // Allows macros to appear in the type position.
178
179     ("type_macros", "1.3.0", Active),
180
181     // allow `repr(simd)`, and importing the various simd intrinsics
182     ("simd_basics", "1.3.0", Active),
183 ];
184 // (changing above list without updating src/doc/reference.md makes @cmr sad)
185
186 enum Status {
187     /// Represents an active feature that is currently being implemented or
188     /// currently being considered for addition/removal.
189     Active,
190
191     /// Represents a feature which has since been removed (it was once Active)
192     Removed,
193
194     /// This language feature has since been Accepted (it was once Active)
195     Accepted,
196 }
197
198 // Attributes that have a special meaning to rustc or rustdoc
199 pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[
200     // Normal attributes
201
202     ("warn", Normal),
203     ("allow", Normal),
204     ("forbid", Normal),
205     ("deny", Normal),
206
207     ("macro_reexport", Normal),
208     ("macro_use", Normal),
209     ("macro_export", Normal),
210     ("plugin_registrar", Normal),
211
212     ("cfg", Normal),
213     ("cfg_attr", Normal),
214     ("main", Normal),
215     ("start", Normal),
216     ("test", Normal),
217     ("bench", Normal),
218     ("simd", Normal),
219     ("repr", Normal),
220     ("path", Normal),
221     ("abi", Normal),
222     ("automatically_derived", Normal),
223     ("no_mangle", Normal),
224     ("no_link", Normal),
225     ("derive", Normal),
226     ("should_panic", Normal),
227     ("ignore", Normal),
228     ("no_implicit_prelude", Normal),
229     ("reexport_test_harness_main", Normal),
230     ("link_args", Normal),
231     ("macro_escape", Normal),
232
233     // Not used any more, but we can't feature gate it
234     ("no_stack_check", Normal),
235
236     ("staged_api", Gated("staged_api",
237                          "staged_api is for use by rustc only")),
238     ("plugin", Gated("plugin",
239                      "compiler plugins are experimental \
240                       and possibly buggy")),
241     ("no_std", Gated("no_std",
242                      "no_std is experimental")),
243     ("no_core", Gated("no_core",
244                      "no_core is experimental")),
245     ("lang", Gated("lang_items",
246                      "language items are subject to change")),
247     ("linkage", Gated("linkage",
248                       "the `linkage` attribute is experimental \
249                        and not portable across platforms")),
250     ("thread_local", Gated("thread_local",
251                             "`#[thread_local]` is an experimental feature, and does not \
252                              currently handle destructors. There is no corresponding \
253                              `#[task_local]` mapping to the task model")),
254
255     ("rustc_on_unimplemented", Gated("on_unimplemented",
256                                      "the `#[rustc_on_unimplemented]` attribute \
257                                       is an experimental feature")),
258     ("allocator", Gated("allocator",
259                         "the `#[allocator]` attribute is an experimental feature")),
260     ("needs_allocator", Gated("needs_allocator", "the `#[needs_allocator]` \
261                                                   attribute is an experimental \
262                                                   feature")),
263     ("rustc_variance", Gated("rustc_attrs",
264                              "the `#[rustc_variance]` attribute \
265                               is an experimental feature")),
266     ("rustc_error", Gated("rustc_attrs",
267                           "the `#[rustc_error]` attribute \
268                            is an experimental feature")),
269     ("rustc_move_fragments", Gated("rustc_attrs",
270                                    "the `#[rustc_move_fragments]` attribute \
271                                     is an experimental feature")),
272
273     ("allow_internal_unstable", Gated("allow_internal_unstable",
274                                       EXPLAIN_ALLOW_INTERNAL_UNSTABLE)),
275
276     ("fundamental", Gated("fundamental",
277                           "the `#[fundamental]` attribute \
278                            is an experimental feature")),
279
280     ("linked_from", Gated("linked_from",
281                           "the `#[linked_from]` attribute \
282                            is an experimental feature")),
283
284     // FIXME: #14408 whitelist docs since rustdoc looks at them
285     ("doc", Whitelisted),
286
287     // FIXME: #14406 these are processed in trans, which happens after the
288     // lint pass
289     ("cold", Whitelisted),
290     ("export_name", Whitelisted),
291     ("inline", Whitelisted),
292     ("link", Whitelisted),
293     ("link_name", Whitelisted),
294     ("link_section", Whitelisted),
295     ("no_builtins", Whitelisted),
296     ("no_mangle", Whitelisted),
297     ("no_debug", Whitelisted),
298     ("omit_gdb_pretty_printer_section", Whitelisted),
299     ("unsafe_no_drop_flag", Gated("unsafe_no_drop_flag",
300                                   "unsafe_no_drop_flag has unstable semantics \
301                                    and may be removed in the future")),
302
303     // used in resolve
304     ("prelude_import", Gated("prelude_import",
305                              "`#[prelude_import]` is for use by rustc only")),
306
307     // FIXME: #14407 these are only looked at on-demand so we can't
308     // guarantee they'll have already been checked
309     ("deprecated", Whitelisted),
310     ("must_use", Whitelisted),
311     ("stable", Whitelisted),
312     ("unstable", Whitelisted),
313
314     ("rustc_paren_sugar", Gated("unboxed_closures",
315                                 "unboxed_closures are still evolving")),
316     ("rustc_reflect_like", Gated("reflect",
317                                  "defining reflective traits is still evolving")),
318
319     // Crate level attributes
320     ("crate_name", CrateLevel),
321     ("crate_type", CrateLevel),
322     ("crate_id", CrateLevel),
323     ("feature", CrateLevel),
324     ("no_start", CrateLevel),
325     ("no_main", CrateLevel),
326     ("no_builtins", CrateLevel),
327     ("recursion_limit", CrateLevel),
328 ];
329
330 #[derive(PartialEq, Copy, Clone, Debug)]
331 pub enum AttributeType {
332     /// Normal, builtin attribute that is consumed
333     /// by the compiler before the unused_attribute check
334     Normal,
335
336     /// Builtin attribute that may not be consumed by the compiler
337     /// before the unused_attribute check. These attributes
338     /// will be ignored by the unused_attribute lint
339     Whitelisted,
340
341     /// Is gated by a given feature gate and reason
342     /// These get whitelisted too
343     Gated(&'static str, &'static str),
344
345     /// Builtin attribute that is only allowed at the crate level
346     CrateLevel,
347 }
348
349 /// A set of features to be used by later passes.
350 pub struct Features {
351     pub unboxed_closures: bool,
352     pub rustc_diagnostic_macros: bool,
353     pub visible_private_types: bool,
354     pub allow_quote: bool,
355     pub allow_asm: bool,
356     pub allow_log_syntax: bool,
357     pub allow_concat_idents: bool,
358     pub allow_trace_macros: bool,
359     pub allow_internal_unstable: bool,
360     pub allow_custom_derive: bool,
361     pub allow_placement_in: bool,
362     pub allow_box: bool,
363     pub allow_pushpop_unsafe: bool,
364     pub simd_ffi: bool,
365     pub simd_basics: bool,
366     pub unmarked_api: bool,
367     pub negate_unsigned: bool,
368     /// spans of #![feature] attrs for stable language features. for error reporting
369     pub declared_stable_lang_features: Vec<Span>,
370     /// #![feature] attrs for non-language (library) features
371     pub declared_lib_features: Vec<(InternedString, Span)>,
372     pub const_fn: bool,
373     pub static_recursion: bool,
374     pub default_type_parameter_fallback: bool,
375     pub type_macros: bool,
376 }
377
378 impl Features {
379     pub fn new() -> Features {
380         Features {
381             unboxed_closures: false,
382             rustc_diagnostic_macros: false,
383             visible_private_types: false,
384             allow_quote: false,
385             allow_asm: false,
386             allow_log_syntax: false,
387             allow_concat_idents: false,
388             allow_trace_macros: false,
389             allow_internal_unstable: false,
390             allow_custom_derive: false,
391             allow_placement_in: false,
392             allow_box: false,
393             allow_pushpop_unsafe: false,
394             simd_ffi: false,
395             simd_basics: false,
396             unmarked_api: false,
397             negate_unsigned: false,
398             declared_stable_lang_features: Vec::new(),
399             declared_lib_features: Vec::new(),
400             const_fn: false,
401             static_recursion: false,
402             default_type_parameter_fallback: false,
403             type_macros: false,
404         }
405     }
406 }
407
408 const EXPLAIN_BOX_SYNTAX: &'static str =
409     "box expression syntax is experimental; you can call `Box::new` instead.";
410
411 const EXPLAIN_PLACEMENT_IN: &'static str =
412     "placement-in expression syntax is experimental and subject to change.";
413
414 const EXPLAIN_PUSHPOP_UNSAFE: &'static str =
415     "push/pop_unsafe macros are experimental and subject to change.";
416
417 pub fn check_for_box_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) {
418     if let Some(&Features { allow_box: true, .. }) = f {
419         return;
420     }
421     emit_feature_err(diag, "box_syntax", span, EXPLAIN_BOX_SYNTAX);
422 }
423
424 pub fn check_for_placement_in(f: Option<&Features>, diag: &SpanHandler, span: Span) {
425     if let Some(&Features { allow_placement_in: true, .. }) = f {
426         return;
427     }
428     emit_feature_err(diag, "placement_in_syntax", span, EXPLAIN_PLACEMENT_IN);
429 }
430
431 pub fn check_for_pushpop_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) {
432     if let Some(&Features { allow_pushpop_unsafe: true, .. }) = f {
433         return;
434     }
435     emit_feature_err(diag, "pushpop_unsafe", span, EXPLAIN_PUSHPOP_UNSAFE);
436 }
437
438 struct Context<'a> {
439     features: Vec<&'static str>,
440     span_handler: &'a SpanHandler,
441     cm: &'a CodeMap,
442     plugin_attributes: &'a [(String, AttributeType)],
443 }
444
445 impl<'a> Context<'a> {
446     fn enable_feature(&mut self, feature: &'static str) {
447         debug!("enabling feature: {}", feature);
448         self.features.push(feature);
449     }
450
451     fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
452         let has_feature = self.has_feature(feature);
453         debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", feature, span, has_feature);
454         if !has_feature {
455             emit_feature_err(self.span_handler, feature, span, explain);
456         }
457     }
458     fn has_feature(&self, feature: &str) -> bool {
459         self.features.iter().any(|&n| n == feature)
460     }
461
462     fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) {
463         debug!("check_attribute(attr = {:?})", attr);
464         let name = &*attr.name();
465         for &(n, ty) in KNOWN_ATTRIBUTES {
466             if n == name {
467                 if let Gated(gate, desc) = ty {
468                     self.gate_feature(gate, attr.span, desc);
469                 }
470                 debug!("check_attribute: {:?} is known, {:?}", name, ty);
471                 return;
472             }
473         }
474         for &(ref n, ref ty) in self.plugin_attributes {
475             if &*n == name {
476                 // Plugins can't gate attributes, so we don't check for it
477                 // unlike the code above; we only use this loop to
478                 // short-circuit to avoid the checks below
479                 debug!("check_attribute: {:?} is registered by a plugin, {:?}", name, ty);
480                 return;
481             }
482         }
483         if name.starts_with("rustc_") {
484             self.gate_feature("rustc_attrs", attr.span,
485                               "unless otherwise specified, attributes \
486                                with the prefix `rustc_` \
487                                are reserved for internal compiler diagnostics");
488         } else if name.starts_with("derive_") {
489             self.gate_feature("custom_derive", attr.span,
490                               "attributes of the form `#[derive_*]` are reserved \
491                                for the compiler");
492         } else {
493             // Only run the custom attribute lint during regular
494             // feature gate checking. Macro gating runs
495             // before the plugin attributes are registered
496             // so we skip this then
497             if !is_macro {
498                 self.gate_feature("custom_attribute", attr.span,
499                            &format!("The attribute `{}` is currently \
500                                     unknown to the compiler and \
501                                     may have meaning \
502                                     added to it in the future",
503                                     name));
504             }
505         }
506     }
507 }
508
509 pub fn emit_feature_err(diag: &SpanHandler, feature: &str, span: Span, explain: &str) {
510     diag.span_err(span, explain);
511
512     // #23973: do not suggest `#![feature(...)]` if we are in beta/stable
513     if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some() { return; }
514     diag.fileline_help(span, &format!("add #![feature({})] to the \
515                                    crate attributes to enable",
516                                   feature));
517 }
518
519 pub const EXPLAIN_ASM: &'static str =
520     "inline assembly is not stable enough for use and is subject to change";
521
522 pub const EXPLAIN_LOG_SYNTAX: &'static str =
523     "`log_syntax!` is not stable enough for use and is subject to change";
524
525 pub const EXPLAIN_CONCAT_IDENTS: &'static str =
526     "`concat_idents` is not stable enough for use and is subject to change";
527
528 pub const EXPLAIN_TRACE_MACROS: &'static str =
529     "`trace_macros` is not stable enough for use and is subject to change";
530 pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &'static str =
531     "allow_internal_unstable side-steps feature gating and stability checks";
532
533 pub const EXPLAIN_CUSTOM_DERIVE: &'static str =
534     "`#[derive]` for custom traits is not stable enough for use and is subject to change";
535
536 struct MacroVisitor<'a> {
537     context: &'a Context<'a>
538 }
539
540 impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
541     fn visit_mac(&mut self, mac: &ast::Mac) {
542         let ast::MacInvocTT(ref path, _, _) = mac.node;
543         let id = path.segments.last().unwrap().identifier;
544
545         // Issue 22234: If you add a new case here, make sure to also
546         // add code to catch the macro during or after expansion.
547         //
548         // We still keep this MacroVisitor (rather than *solely*
549         // relying on catching cases during or after expansion) to
550         // catch uses of these macros within conditionally-compiled
551         // code, e.g. `#[cfg]`-guarded functions.
552
553         if id == token::str_to_ident("asm") {
554             self.context.gate_feature("asm", path.span, EXPLAIN_ASM);
555         }
556
557         else if id == token::str_to_ident("log_syntax") {
558             self.context.gate_feature("log_syntax", path.span, EXPLAIN_LOG_SYNTAX);
559         }
560
561         else if id == token::str_to_ident("trace_macros") {
562             self.context.gate_feature("trace_macros", path.span, EXPLAIN_TRACE_MACROS);
563         }
564
565         else if id == token::str_to_ident("concat_idents") {
566             self.context.gate_feature("concat_idents", path.span, EXPLAIN_CONCAT_IDENTS);
567         }
568     }
569
570     fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
571         self.context.check_attribute(attr, true);
572     }
573
574     fn visit_expr(&mut self, e: &ast::Expr) {
575         // Issue 22181: overloaded-`box` and placement-`in` are
576         // implemented via a desugaring expansion, so their feature
577         // gates go into MacroVisitor since that works pre-expansion.
578         //
579         // Issue 22234: we also check during expansion as well.
580         // But we keep these checks as a pre-expansion check to catch
581         // uses in e.g. conditionalized code.
582
583         if let ast::ExprBox(None, _) = e.node {
584             self.context.gate_feature("box_syntax", e.span, EXPLAIN_BOX_SYNTAX);
585         }
586
587         if let ast::ExprBox(Some(_), _) = e.node {
588             self.context.gate_feature("placement_in_syntax", e.span, EXPLAIN_PLACEMENT_IN);
589         }
590
591         visit::walk_expr(self, e);
592     }
593 }
594
595 struct PostExpansionVisitor<'a> {
596     context: &'a Context<'a>
597 }
598
599 impl<'a> PostExpansionVisitor<'a> {
600     fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
601         if !self.context.cm.span_allows_unstable(span) {
602             self.context.gate_feature(feature, span, explain)
603         }
604     }
605 }
606
607 impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
608     fn visit_attribute(&mut self, attr: &ast::Attribute) {
609         if !self.context.cm.span_allows_unstable(attr.span) {
610             self.context.check_attribute(attr, false);
611         }
612     }
613
614     fn visit_name(&mut self, sp: Span, name: ast::Name) {
615         if !name.as_str().is_ascii() {
616             self.gate_feature("non_ascii_idents", sp,
617                               "non-ascii idents are not fully supported.");
618         }
619     }
620
621     fn visit_item(&mut self, i: &ast::Item) {
622         match i.node {
623             ast::ItemExternCrate(_) => {
624                 if attr::contains_name(&i.attrs[..], "macro_reexport") {
625                     self.gate_feature("macro_reexport", i.span,
626                                       "macros reexports are experimental \
627                                        and possibly buggy");
628                 }
629             }
630
631             ast::ItemForeignMod(ref foreign_module) => {
632                 if attr::contains_name(&i.attrs[..], "link_args") {
633                     self.gate_feature("link_args", i.span,
634                                       "the `link_args` attribute is not portable \
635                                        across platforms, it is recommended to \
636                                        use `#[link(name = \"foo\")]` instead")
637                 }
638                 if foreign_module.abi == Abi::RustIntrinsic {
639                     self.gate_feature("intrinsics",
640                                       i.span,
641                                       "intrinsics are subject to change")
642                 }
643             }
644
645             ast::ItemFn(..) => {
646                 if attr::contains_name(&i.attrs[..], "plugin_registrar") {
647                     self.gate_feature("plugin_registrar", i.span,
648                                       "compiler plugins are experimental and possibly buggy");
649                 }
650                 if attr::contains_name(&i.attrs[..], "start") {
651                     self.gate_feature("start", i.span,
652                                       "a #[start] function is an experimental \
653                                        feature whose signature may change \
654                                        over time");
655                 }
656                 if attr::contains_name(&i.attrs[..], "main") {
657                     self.gate_feature("main", i.span,
658                                       "declaration of a nonstandard #[main] \
659                                        function may change over time, for now \
660                                        a top-level `fn main()` is required");
661                 }
662             }
663
664             ast::ItemStruct(..) => {
665                 if attr::contains_name(&i.attrs[..], "simd") {
666                     self.gate_feature("simd", i.span,
667                                       "SIMD types are experimental and possibly buggy");
668                     self.context.span_handler.span_warn(i.span,
669                                                         "the `#[simd]` attribute is deprecated, \
670                                                          use `#[repr(simd)]` instead");
671                 }
672                 for attr in &i.attrs {
673                     if attr.name() == "repr" {
674                         for item in attr.meta_item_list().unwrap_or(&[]) {
675                             if item.name() == "simd" {
676                                 self.gate_feature("simd_basics", i.span,
677                                                   "SIMD types are experimental and possibly buggy");
678
679                             }
680                         }
681                     }
682                 }
683             }
684
685             ast::ItemDefaultImpl(..) => {
686                 self.gate_feature("optin_builtin_traits",
687                                   i.span,
688                                   "default trait implementations are experimental \
689                                    and possibly buggy");
690             }
691
692             ast::ItemImpl(_, polarity, _, _, _, _) => {
693                 match polarity {
694                     ast::ImplPolarity::Negative => {
695                         self.gate_feature("optin_builtin_traits",
696                                           i.span,
697                                           "negative trait bounds are not yet fully implemented; \
698                                           use marker types for now");
699                     },
700                     _ => {}
701                 }
702             }
703
704             _ => {}
705         }
706
707         visit::walk_item(self, i);
708     }
709
710     fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
711         let links_to_llvm = match attr::first_attr_value_str_by_name(&i.attrs,
712                                                                      "link_name") {
713             Some(val) => val.starts_with("llvm."),
714             _ => false
715         };
716         if links_to_llvm {
717             self.gate_feature("link_llvm_intrinsics", i.span,
718                               "linking to LLVM intrinsics is experimental");
719         }
720
721         visit::walk_foreign_item(self, i)
722     }
723
724     fn visit_expr(&mut self, e: &ast::Expr) {
725         match e.node {
726             ast::ExprBox(..) | ast::ExprUnary(ast::UnOp::UnUniq, _) => {
727                 self.gate_feature("box_syntax",
728                                   e.span,
729                                   "box expression syntax is experimental; \
730                                    you can call `Box::new` instead.");
731             }
732             _ => {}
733         }
734         visit::walk_expr(self, e);
735     }
736
737     fn visit_pat(&mut self, pattern: &ast::Pat) {
738         match pattern.node {
739             ast::PatVec(_, Some(_), ref last) if !last.is_empty() => {
740                 self.gate_feature("advanced_slice_patterns",
741                                   pattern.span,
742                                   "multiple-element slice matches anywhere \
743                                    but at the end of a slice (e.g. \
744                                    `[0, ..xs, 0]`) are experimental")
745             }
746             ast::PatVec(..) => {
747                 self.gate_feature("slice_patterns",
748                                   pattern.span,
749                                   "slice pattern syntax is experimental");
750             }
751             ast::PatBox(..) => {
752                 self.gate_feature("box_patterns",
753                                   pattern.span,
754                                   "box pattern syntax is experimental");
755             }
756             _ => {}
757         }
758         visit::walk_pat(self, pattern)
759     }
760
761     fn visit_fn(&mut self,
762                 fn_kind: visit::FnKind<'v>,
763                 fn_decl: &'v ast::FnDecl,
764                 block: &'v ast::Block,
765                 span: Span,
766                 _node_id: NodeId) {
767         // check for const fn declarations
768         match fn_kind {
769             visit::FkItemFn(_, _, _, ast::Constness::Const, _, _) => {
770                 self.gate_feature("const_fn", span, "const fn is unstable");
771             }
772             _ => {
773                 // stability of const fn methods are covered in
774                 // visit_trait_item and visit_impl_item below; this is
775                 // because default methods don't pass through this
776                 // point.
777             }
778         }
779
780         match fn_kind {
781             visit::FkItemFn(_, _, _, _, abi, _) if abi == Abi::RustIntrinsic => {
782                 self.gate_feature("intrinsics",
783                                   span,
784                                   "intrinsics are subject to change")
785             }
786             visit::FkItemFn(_, _, _, _, abi, _) |
787             visit::FkMethod(_, &ast::MethodSig { abi, .. }, _) if abi == Abi::RustCall => {
788                 self.gate_feature("unboxed_closures",
789                                   span,
790                                   "rust-call ABI is subject to change")
791             }
792             _ => {}
793         }
794         visit::walk_fn(self, fn_kind, fn_decl, block, span);
795     }
796
797     fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) {
798         match ti.node {
799             ast::ConstTraitItem(..) => {
800                 self.gate_feature("associated_consts",
801                                   ti.span,
802                                   "associated constants are experimental")
803             }
804             ast::MethodTraitItem(ref sig, _) => {
805                 if sig.constness == ast::Constness::Const {
806                     self.gate_feature("const_fn", ti.span, "const fn is unstable");
807                 }
808             }
809             ast::TypeTraitItem(_, Some(_)) => {
810                 self.gate_feature("associated_type_defaults", ti.span,
811                                   "associated type defaults are unstable");
812             }
813             _ => {}
814         }
815         visit::walk_trait_item(self, ti);
816     }
817
818     fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
819         match ii.node {
820             ast::ConstImplItem(..) => {
821                 self.gate_feature("associated_consts",
822                                   ii.span,
823                                   "associated constants are experimental")
824             }
825             ast::MethodImplItem(ref sig, _) => {
826                 if sig.constness == ast::Constness::Const {
827                     self.gate_feature("const_fn", ii.span, "const fn is unstable");
828                 }
829             }
830             _ => {}
831         }
832         visit::walk_impl_item(self, ii);
833     }
834 }
835
836 fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
837                         krate: &ast::Crate,
838                         plugin_attributes: &[(String, AttributeType)],
839                         check: F)
840                        -> Features
841     where F: FnOnce(&mut Context, &ast::Crate)
842 {
843     let mut cx = Context {
844         features: Vec::new(),
845         span_handler: span_handler,
846         cm: cm,
847         plugin_attributes: plugin_attributes,
848     };
849
850     let mut accepted_features = Vec::new();
851     let mut unknown_features = Vec::new();
852
853     for attr in &krate.attrs {
854         if !attr.check_name("feature") {
855             continue
856         }
857
858         match attr.meta_item_list() {
859             None => {
860                 span_handler.span_err(attr.span, "malformed feature attribute, \
861                                                   expected #![feature(...)]");
862             }
863             Some(list) => {
864                 for mi in list {
865                     let name = match mi.node {
866                         ast::MetaWord(ref word) => (*word).clone(),
867                         _ => {
868                             span_handler.span_err(mi.span,
869                                                   "malformed feature, expected just \
870                                                    one word");
871                             continue
872                         }
873                     };
874                     match KNOWN_FEATURES.iter()
875                                         .find(|& &(n, _, _)| name == n) {
876                         Some(&(name, _, Active)) => {
877                             cx.enable_feature(name);
878                         }
879                         Some(&(_, _, Removed)) => {
880                             span_handler.span_err(mi.span, "feature has been removed");
881                         }
882                         Some(&(_, _, Accepted)) => {
883                             accepted_features.push(mi.span);
884                         }
885                         None => {
886                             unknown_features.push((name, mi.span));
887                         }
888                     }
889                 }
890             }
891         }
892     }
893
894     check(&mut cx, krate);
895
896     // FIXME (pnkfelix): Before adding the 99th entry below, change it
897     // to a single-pass (instead of N calls to `.has_feature`).
898
899     Features {
900         unboxed_closures: cx.has_feature("unboxed_closures"),
901         rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
902         visible_private_types: cx.has_feature("visible_private_types"),
903         allow_quote: cx.has_feature("quote"),
904         allow_asm: cx.has_feature("asm"),
905         allow_log_syntax: cx.has_feature("log_syntax"),
906         allow_concat_idents: cx.has_feature("concat_idents"),
907         allow_trace_macros: cx.has_feature("trace_macros"),
908         allow_internal_unstable: cx.has_feature("allow_internal_unstable"),
909         allow_custom_derive: cx.has_feature("custom_derive"),
910         allow_placement_in: cx.has_feature("placement_in_syntax"),
911         allow_box: cx.has_feature("box_syntax"),
912         allow_pushpop_unsafe: cx.has_feature("pushpop_unsafe"),
913         simd_ffi: cx.has_feature("simd_ffi"),
914         simd_basics: cx.has_feature("simd_basics"),
915         unmarked_api: cx.has_feature("unmarked_api"),
916         negate_unsigned: cx.has_feature("negate_unsigned"),
917         declared_stable_lang_features: accepted_features,
918         declared_lib_features: unknown_features,
919         const_fn: cx.has_feature("const_fn"),
920         static_recursion: cx.has_feature("static_recursion"),
921         default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"),
922         type_macros: cx.has_feature("type_macros"),
923     }
924 }
925
926 pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
927 -> Features {
928     check_crate_inner(cm, span_handler, krate, &[] as &'static [_],
929                       |ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
930 }
931
932 pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate,
933                    plugin_attributes: &[(String, AttributeType)],
934                    unstable: UnstableFeatures) -> Features
935 {
936     maybe_stage_features(span_handler, krate, unstable);
937
938     check_crate_inner(cm, span_handler, krate, plugin_attributes,
939                       |ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx },
940                                                      krate))
941 }
942
943 #[derive(Clone, Copy)]
944 pub enum UnstableFeatures {
945     /// Hard errors for unstable features are active, as on
946     /// beta/stable channels.
947     Disallow,
948     /// Allow features to me activated, as on nightly.
949     Allow,
950     /// Errors are bypassed for bootstrapping. This is required any time
951     /// during the build that feature-related lints are set to warn or above
952     /// because the build turns on warnings-as-errors and uses lots of unstable
953     /// features. As a result, this this is always required for building Rust
954     /// itself.
955     Cheat
956 }
957
958 fn maybe_stage_features(span_handler: &SpanHandler, krate: &ast::Crate,
959                         unstable: UnstableFeatures) {
960     let allow_features = match unstable {
961         UnstableFeatures::Allow => true,
962         UnstableFeatures::Disallow => false,
963         UnstableFeatures::Cheat => true
964     };
965     if !allow_features {
966         for attr in &krate.attrs {
967             if attr.check_name("feature") {
968                 let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)");
969                 let ref msg = format!("#[feature] may not be used on the {} release channel",
970                                       release_channel);
971                 span_handler.span_err(attr.span, msg);
972             }
973         }
974     }
975 }