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.
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.
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
18 //! Features are enabled in programs via the crate-level attributes of
19 //! `#![feature(...)]` with a comma-separated list of features.
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
26 use self::AttributeType::*;
27 use self::AttributeGate::*;
33 use attr::AttrMetaMethods;
34 use codemap::{CodeMap, Span};
35 use diagnostic::SpanHandler;
37 use visit::{FnKind, Visitor};
38 use parse::token::{self, InternedString};
40 use std::ascii::AsciiExt;
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
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),
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),
73 ("quad_precision_float", "1.0.0", None, Removed),
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),
93 ("if_let", "1.0.0", None, Accepted),
94 ("while_let", "1.0.0", None, Accepted),
96 ("plugin", "1.0.0", None, Active),
97 ("start", "1.0.0", None, Active),
98 ("main", "1.0.0", None, Active),
100 ("fundamental", "1.0.0", None, Active),
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),
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),
109 // OIBIT specific features
110 ("optin_builtin_traits", "1.0.0", None, Active),
112 // macro reexport needs more discussion and stabilization
113 ("macro_reexport", "1.0.0", None, Active),
115 // These are used to test this portion of the compiler, they don't actually
117 ("test_accepted_feature", "1.0.0", None, Accepted),
118 ("test_removed_feature", "1.0.0", None, Removed),
120 // Allows use of #[staged_api]
121 ("staged_api", "1.0.0", None, Active),
123 // Allows using items which are missing stability attributes
124 ("unmarked_api", "1.0.0", None, Active),
126 // Allows using #![no_std]
127 ("no_std", "1.0.0", None, Active),
129 // Allows using #![no_core]
130 ("no_core", "1.3.0", None, Active),
132 // Allows using `box` in patterns; RFC 469
133 ("box_patterns", "1.0.0", None, Active),
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),
139 // Allows the use of custom attributes; RFC 572
140 ("custom_attribute", "1.0.0", None, Active),
142 // Allows the use of #[derive(Anything)] as sugar for
143 // #[derive_Anything].
144 ("custom_derive", "1.0.0", None, Active),
146 // Allows the use of rustc_* attributes; RFC 572
147 ("rustc_attrs", "1.0.0", None, Active),
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),
155 // #23121. Array patterns have some hazards yet.
156 ("slice_patterns", "1.0.0", None, Active),
158 // Allows use of unary negate on unsigned integers, e.g. -e for e: u8
159 ("negate_unsigned", "1.0.0", None, Active),
161 // Allows the definition of associated constants in `trait` or `impl`
163 ("associated_consts", "1.0.0", None, Active),
165 // Allows the definition of `const fn` functions.
166 ("const_fn", "1.2.0", None, Active),
168 // Allows using #[prelude_import] on glob `use` items.
169 ("prelude_import", "1.2.0", None, Active),
171 // Allows the definition recursive static items.
172 ("static_recursion", "1.3.0", None, Active),
174 // Allows default type parameters to influence type inference.
175 ("default_type_parameter_fallback", "1.3.0", None, Active),
177 // Allows associated type defaults
178 ("associated_type_defaults", "1.2.0", None, Active),
179 // Allows macros to appear in the type position.
181 ("type_macros", "1.3.0", Some(27336), Active),
183 // allow `repr(simd)`, and importing the various simd intrinsics
184 ("repr_simd", "1.4.0", Some(27731), Active),
186 // Allows cfg(target_feature = "...").
187 ("cfg_target_feature", "1.4.0", None, Active),
189 // allow `extern "platform-intrinsic" { ... }`
190 ("platform_intrinsics", "1.4.0", Some(27731), Active),
193 ("unwind_attributes", "1.4.0", None, Active),
195 // allow empty structs/enum variants with braces
196 ("braced_empty_structs", "1.5.0", None, Active),
198 // allow overloading augmented assignment operations like `a += b`
199 ("augmented_assignments", "1.5.0", None, Active),
201 // allow `#[no_debug]`
202 ("no_debug", "1.5.0", None, Active),
204 // allow `#[omit_gdb_pretty_printer_section]`
205 ("omit_gdb_pretty_printer_section", "1.5.0", None, Active),
207 // Allows cfg(target_vendor = "...").
208 ("cfg_target_vendor", "1.5.0", None, Active),
210 // (changing above list without updating src/doc/reference.md makes @cmr sad)
213 /// Represents an active feature that is currently being implemented or
214 /// currently being considered for addition/removal.
217 /// Represents a feature which has since been removed (it was once Active)
220 /// This language feature has since been Accepted (it was once Active)
224 // Attributes that have a special meaning to rustc or rustdoc
225 pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGate)] = &[
228 ("warn", Normal, Ungated),
229 ("allow", Normal, Ungated),
230 ("forbid", Normal, Ungated),
231 ("deny", Normal, Ungated),
233 ("macro_reexport", Normal, Ungated),
234 ("macro_use", Normal, Ungated),
235 ("macro_export", Normal, Ungated),
236 ("plugin_registrar", Normal, Ungated),
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),
259 // Not used any more, but we can't feature gate it
260 ("no_stack_check", Normal, Ungated),
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 \
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 \
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")),
308 ("allow_internal_unstable", Normal, Gated("allow_internal_unstable",
309 EXPLAIN_ALLOW_INTERNAL_UNSTABLE)),
311 ("fundamental", Whitelisted, Gated("fundamental",
312 "the `#[fundamental]` attribute \
313 is an experimental feature")),
315 ("linked_from", Normal, Gated("linked_from",
316 "the `#[linked_from]` attribute \
317 is an experimental feature")),
319 // FIXME: #14408 whitelist docs since rustdoc looks at them
320 ("doc", Whitelisted, Ungated),
322 // FIXME: #14406 these are processed in trans, which happens after the
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 \
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")),
345 ("prelude_import", Whitelisted, Gated("prelude_import",
346 "`#[prelude_import]` is for use by rustc only")),
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),
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")),
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),
371 macro_rules! cfg_fn {
372 (|$x: ident| $e: expr) => {{
373 fn f($x: &Features) -> bool {
376 f as fn(&Features) -> bool
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)),
386 #[derive(Debug, Eq, PartialEq)]
387 pub struct GatedCfg {
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))
399 impl PartialOrd for GatedCfg {
400 fn partial_cmp(&self, other: &GatedCfg) -> Option<cmp::Ordering> {
401 Some(self.cmp(other))
406 pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> {
407 let name = cfg.name();
409 .position(|info| info.0 == name)
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);
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
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
438 /// Builtin attribute that is only allowed at the crate level
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),
447 /// Ungated attribute, can be used on all release channels
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,
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,
465 pub allow_pushpop_unsafe: 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)>,
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,
483 pub fn new() -> Features {
485 unboxed_closures: false,
486 rustc_diagnostic_macros: false,
487 visible_private_types: 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,
497 allow_pushpop_unsafe: false,
500 negate_unsigned: false,
501 declared_stable_lang_features: Vec::new(),
502 declared_lib_features: Vec::new(),
504 static_recursion: false,
505 default_type_parameter_fallback: false,
507 cfg_target_feature: false,
508 cfg_target_vendor: false,
509 augmented_assignments: false,
514 const EXPLAIN_BOX_SYNTAX: &'static str =
515 "box expression syntax is experimental; you can call `Box::new` instead.";
517 const EXPLAIN_PLACEMENT_IN: &'static str =
518 "placement-in expression syntax is experimental and subject to change.";
520 const EXPLAIN_PUSHPOP_UNSAFE: &'static str =
521 "push/pop_unsafe macros are experimental and subject to change.";
523 pub fn check_for_box_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) {
524 if let Some(&Features { allow_box: true, .. }) = f {
527 emit_feature_err(diag, "box_syntax", span, GateIssue::Language, EXPLAIN_BOX_SYNTAX);
530 pub fn check_for_placement_in(f: Option<&Features>, diag: &SpanHandler, span: Span) {
531 if let Some(&Features { allow_placement_in: true, .. }) = f {
534 emit_feature_err(diag, "placement_in_syntax", span, GateIssue::Language, EXPLAIN_PLACEMENT_IN);
537 pub fn check_for_pushpop_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) {
538 if let Some(&Features { allow_pushpop_unsafe: true, .. }) = f {
541 emit_feature_err(diag, "pushpop_unsafe", span, GateIssue::Language, EXPLAIN_PUSHPOP_UNSAFE);
545 features: Vec<&'static str>,
546 span_handler: &'a SpanHandler,
548 plugin_attributes: &'a [(String, AttributeType)],
551 impl<'a> Context<'a> {
552 fn enable_feature(&mut self, feature: &'static str) {
553 debug!("enabling feature: {}", feature);
554 self.features.push(feature);
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);
561 emit_feature_err(self.span_handler, feature, span, GateIssue::Language, explain);
564 fn has_feature(&self, feature: &str) -> bool {
565 self.features.iter().any(|&n| n == feature)
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 {
573 if let Gated(gate, desc) = gateage {
574 self.gate_feature(gate, attr.span, desc);
576 debug!("check_attribute: {:?} is known, {:?}, {:?}", name, ty, gateage);
580 for &(ref n, ref ty) in self.plugin_attributes {
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);
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 \
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
604 self.gate_feature("custom_attribute", attr.span,
605 &format!("The attribute `{}` is currently \
606 unknown to the compiler and \
608 added to it in the future",
615 fn find_lang_feature_issue(feature: &str) -> Option<u32> {
616 let info = KNOWN_FEATURES.iter()
617 .find(|t| t.0 == feature)
620 if let Active = info.3 {
621 // FIXME (#28244): enforce that active features have issue numbers
622 // assert!(issue.is_some())
632 pub fn emit_feature_err(diag: &SpanHandler, feature: &str, span: Span, issue: GateIssue,
634 let issue = match issue {
635 GateIssue::Language => find_lang_feature_issue(feature),
636 GateIssue::Library(lib) => lib,
639 if let Some(n) = issue {
640 diag.span_err(span, &format!("{} (see issue #{})", explain, n));
642 diag.span_err(span, explain);
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",
652 pub const EXPLAIN_ASM: &'static str =
653 "inline assembly is not stable enough for use and is subject to change";
655 pub const EXPLAIN_LOG_SYNTAX: &'static str =
656 "`log_syntax!` is not stable enough for use and is subject to change";
658 pub const EXPLAIN_CONCAT_IDENTS: &'static str =
659 "`concat_idents` is not stable enough for use and is subject to change";
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";
666 pub const EXPLAIN_CUSTOM_DERIVE: &'static str =
667 "`#[derive]` for custom traits is not stable enough for use and is subject to change";
669 struct MacroVisitor<'a> {
670 context: &'a Context<'a>
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;
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.
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.
686 if id == token::str_to_ident("asm") {
687 self.context.gate_feature("asm", path.span, EXPLAIN_ASM);
690 else if id == token::str_to_ident("log_syntax") {
691 self.context.gate_feature("log_syntax", path.span, EXPLAIN_LOG_SYNTAX);
694 else if id == token::str_to_ident("trace_macros") {
695 self.context.gate_feature("trace_macros", path.span, EXPLAIN_TRACE_MACROS);
698 else if id == token::str_to_ident("concat_idents") {
699 self.context.gate_feature("concat_idents", path.span, EXPLAIN_CONCAT_IDENTS);
703 fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
704 self.context.check_attribute(attr, true);
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.
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.
716 if let ast::ExprBox(_) = e.node {
717 self.context.gate_feature("box_syntax", e.span, EXPLAIN_BOX_SYNTAX);
720 if let ast::ExprInPlace(..) = e.node {
721 self.context.gate_feature("placement_in_syntax", e.span, EXPLAIN_PLACEMENT_IN);
724 visit::walk_expr(self, e);
728 struct PostExpansionVisitor<'a> {
729 context: &'a Context<'a>
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)
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);
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.");
754 fn visit_item(&mut self, i: &ast::Item) {
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");
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")
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"))
779 if let Some((feature, msg)) = maybe_feature {
780 self.gate_feature(feature, i.span, msg)
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");
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 \
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");
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");
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");
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");
828 ast::ItemDefaultImpl(..) => {
829 self.gate_feature("optin_builtin_traits",
831 "default trait implementations are experimental \
832 and possibly buggy");
835 ast::ItemImpl(_, polarity, _, _, _, _) => {
837 ast::ImplPolarity::Negative => {
838 self.gate_feature("optin_builtin_traits",
840 "negative trait bounds are not yet fully implemented; \
841 use marker types for now");
850 visit::walk_item(self, i);
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,
856 Some(val) => val.starts_with("llvm."),
860 self.gate_feature("link_llvm_intrinsics", i.span,
861 "linking to LLVM intrinsics is experimental");
864 visit::walk_foreign_item(self, i)
867 fn visit_expr(&mut self, e: &ast::Expr) {
870 self.gate_feature("box_syntax",
872 "box expression syntax is experimental; \
873 you can call `Box::new` instead.");
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");
883 visit::walk_expr(self, e);
886 fn visit_pat(&mut self, pattern: &ast::Pat) {
888 ast::PatVec(_, Some(_), ref last) if !last.is_empty() => {
889 self.gate_feature("advanced_slice_patterns",
891 "multiple-element slice matches anywhere \
892 but at the end of a slice (e.g. \
893 `[0, ..xs, 0]`) are experimental")
896 self.gate_feature("slice_patterns",
898 "slice pattern syntax is experimental");
901 self.gate_feature("box_patterns",
903 "box pattern syntax is experimental");
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");
913 visit::walk_pat(self, pattern)
916 fn visit_fn(&mut self,
918 fn_decl: &'v ast::FnDecl,
919 block: &'v ast::Block,
922 // check for const fn declarations
924 FnKind::ItemFn(_, _, _, ast::Constness::Const, _, _) => {
925 self.gate_feature("const_fn", span, "const fn is unstable");
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
936 FnKind::ItemFn(_, _, _, _, abi, _) if abi == Abi::RustIntrinsic => {
937 self.gate_feature("intrinsics",
939 "intrinsics are subject to change")
941 FnKind::ItemFn(_, _, _, _, abi, _) |
942 FnKind::Method(_, &ast::MethodSig { abi, .. }, _) if abi == Abi::RustCall => {
943 self.gate_feature("unboxed_closures",
945 "rust-call ABI is subject to change")
949 visit::walk_fn(self, fn_kind, fn_decl, block, span);
952 fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) {
954 ast::ConstTraitItem(..) => {
955 self.gate_feature("associated_consts",
957 "associated constants are experimental")
959 ast::MethodTraitItem(ref sig, _) => {
960 if sig.constness == ast::Constness::Const {
961 self.gate_feature("const_fn", ti.span, "const fn is unstable");
964 ast::TypeTraitItem(_, Some(_)) => {
965 self.gate_feature("associated_type_defaults", ti.span,
966 "associated type defaults are unstable");
970 visit::walk_trait_item(self, ti);
973 fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
975 ast::ConstImplItem(..) => {
976 self.gate_feature("associated_consts",
978 "associated constants are experimental")
980 ast::MethodImplItem(ref sig, _) => {
981 if sig.constness == ast::Constness::Const {
982 self.gate_feature("const_fn", ii.span, "const fn is unstable");
987 visit::walk_impl_item(self, ii);
991 fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
993 plugin_attributes: &[(String, AttributeType)],
996 where F: FnOnce(&mut Context, &ast::Crate)
998 let mut cx = Context {
999 features: Vec::new(),
1000 span_handler: span_handler,
1002 plugin_attributes: plugin_attributes,
1005 let mut accepted_features = Vec::new();
1006 let mut unknown_features = Vec::new();
1008 for attr in &krate.attrs {
1009 if !attr.check_name("feature") {
1013 match attr.meta_item_list() {
1015 span_handler.span_err(attr.span, "malformed feature attribute, \
1016 expected #![feature(...)]");
1020 let name = match mi.node {
1021 ast::MetaWord(ref word) => (*word).clone(),
1023 span_handler.span_err(mi.span,
1024 "malformed feature, expected just \
1029 match KNOWN_FEATURES.iter()
1030 .find(|& &(n, _, _, _)| name == n) {
1031 Some(&(name, _, _, Active)) => {
1032 cx.enable_feature(name);
1034 Some(&(_, _, _, Removed)) => {
1035 span_handler.span_err(mi.span, "feature has been removed");
1037 Some(&(_, _, _, Accepted)) => {
1038 accepted_features.push(mi.span);
1041 unknown_features.push((name, mi.span));
1049 check(&mut cx, krate);
1051 // FIXME (pnkfelix): Before adding the 99th entry below, change it
1052 // to a single-pass (instead of N calls to `.has_feature`).
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"),
1083 pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
1085 check_crate_inner(cm, span_handler, krate, &[] as &'static [_],
1086 |ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
1089 pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate,
1090 plugin_attributes: &[(String, AttributeType)],
1091 unstable: UnstableFeatures) -> Features
1093 maybe_stage_features(span_handler, krate, unstable);
1095 check_crate_inner(cm, span_handler, krate, plugin_attributes,
1096 |ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx },
1100 #[derive(Clone, Copy)]
1101 pub enum UnstableFeatures {
1102 /// Hard errors for unstable features are active, as on
1103 /// beta/stable channels.
1105 /// Allow features to me activated, as on nightly.
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
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
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",
1128 span_handler.span_err(attr.span, msg);