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