feature_gate.rs 26.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Feature gating
//!
//! This modules implements the gating necessary for preventing certain compiler
//! features from being used by default. This module will crawl a pre-expanded
//! AST to ensure that there are no features which are used that are not
//! enabled.
//!
//! Features are enabled in programs via the crate-level attributes of
19
//! `#![feature(...)]` with a comma-separated list of features.
B
Brian Anderson 已提交
20 21 22 23
//!
//! For the purpose of future feature-tracking, once code for detection of feature
//! gate usage is added, *do not remove it again* even once the feature
//! becomes stable.
G
GuillaumeGomez 已提交
24

S
Steven Fackler 已提交
25
use self::Status::*;
26
use self::AttributeType::*;
27

N
Nick Cameron 已提交
28 29 30 31 32
use abi::RustIntrinsic;
use ast::NodeId;
use ast;
use attr;
use attr::AttrMetaMethods;
C
Corey Richardson 已提交
33
use codemap::{CodeMap, Span};
N
Nick Cameron 已提交
34 35 36
use diagnostic::SpanHandler;
use visit;
use visit::Visitor;
37
use parse::token::{self, InternedString};
38

39 40
use std::ascii::AsciiExt;

B
Brian Anderson 已提交
41 42 43 44 45 46 47
// If you change this list without updating src/doc/reference.md, @cmr will be sad
// Don't ever remove anything from this list; set them to 'Removed'.
// The version numbers here correspond to the version in which the current status
// was set. This is most important for knowing when a particular feature became
// stable (active).
// NB: The featureck.py script parses this information directly out of the source
// so take care when modifying it.
48
const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
B
Brian Anderson 已提交
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
    ("globs", "1.0.0", Accepted),
    ("macro_rules", "1.0.0", Accepted),
    ("struct_variant", "1.0.0", Accepted),
    ("asm", "1.0.0", Active),
    ("managed_boxes", "1.0.0", Removed),
    ("non_ascii_idents", "1.0.0", Active),
    ("thread_local", "1.0.0", Active),
    ("link_args", "1.0.0", Active),
    ("plugin_registrar", "1.0.0", Active),
    ("log_syntax", "1.0.0", Active),
    ("trace_macros", "1.0.0", Active),
    ("concat_idents", "1.0.0", Active),
    ("intrinsics", "1.0.0", Active),
    ("lang_items", "1.0.0", Active),

    ("simd", "1.0.0", Active),
    ("default_type_params", "1.0.0", Accepted),
    ("quote", "1.0.0", Active),
    ("link_llvm_intrinsics", "1.0.0", Active),
    ("linkage", "1.0.0", Active),
    ("struct_inherit", "1.0.0", Removed),

    ("quad_precision_float", "1.0.0", Removed),

    ("rustc_diagnostic_macros", "1.0.0", Active),
    ("unboxed_closures", "1.0.0", Active),
75
    ("reflect", "1.0.0", Active),
76
    ("import_shadowing", "1.0.0", Removed),
B
Brian Anderson 已提交
77 78 79 80
    ("advanced_slice_patterns", "1.0.0", Active),
    ("tuple_indexing", "1.0.0", Accepted),
    ("associated_types", "1.0.0", Accepted),
    ("visible_private_types", "1.0.0", Active),
81
    ("slicing_syntax", "1.0.0", Accepted),
B
Brian Anderson 已提交
82 83 84
    ("box_syntax", "1.0.0", Active),
    ("on_unimplemented", "1.0.0", Active),
    ("simd_ffi", "1.0.0", Active),
85
    ("allocator", "1.0.0", Active),
B
Brian Anderson 已提交
86 87 88 89 90 91 92

    ("if_let", "1.0.0", Accepted),
    ("while_let", "1.0.0", Accepted),

    ("plugin", "1.0.0", Active),
    ("start", "1.0.0", Active),
    ("main", "1.0.0", Active),
93

94
    // Deprecate after snapshot
95
    // SNAP 5520801
96 97
    ("unsafe_destructor", "1.0.0", Active),

98 99
    // A temporary feature gate used to enable parser extensions needed
    // to bootstrap fix for #5723.
B
Brian Anderson 已提交
100
    ("issue_5723_bootstrap", "1.0.0", Accepted),
101

102
    // A way to temporarily opt out of opt in copy. This will *never* be accepted.
B
Brian Anderson 已提交
103
    ("opt_out_copy", "1.0.0", Removed),
104

105
    // OIBIT specific features
B
Brian Anderson 已提交
106
    ("optin_builtin_traits", "1.0.0", Active),
107

J
Joseph Crail 已提交
108
    // macro reexport needs more discussion and stabilization
A
Alex Crichton 已提交
109
    ("macro_reexport", "1.0.0", Active),
110

111 112
    // These are used to test this portion of the compiler, they don't actually
    // mean anything
B
Brian Anderson 已提交
113 114
    ("test_accepted_feature", "1.0.0", Accepted),
    ("test_removed_feature", "1.0.0", Removed),
115 116 117

    // Allows use of #[staged_api]
    ("staged_api", "1.0.0", Active),
118 119

    // Allows using items which are missing stability attributes
K
Keegan McAllister 已提交
120 121 122 123
    ("unmarked_api", "1.0.0", Active),

    // Allows using #![no_std]
    ("no_std", "1.0.0", Active),
124 125 126

    // Allows using `box` in patterns; RFC 469
    ("box_patterns", "1.0.0", Active),
127

128 129 130
    // Allows using the unsafe_no_drop_flag attribute (unlikely to
    // switch to Accepted; see RFC 320)
    ("unsafe_no_drop_flag", "1.0.0", Active),
131 132

    // Allows the use of custom attributes; RFC 572
M
Manish Goregaokar 已提交
133 134
    ("custom_attribute", "1.0.0", Active),

135 136 137 138
    // Allows the use of #[derive(Anything)] as sugar for
    // #[derive_Anything].
    ("custom_derive", "1.0.0", Active),

M
Manish Goregaokar 已提交
139 140
    // Allows the use of rustc_* attributes; RFC 572
    ("rustc_attrs", "1.0.0", Active),
H
Huon Wilson 已提交
141 142 143

    // Allows the use of `static_assert`
    ("static_assert", "1.0.0", Active),
144 145 146 147 148 149

    // Allows the use of #[allow_internal_unstable]. This is an
    // attribute on macro_rules! and can't use the attribute handling
    // below (it has to be checked before expansion possibly makes
    // macros disappear).
    ("allow_internal_unstable", "1.0.0", Active),
150 151 152

    // #23121. Array patterns have some hazards yet.
    ("slice_patterns", "1.0.0", Active),
153
];
154
// (changing above list without updating src/doc/reference.md makes @cmr sad)
155 156 157 158 159 160 161 162 163 164 165 166 167

enum Status {
    /// Represents an active feature that is currently being implemented or
    /// currently being considered for addition/removal.
    Active,

    /// Represents a feature which has since been removed (it was once Active)
    Removed,

    /// This language feature has since been Accepted (it was once Active)
    Accepted,
}

168
// Attributes that have a special meaning to rustc or rustdoc
169
pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[
170 171 172 173 174 175 176 177 178 179 180 181 182
    // Normal attributes

    ("warn", Normal),
    ("allow", Normal),
    ("forbid", Normal),
    ("deny", Normal),

    ("macro_reexport", Normal),
    ("macro_use", Normal),
    ("macro_export", Normal),
    ("plugin_registrar", Normal),

    ("cfg", Normal),
183
    ("cfg_attr", Normal),
184 185 186 187 188 189 190 191 192 193 194 195
    ("main", Normal),
    ("start", Normal),
    ("test", Normal),
    ("bench", Normal),
    ("simd", Normal),
    ("repr", Normal),
    ("path", Normal),
    ("abi", Normal),
    ("automatically_derived", Normal),
    ("no_mangle", Normal),
    ("no_link", Normal),
    ("derive", Normal),
S
Steven Fackler 已提交
196
    ("should_panic", Normal),
197 198 199 200 201
    ("ignore", Normal),
    ("no_implicit_prelude", Normal),
    ("reexport_test_harness_main", Normal),
    ("link_args", Normal),
    ("macro_escape", Normal),
202

203 204
    ("unsafe_destructor", Gated("unsafe_destructor",
                                "`#[unsafe_destructor]` does nothing anymore")),
M
Manish Goregaokar 已提交
205 206 207 208 209 210 211 212 213
    ("staged_api", Gated("staged_api",
                         "staged_api is for use by rustc only")),
    ("plugin", Gated("plugin",
                     "compiler plugins are experimental \
                      and possibly buggy")),
    ("no_std", Gated("no_std",
                     "no_std is experimental")),
    ("lang", Gated("lang_items",
                     "language items are subject to change")),
214 215 216 217 218 219 220
    ("linkage", Gated("linkage",
                      "the `linkage` attribute is experimental \
                       and not portable across platforms")),
    ("thread_local", Gated("thread_local",
                            "`#[thread_local]` is an experimental feature, and does not \
                             currently handle destructors. There is no corresponding \
                             `#[task_local]` mapping to the task model")),
M
Manish Goregaokar 已提交
221

M
Manish Goregaokar 已提交
222 223 224
    ("rustc_on_unimplemented", Gated("on_unimplemented",
                                     "the `#[rustc_on_unimplemented]` attribute \
                                      is an experimental feature")),
225 226
    ("allocator", Gated("allocator",
                        "the `#[allocator]` attribute is an experimental feature")),
M
Manish Goregaokar 已提交
227 228 229 230 231 232 233 234 235 236
    ("rustc_variance", Gated("rustc_attrs",
                             "the `#[rustc_variance]` attribute \
                              is an experimental feature")),
    ("rustc_error", Gated("rustc_attrs",
                          "the `#[rustc_error]` attribute \
                           is an experimental feature")),
    ("rustc_move_fragments", Gated("rustc_attrs",
                                   "the `#[rustc_move_fragments]` attribute \
                                    is an experimental feature")),

237 238 239
    ("allow_internal_unstable", Gated("allow_internal_unstable",
                                      EXPLAIN_ALLOW_INTERNAL_UNSTABLE)),

240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
    // FIXME: #14408 whitelist docs since rustdoc looks at them
    ("doc", Whitelisted),

    // FIXME: #14406 these are processed in trans, which happens after the
    // lint pass
    ("cold", Whitelisted),
    ("export_name", Whitelisted),
    ("inline", Whitelisted),
    ("link", Whitelisted),
    ("link_name", Whitelisted),
    ("link_section", Whitelisted),
    ("no_builtins", Whitelisted),
    ("no_mangle", Whitelisted),
    ("no_stack_check", Whitelisted),
    ("packed", Whitelisted),
H
Huon Wilson 已提交
255 256
    ("static_assert", Gated("static_assert",
                            "`#[static_assert]` is an experimental feature, and has a poor API")),
257 258
    ("no_debug", Whitelisted),
    ("omit_gdb_pretty_printer_section", Whitelisted),
259 260 261
    ("unsafe_no_drop_flag", Gated("unsafe_no_drop_flag",
                                  "unsafe_no_drop_flag has unstable semantics \
                                   and may be removed in the future")),
262 263 264 265 266 267 268 269 270 271 272

    // used in resolve
    ("prelude_import", Whitelisted),

    // FIXME: #14407 these are only looked at on-demand so we can't
    // guarantee they'll have already been checked
    ("deprecated", Whitelisted),
    ("must_use", Whitelisted),
    ("stable", Whitelisted),
    ("unstable", Whitelisted),

273 274 275 276
    ("rustc_paren_sugar", Gated("unboxed_closures",
                                "unboxed_closures are still evolving")),
    ("rustc_reflect_like", Gated("reflect",
                                 "defining reflective traits is still evolving")),
277 278 279 280

    // Crate level attributes
    ("crate_name", CrateLevel),
    ("crate_type", CrateLevel),
281
    ("crate_id", CrateLevel),
282 283 284 285
    ("feature", CrateLevel),
    ("no_start", CrateLevel),
    ("no_main", CrateLevel),
    ("no_builtins", CrateLevel),
286
    ("recursion_limit", CrateLevel),
287 288
];

289
#[derive(PartialEq, Copy, Debug)]
290 291 292 293 294 295 296 297 298 299
pub enum AttributeType {
    /// Normal, builtin attribute that is consumed
    /// by the compiler before the unused_attribute check
    Normal,

    /// Builtin attribute that may not be consumed by the compiler
    /// before the unused_attribute check. These attributes
    /// will be ignored by the unused_attribute lint
    Whitelisted,

M
Manish Goregaokar 已提交
300 301 302 303
    /// Is gated by a given feature gate and reason
    /// These get whitelisted too
    Gated(&'static str, &'static str),

304 305 306 307
    /// Builtin attribute that is only allowed at the crate level
    CrateLevel,
}

308 309
/// A set of features to be used by later passes.
pub struct Features {
310
    pub unboxed_closures: bool,
N
Nick Cameron 已提交
311
    pub rustc_diagnostic_macros: bool,
312
    pub visible_private_types: bool,
313 314
    pub allow_quote: bool,
    pub allow_asm: bool,
315 316 317
    pub allow_log_syntax: bool,
    pub allow_concat_idents: bool,
    pub allow_trace_macros: bool,
318
    pub allow_internal_unstable: bool,
319
    pub allow_custom_derive: bool,
320
    pub simd_ffi: bool,
321
    pub unmarked_api: bool,
322 323 324 325
    /// spans of #![feature] attrs for stable language features. for error reporting
    pub declared_stable_lang_features: Vec<Span>,
    /// #![feature] attrs for non-language (library) features
    pub declared_lib_features: Vec<(InternedString, Span)>
326 327 328 329 330
}

impl Features {
    pub fn new() -> Features {
        Features {
331
            unboxed_closures: false,
N
Nick Cameron 已提交
332
            rustc_diagnostic_macros: false,
333
            visible_private_types: false,
334 335
            allow_quote: false,
            allow_asm: false,
336 337 338
            allow_log_syntax: false,
            allow_concat_idents: false,
            allow_trace_macros: false,
339
            allow_internal_unstable: false,
340
            allow_custom_derive: false,
341
            simd_ffi: false,
342
            unmarked_api: false,
343 344
            declared_stable_lang_features: Vec::new(),
            declared_lib_features: Vec::new()
345 346 347 348
        }
    }
}

E
Eduard Burtescu 已提交
349 350
struct Context<'a> {
    features: Vec<&'static str>,
N
Nick Cameron 已提交
351
    span_handler: &'a SpanHandler,
C
Corey Richardson 已提交
352
    cm: &'a CodeMap,
353 354
}

E
Eduard Burtescu 已提交
355
impl<'a> Context<'a> {
356
    fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
357 358 359
        let has_feature = self.has_feature(feature);
        debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", feature, span, has_feature);
        if !has_feature {
360
            emit_feature_err(self.span_handler, feature, span, explain);
361 362
        }
    }
363
    fn has_feature(&self, feature: &str) -> bool {
364
        self.features.iter().any(|&n| n == feature)
365
    }
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383

    fn check_attribute(&self, attr: &ast::Attribute) {
        debug!("check_attribute(attr = {:?})", attr);
        let name = &*attr.name();
        for &(n, ty) in KNOWN_ATTRIBUTES {
            if n == name {
                if let Gated(gate, desc) = ty {
                    self.gate_feature(gate, attr.span, desc);
                }
                debug!("check_attribute: {:?} is known, {:?}", name, ty);
                return;
            }
        }
        if name.starts_with("rustc_") {
            self.gate_feature("rustc_attrs", attr.span,
                              "unless otherwise specified, attributes \
                               with the prefix `rustc_` \
                               are reserved for internal compiler diagnostics");
384 385 386 387
        } else if name.starts_with("derive_") {
            self.gate_feature("custom_derive", attr.span,
                              "attributes of the form `#[derive_*]` are reserved
                               for the compiler");
388 389
        } else {
            self.gate_feature("custom_attribute", attr.span,
390
                       &format!("The attribute `{}` is currently \
391 392 393
                                unknown to the the compiler and \
                                may have meaning \
                                added to it in the future",
394
                                name));
395 396
        }
    }
397 398
}

399 400
pub fn emit_feature_err(diag: &SpanHandler, feature: &str, span: Span, explain: &str) {
    diag.span_err(span, explain);
401
    diag.fileline_help(span, &format!("add #![feature({})] to the \
402
                                   crate attributes to enable",
403
                                  feature));
404 405
}

B
Brian Anderson 已提交
406 407
pub fn emit_feature_warn(diag: &SpanHandler, feature: &str, span: Span, explain: &str) {
    diag.span_warn(span, explain);
408
    if diag.handler.can_emit_warnings {
409
        diag.fileline_help(span, &format!("add #![feature({})] to the \
410
                                       crate attributes to silence this warning",
411
                                      feature));
412
    }
B
Brian Anderson 已提交
413 414
}

415 416 417
pub const EXPLAIN_ASM: &'static str =
    "inline assembly is not stable enough for use and is subject to change";

418 419 420 421 422 423 424 425
pub const EXPLAIN_LOG_SYNTAX: &'static str =
    "`log_syntax!` is not stable enough for use and is subject to change";

pub const EXPLAIN_CONCAT_IDENTS: &'static str =
    "`concat_idents` is not stable enough for use and is subject to change";

pub const EXPLAIN_TRACE_MACROS: &'static str =
    "`trace_macros` is not stable enough for use and is subject to change";
426 427
pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &'static str =
    "allow_internal_unstable side-steps feature gating and stability checks";
428

429 430 431
pub const EXPLAIN_CUSTOM_DERIVE: &'static str =
    "`#[derive]` for custom traits is not stable enough for use and is subject to change";

C
Corey Richardson 已提交
432 433 434 435 436
struct MacroVisitor<'a> {
    context: &'a Context<'a>
}

impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
K
Keegan McAllister 已提交
437 438
    fn visit_mac(&mut self, mac: &ast::Mac) {
        let ast::MacInvocTT(ref path, _, _) = mac.node;
C
Corey Richardson 已提交
439 440
        let id = path.segments.last().unwrap().identifier;

441 442 443 444 445 446 447 448
        // Issue 22234: If you add a new case here, make sure to also
        // add code to catch the macro during or after expansion.
        //
        // We still keep this MacroVisitor (rather than *solely*
        // relying on catching cases during or after expansion) to
        // catch uses of these macros within conditionally-compiled
        // code, e.g. `#[cfg]`-guarded functions.

K
Keegan McAllister 已提交
449
        if id == token::str_to_ident("asm") {
450
            self.context.gate_feature("asm", path.span, EXPLAIN_ASM);
C
Corey Richardson 已提交
451 452 453
        }

        else if id == token::str_to_ident("log_syntax") {
454
            self.context.gate_feature("log_syntax", path.span, EXPLAIN_LOG_SYNTAX);
C
Corey Richardson 已提交
455 456 457
        }

        else if id == token::str_to_ident("trace_macros") {
458
            self.context.gate_feature("trace_macros", path.span, EXPLAIN_TRACE_MACROS);
C
Corey Richardson 已提交
459 460 461
        }

        else if id == token::str_to_ident("concat_idents") {
462
            self.context.gate_feature("concat_idents", path.span, EXPLAIN_CONCAT_IDENTS);
C
Corey Richardson 已提交
463 464
        }
    }
465 466

    fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
467
        self.context.check_attribute(attr);
468
    }
C
Corey Richardson 已提交
469 470 471 472 473 474 475 476
}

struct PostExpansionVisitor<'a> {
    context: &'a Context<'a>
}

impl<'a> PostExpansionVisitor<'a> {
    fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
477
        if !self.context.cm.span_allows_unstable(span) {
C
Corey Richardson 已提交
478 479 480 481 482 483
            self.context.gate_feature(feature, span, explain)
        }
    }
}

impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
484 485 486 487 488 489
    fn visit_attribute(&mut self, attr: &ast::Attribute) {
        if !self.context.cm.span_allows_unstable(attr.span) {
            self.context.check_attribute(attr);
        }
    }

490
    fn visit_name(&mut self, sp: Span, name: ast::Name) {
G
GuillaumeGomez 已提交
491
        if !token::get_name(name).is_ascii() {
492 493 494 495 496
            self.gate_feature("non_ascii_idents", sp,
                              "non-ascii idents are not fully supported.");
        }
    }

497
    fn visit_item(&mut self, i: &ast::Item) {
498
        match i.node {
499
            ast::ItemExternCrate(_) => {
500
                if attr::contains_name(&i.attrs[..], "macro_reexport") {
501 502 503
                    self.gate_feature("macro_reexport", i.span,
                                      "macros reexports are experimental \
                                       and possibly buggy");
504 505 506
                }
            }

507
            ast::ItemForeignMod(ref foreign_module) => {
508
                if attr::contains_name(&i.attrs[..], "link_args") {
509 510 511 512 513
                    self.gate_feature("link_args", i.span,
                                      "the `link_args` attribute is not portable \
                                       across platforms, it is recommended to \
                                       use `#[link(name = \"foo\")]` instead")
                }
514 515 516 517 518
                if foreign_module.abi == RustIntrinsic {
                    self.gate_feature("intrinsics",
                                      i.span,
                                      "intrinsics are subject to change")
                }
519 520
            }

521
            ast::ItemFn(..) => {
522
                if attr::contains_name(&i.attrs[..], "plugin_registrar") {
523 524
                    self.gate_feature("plugin_registrar", i.span,
                                      "compiler plugins are experimental and possibly buggy");
525
                }
526
                if attr::contains_name(&i.attrs[..], "start") {
527 528 529 530 531
                    self.gate_feature("start", i.span,
                                      "a #[start] function is an experimental \
                                       feature whose signature may change \
                                       over time");
                }
532
                if attr::contains_name(&i.attrs[..], "main") {
533 534 535 536 537
                    self.gate_feature("main", i.span,
                                      "declaration of a nonstandard #[main] \
                                       function may change over time, for now \
                                       a top-level `fn main()` is required");
                }
538 539
            }

540
            ast::ItemStruct(..) => {
541
                if attr::contains_name(&i.attrs[..], "simd") {
D
David Manescu 已提交
542 543
                    self.gate_feature("simd", i.span,
                                      "SIMD types are experimental and possibly buggy");
544
                }
D
David Manescu 已提交
545 546
            }

F
Flavio Percoco 已提交
547 548 549 550 551 552 553
            ast::ItemDefaultImpl(..) => {
                self.gate_feature("optin_builtin_traits",
                                  i.span,
                                  "default trait implementations are experimental \
                                   and possibly buggy");
            }

A
Alex Crichton 已提交
554
            ast::ItemImpl(_, polarity, _, _, _, _) => {
555 556 557 558 559 560 561 562 563
                match polarity {
                    ast::ImplPolarity::Negative => {
                        self.gate_feature("optin_builtin_traits",
                                          i.span,
                                          "negative trait bounds are not yet fully implemented; \
                                          use marker types for now");
                    },
                    _ => {}
                }
564 565
            }

566 567 568
            _ => {}
        }

569
        visit::walk_item(self, i);
570
    }
571

572
    fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
573
        let links_to_llvm = match attr::first_attr_value_str_by_name(&i.attrs,
N
fallout  
Nick Cameron 已提交
574
                                                                     "link_name") {
G
GuillaumeGomez 已提交
575
            Some(val) => val.starts_with("llvm."),
576 577 578 579 580 581 582
            _ => false
        };
        if links_to_llvm {
            self.gate_feature("link_llvm_intrinsics", i.span,
                              "linking to LLVM intrinsics is experimental");
        }

583
        visit::walk_foreign_item(self, i)
584 585
    }

586
    fn visit_expr(&mut self, e: &ast::Expr) {
587
        match e.node {
588 589 590
            ast::ExprBox(..) | ast::ExprUnary(ast::UnOp::UnUniq, _) => {
                self.gate_feature("box_syntax",
                                  e.span,
591
                                  "box expression syntax is experimental; \
592 593
                                   you can call `Box::new` instead.");
            }
594 595
            _ => {}
        }
596
        visit::walk_expr(self, e);
597
    }
598

599
    fn visit_pat(&mut self, pattern: &ast::Pat) {
600 601 602 603 604 605 606 607
        match pattern.node {
            ast::PatVec(_, Some(_), ref last) if !last.is_empty() => {
                self.gate_feature("advanced_slice_patterns",
                                  pattern.span,
                                  "multiple-element slice matches anywhere \
                                   but at the end of a slice (e.g. \
                                   `[0, ..xs, 0]` are experimental")
            }
608 609 610 611 612
            ast::PatVec(..) => {
                self.gate_feature("slice_patterns",
                                  pattern.span,
                                  "slice pattern syntax is experimental");
            }
613
            ast::PatBox(..) => {
614
                self.gate_feature("box_patterns",
615
                                  pattern.span,
616
                                  "box pattern syntax is experimental");
617
            }
618 619
            _ => {}
        }
620
        visit::walk_pat(self, pattern)
621 622
    }

623
    fn visit_fn(&mut self,
624 625 626
                fn_kind: visit::FnKind<'v>,
                fn_decl: &'v ast::FnDecl,
                block: &'v ast::Block,
627
                span: Span,
628
                _node_id: NodeId) {
629 630
        match fn_kind {
            visit::FkItemFn(_, _, _, abi) if abi == RustIntrinsic => {
631 632 633 634 635 636
                self.gate_feature("intrinsics",
                                  span,
                                  "intrinsics are subject to change")
            }
            _ => {}
        }
637
        visit::walk_fn(self, fn_kind, fn_decl, block, span);
638
    }
639 640
}

641 642
fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
                        krate: &ast::Crate,
C
Corey Richardson 已提交
643
                        check: F)
644
                       -> Features
C
Corey Richardson 已提交
645 646
    where F: FnOnce(&mut Context, &ast::Crate)
{
647
    let mut cx = Context {
648
        features: Vec::new(),
N
Nick Cameron 已提交
649
        span_handler: span_handler,
C
Corey Richardson 已提交
650
        cm: cm,
651 652
    };

653
    let mut accepted_features = Vec::new();
N
Nick Cameron 已提交
654 655
    let mut unknown_features = Vec::new();

656
    for attr in &krate.attrs {
S
Steven Fackler 已提交
657
        if !attr.check_name("feature") {
658 659
            continue
        }
660 661 662

        match attr.meta_item_list() {
            None => {
N
Nick Cameron 已提交
663 664
                span_handler.span_err(attr.span, "malformed feature attribute, \
                                                  expected #![feature(...)]");
665 666
            }
            Some(list) => {
667
                for mi in list {
668
                    let name = match mi.node {
669
                        ast::MetaWord(ref word) => (*word).clone(),
670
                        _ => {
N
Nick Cameron 已提交
671 672 673
                            span_handler.span_err(mi.span,
                                                  "malformed feature, expected just \
                                                   one word");
674 675 676
                            continue
                        }
                    };
677
                    match KNOWN_FEATURES.iter()
B
Brian Anderson 已提交
678 679
                                        .find(|& &(n, _, _)| name == n) {
                        Some(&(name, _, Active)) => {
680 681
                            cx.features.push(name);
                        }
B
Brian Anderson 已提交
682
                        Some(&(_, _, Removed)) => {
N
Nick Cameron 已提交
683
                            span_handler.span_err(mi.span, "feature has been removed");
684
                        }
B
Brian Anderson 已提交
685
                        Some(&(_, _, Accepted)) => {
686
                            accepted_features.push(mi.span);
687 688
                        }
                        None => {
689
                            unknown_features.push((name, mi.span));
690 691 692 693 694 695 696
                        }
                    }
                }
            }
        }
    }

C
Corey Richardson 已提交
697
    check(&mut cx, krate);
698

699 700 701
    // FIXME (pnkfelix): Before adding the 99th entry below, change it
    // to a single-pass (instead of N calls to `.has_feature`).

702
    Features {
703
        unboxed_closures: cx.has_feature("unboxed_closures"),
N
Nick Cameron 已提交
704
        rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
705
        visible_private_types: cx.has_feature("visible_private_types"),
706 707
        allow_quote: cx.has_feature("quote"),
        allow_asm: cx.has_feature("asm"),
708 709 710
        allow_log_syntax: cx.has_feature("log_syntax"),
        allow_concat_idents: cx.has_feature("concat_idents"),
        allow_trace_macros: cx.has_feature("trace_macros"),
711
        allow_internal_unstable: cx.has_feature("allow_internal_unstable"),
712
        allow_custom_derive: cx.has_feature("custom_derive"),
713
        simd_ffi: cx.has_feature("simd_ffi"),
714
        unmarked_api: cx.has_feature("unmarked_api"),
715 716
        declared_stable_lang_features: accepted_features,
        declared_lib_features: unknown_features
717
    }
718
}
C
Corey Richardson 已提交
719 720

pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
721
-> Features {
722
    check_crate_inner(cm, span_handler, krate,
C
Corey Richardson 已提交
723 724 725
                      |ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
}

726 727
pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
                   -> Features
728
{
729
    check_crate_inner(cm, span_handler, krate,
C
Corey Richardson 已提交
730 731 732
                      |ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx },
                                                     krate))
}