feature_gate.rs 18.4 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.
S
Steven Fackler 已提交
20
use self::Status::*;
21

N
Nick Cameron 已提交
22 23 24 25 26
use abi::RustIntrinsic;
use ast::NodeId;
use ast;
use attr;
use attr::AttrMetaMethods;
C
Corey Richardson 已提交
27
use codemap::{CodeMap, Span};
N
Nick Cameron 已提交
28 29 30 31
use diagnostic::SpanHandler;
use visit;
use visit::Visitor;
use parse::token;
32

33
use std::slice;
34 35
use std::ascii::AsciiExt;

36

S
Steve Klabnik 已提交
37
// if you change this list without updating src/doc/reference.md, @cmr will be sad
38 39 40
static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
    ("globs", Active),
    ("macro_rules", Active),
S
Steven Fackler 已提交
41
    ("struct_variant", Accepted),
L
Léo Testard 已提交
42
    ("asm", Active),
43
    ("managed_boxes", Removed),
44
    ("non_ascii_idents", Active),
D
Daniel Micay 已提交
45
    ("thread_local", Active),
46
    ("link_args", Active),
47
    ("phase", Active),  // NOTE(stage0): switch to Removed after next snapshot
48
    ("plugin_registrar", Active),
49
    ("log_syntax", Active),
X
xales 已提交
50
    ("trace_macros", Active),
51
    ("concat_idents", Active),
52
    ("unsafe_destructor", Active),
53 54
    ("intrinsics", Active),
    ("lang_items", Active),
55

D
David Manescu 已提交
56
    ("simd", Active),
57
    ("default_type_params", Active),
58
    ("quote", Active),
59
    ("link_llvm_intrinsics", Active),
60
    ("linkage", Active),
61
    ("struct_inherit", Removed),
62

63
    ("quad_precision_float", Removed),
64

65
    ("rustc_diagnostic_macros", Active),
66
    ("unboxed_closures", Active),
67
    ("import_shadowing", Active),
68
    ("advanced_slice_patterns", Active),
69
    ("tuple_indexing", Accepted),
70
    ("associated_types", Active),
71
    ("visible_private_types", Active),
72
    ("slicing_syntax", Active),
73

74 75
    ("if_let", Accepted),
    ("while_let", Accepted),
76

77 78
    ("plugin", Active),

79 80
    // A temporary feature gate used to enable parser extensions needed
    // to bootstrap fix for #5723.
81
    ("issue_5723_bootstrap", Accepted),
82

83 84 85 86 87
    // A way to temporarily opt out of opt in copy. This will *never* be accepted.
    ("opt_out_copy", Deprecated),

    // A way to temporarily opt out of the new orphan rules. This will *never* be accepted.
    ("old_orphan_check", Deprecated),
88

89 90 91
    // OIBIT specific features
    ("optin_builtin_traits", Active),

92 93 94 95 96 97 98 99 100 101 102
    // These are used to test this portion of the compiler, they don't actually
    // mean anything
    ("test_accepted_feature", Accepted),
    ("test_removed_feature", Removed),
];

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

103 104 105 106
    /// Represents a feature gate that is temporarily enabling deprecated behavior.
    /// This gate will never be accepted.
    Deprecated,

107 108 109 110 111 112 113
    /// 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,
}

114
/// A set of features to be used by later passes.
115
#[derive(Copy)]
116
pub struct Features {
N
Nick Cameron 已提交
117
    pub default_type_params: bool,
118
    pub unboxed_closures: bool,
N
Nick Cameron 已提交
119 120
    pub rustc_diagnostic_macros: bool,
    pub import_shadowing: bool,
121
    pub visible_private_types: bool,
122
    pub quote: bool,
123
    pub opt_out_copy: bool,
124
    pub old_orphan_check: bool,
125 126 127 128 129
}

impl Features {
    pub fn new() -> Features {
        Features {
N
Nick Cameron 已提交
130
            default_type_params: false,
131
            unboxed_closures: false,
N
Nick Cameron 已提交
132 133
            rustc_diagnostic_macros: false,
            import_shadowing: false,
134
            visible_private_types: false,
135
            quote: false,
136
            opt_out_copy: false,
137
            old_orphan_check: false,
138 139 140 141
        }
    }
}

E
Eduard Burtescu 已提交
142 143
struct Context<'a> {
    features: Vec<&'static str>,
N
Nick Cameron 已提交
144
    span_handler: &'a SpanHandler,
C
Corey Richardson 已提交
145
    cm: &'a CodeMap,
146 147
}

E
Eduard Burtescu 已提交
148
impl<'a> Context<'a> {
149 150
    fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
        if !self.has_feature(feature) {
N
Nick Cameron 已提交
151
            self.span_handler.span_err(span, explain);
P
P1start 已提交
152
            self.span_handler.span_help(span, format!("add #![feature({})] to the \
N
Nick Cameron 已提交
153
                                                       crate attributes to enable",
A
Alex Crichton 已提交
154
                                                      feature)[]);
155 156
        }
    }
157 158

    fn has_feature(&self, feature: &str) -> bool {
159
        self.features.iter().any(|&n| n == feature)
160 161 162
    }
}

C
Corey Richardson 已提交
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
struct MacroVisitor<'a> {
    context: &'a Context<'a>
}

impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
    fn visit_mac(&mut self, macro: &ast::Mac) {
        let ast::MacInvocTT(ref path, _, _) = macro.node;
        let id = path.segments.last().unwrap().identifier;

        if id == token::str_to_ident("macro_rules") {
            self.context.gate_feature("macro_rules", path.span, "macro definitions are \
                not stable enough for use and are subject to change");
        }

        else if id == token::str_to_ident("asm") {
            self.context.gate_feature("asm", path.span, "inline assembly is not \
                stable enough for use and is subject to change");
        }

        else if id == token::str_to_ident("log_syntax") {
            self.context.gate_feature("log_syntax", path.span, "`log_syntax!` is not \
                stable enough for use and is subject to change");
        }

        else if id == token::str_to_ident("trace_macros") {
            self.context.gate_feature("trace_macros", path.span, "`trace_macros` is not \
                stable enough for use and is subject to change");
        }

        else if id == token::str_to_ident("concat_idents") {
            self.context.gate_feature("concat_idents", path.span, "`concat_idents` is not \
                stable enough for use and is subject to change");
        }
    }
}

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

impl<'a> PostExpansionVisitor<'a> {
    fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
        if !self.context.cm.span_is_internal(span) {
            self.context.gate_feature(feature, span, explain)
        }
    }
}

impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
212 213
    fn visit_name(&mut self, sp: Span, name: ast::Name) {
        if !token::get_name(name).get().is_ascii() {
214 215 216 217 218
            self.gate_feature("non_ascii_idents", sp,
                              "non-ascii idents are not fully supported.");
        }
    }

219
    fn visit_view_item(&mut self, i: &ast::ViewItem) {
220
        match i.node {
221
            ast::ViewItemUse(ref path) => {
222 223 224 225
                if let ast::ViewPathGlob(..) = path.node {
                    self.gate_feature("globs", path.span,
                                      "glob import statements are \
                                       experimental and possibly buggy");
226 227
                }
            }
228
            ast::ViewItemExternCrate(..) => {
229
                for attr in i.attrs.iter() {
230 231 232 233
                    if attr.check_name("plugin") {
                        self.gate_feature("plugin", attr.span,
                                          "compiler plugins are experimental \
                                           and possibly buggy");
234 235 236
                    }
                }
            }
237
        }
238
        visit::walk_view_item(self, i)
239 240
    }

241
    fn visit_item(&mut self, i: &ast::Item) {
D
Daniel Micay 已提交
242
        for attr in i.attrs.iter() {
243
            if attr.name() == "thread_local" {
D
Daniel Micay 已提交
244 245 246 247
                self.gate_feature("thread_local", i.span,
                                  "`#[thread_local]` is an experimental feature, and does not \
                                  currently handle destructors. There is no corresponding \
                                  `#[task_local]` mapping to the task model");
248
            } else if attr.name() == "linkage" {
249 250 251
                self.gate_feature("linkage", i.span,
                                  "the `linkage` attribute is experimental \
                                   and not portable across platforms")
D
Daniel Micay 已提交
252 253
            }
        }
254
        match i.node {
255
            ast::ItemForeignMod(ref foreign_module) => {
A
Alex Crichton 已提交
256
                if attr::contains_name(i.attrs[], "link_args") {
257 258 259 260 261
                    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")
                }
262 263 264 265 266
                if foreign_module.abi == RustIntrinsic {
                    self.gate_feature("intrinsics",
                                      i.span,
                                      "intrinsics are subject to change")
                }
267 268
            }

269
            ast::ItemFn(..) => {
A
Alex Crichton 已提交
270
                if attr::contains_name(i.attrs[], "plugin_registrar") {
271 272
                    self.gate_feature("plugin_registrar", i.span,
                                      "compiler plugins are experimental and possibly buggy");
273 274 275
                }
            }

276
            ast::ItemStruct(..) => {
A
Alex Crichton 已提交
277
                if attr::contains_name(i.attrs[], "simd") {
D
David Manescu 已提交
278 279
                    self.gate_feature("simd", i.span,
                                      "SIMD types are experimental and possibly buggy");
280
                }
D
David Manescu 已提交
281 282
            }

283 284 285 286 287 288 289 290 291 292 293
            ast::ItemImpl(_, polarity, _, _, _, ref items) => {
                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");
                    },
                    _ => {}
                }

C
Corey Richardson 已提交
294
                if attr::contains_name(i.attrs[],
295 296 297 298 299 300 301
                                       "unsafe_destructor") {
                    self.gate_feature("unsafe_destructor",
                                      i.span,
                                      "`#[unsafe_destructor]` allows too \
                                       many unsafe patterns and may be \
                                       removed in the future");
                }
302 303 304 305 306 307 308 309 310 311 312 313

                for item in items.iter() {
                    match *item {
                        ast::MethodImplItem(_) => {}
                        ast::TypeImplItem(ref typedef) => {
                            self.gate_feature("associated_types",
                                              typedef.span,
                                              "associated types are \
                                               experimental")
                        }
                    }
                }
314 315
            }

316 317 318
            _ => {}
        }

319
        visit::walk_item(self, i);
320
    }
321

322 323 324 325 326
    fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) {
        match *trait_item {
            ast::RequiredMethod(_) | ast::ProvidedMethod(_) => {}
            ast::TypeTraitItem(ref ti) => {
                self.gate_feature("associated_types",
327
                                  ti.ty_param.span,
328 329 330 331 332
                                  "associated types are experimental")
            }
        }
    }

333
    fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
A
Alex Crichton 已提交
334
        if attr::contains_name(i.attrs[], "linkage") {
335 336 337
            self.gate_feature("linkage", i.span,
                              "the `linkage` attribute is experimental \
                               and not portable across platforms")
338
        }
339 340 341 342 343 344 345 346 347 348

        let links_to_llvm = match attr::first_attr_value_str_by_name(i.attrs[], "link_name") {
            Some(val) => val.get().starts_with("llvm."),
            _ => false
        };
        if links_to_llvm {
            self.gate_feature("link_llvm_intrinsics", i.span,
                              "linking to LLVM intrinsics is experimental");
        }

349
        visit::walk_foreign_item(self, i)
350 351
    }

352
    fn visit_ty(&mut self, t: &ast::Ty) {
353 354 355 356
        if let ast::TyClosure(ref closure) =  t.node {
            // this used to be blocked by a feature gate, but it should just
            // be plain impossible right now
            assert!(closure.onceness != ast::Once);
357 358
        }

359
        visit::walk_ty(self, t);
360
    }
361

362
    fn visit_expr(&mut self, e: &ast::Expr) {
363
        match e.node {
364
            ast::ExprRange(..) => {
365 366
                self.gate_feature("slicing_syntax",
                                  e.span,
367
                                  "range syntax is experimental");
368
            }
369 370
            _ => {}
        }
371
        visit::walk_expr(self, e);
372
    }
373

374
    fn visit_generics(&mut self, generics: &ast::Generics) {
375 376
        for type_parameter in generics.ty_params.iter() {
            match type_parameter.default {
377
                Some(ref ty) => {
378 379 380 381 382 383 384
                    self.gate_feature("default_type_params", ty.span,
                                      "default type parameters are \
                                       experimental and possibly buggy");
                }
                None => {}
            }
        }
385
        visit::walk_generics(self, generics);
386
    }
387

388
    fn visit_attribute(&mut self, attr: &ast::Attribute) {
389
        if attr::contains_name(slice::ref_slice(attr), "lang") {
390 391 392 393 394 395
            self.gate_feature("lang_items",
                              attr.span,
                              "language items are subject to change");
        }
    }

396
    fn visit_pat(&mut self, pattern: &ast::Pat) {
397 398 399 400 401 402 403 404 405 406
        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")
            }
            _ => {}
        }
407
        visit::walk_pat(self, pattern)
408 409
    }

410
    fn visit_fn(&mut self,
411 412 413
                fn_kind: visit::FnKind<'v>,
                fn_decl: &'v ast::FnDecl,
                block: &'v ast::Block,
414
                span: Span,
415
                _node_id: NodeId) {
416 417
        match fn_kind {
            visit::FkItemFn(_, _, _, abi) if abi == RustIntrinsic => {
418 419 420 421 422 423
                self.gate_feature("intrinsics",
                                  span,
                                  "intrinsics are subject to change")
            }
            _ => {}
        }
424
        visit::walk_fn(self, fn_kind, fn_decl, block, span);
425
    }
426 427
}

C
Corey Richardson 已提交
428 429 430 431 432
fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate,
                        check: F)
                       -> (Features, Vec<Span>)
    where F: FnOnce(&mut Context, &ast::Crate)
{
433
    let mut cx = Context {
434
        features: Vec::new(),
N
Nick Cameron 已提交
435
        span_handler: span_handler,
C
Corey Richardson 已提交
436
        cm: cm,
437 438
    };

N
Nick Cameron 已提交
439 440
    let mut unknown_features = Vec::new();

441
    for attr in krate.attrs.iter() {
S
Steven Fackler 已提交
442
        if !attr.check_name("feature") {
443 444
            continue
        }
445 446 447

        match attr.meta_item_list() {
            None => {
N
Nick Cameron 已提交
448 449
                span_handler.span_err(attr.span, "malformed feature attribute, \
                                                  expected #![feature(...)]");
450 451
            }
            Some(list) => {
452
                for mi in list.iter() {
453
                    let name = match mi.node {
454
                        ast::MetaWord(ref word) => (*word).clone(),
455
                        _ => {
N
Nick Cameron 已提交
456 457 458
                            span_handler.span_err(mi.span,
                                                  "malformed feature, expected just \
                                                   one word");
459 460 461
                            continue
                        }
                    };
462
                    match KNOWN_FEATURES.iter()
463
                                        .find(|& &(n, _)| name == n) {
464 465 466 467 468 469 470 471 472 473
                        Some(&(name, Active)) => {
                            cx.features.push(name);
                        }
                        Some(&(name, Deprecated)) => {
                            cx.features.push(name);
                            span_handler.span_warn(
                                mi.span,
                                "feature is deprecated and will only be available \
                                 for a limited time, please rewrite code that relies on it");
                        }
474
                        Some(&(_, Removed)) => {
N
Nick Cameron 已提交
475
                            span_handler.span_err(mi.span, "feature has been removed");
476 477
                        }
                        Some(&(_, Accepted)) => {
N
Nick Cameron 已提交
478 479
                            span_handler.span_warn(mi.span, "feature has been added to Rust, \
                                                             directive not necessary");
480 481
                        }
                        None => {
N
Nick Cameron 已提交
482
                            unknown_features.push(mi.span);
483 484 485 486 487 488 489
                        }
                    }
                }
            }
        }
    }

C
Corey Richardson 已提交
490
    check(&mut cx, krate);
491

N
Nick Cameron 已提交
492 493
    (Features {
        default_type_params: cx.has_feature("default_type_params"),
494
        unboxed_closures: cx.has_feature("unboxed_closures"),
N
Nick Cameron 已提交
495 496
        rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
        import_shadowing: cx.has_feature("import_shadowing"),
497
        visible_private_types: cx.has_feature("visible_private_types"),
498
        quote: cx.has_feature("quote"),
499
        opt_out_copy: cx.has_feature("opt_out_copy"),
500
        old_orphan_check: cx.has_feature("old_orphan_check"),
N
Nick Cameron 已提交
501 502
    },
    unknown_features)
503
}
C
Corey Richardson 已提交
504 505 506 507 508 509 510 511 512 513 514 515 516

pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
-> (Features, Vec<Span>) {
    check_crate_inner(cm, span_handler, krate,
                      |ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
}

pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
-> (Features, Vec<Span>) {
    check_crate_inner(cm, span_handler, krate,
                      |ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx },
                                                     krate))
}