feature_gate.rs 18.2 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
static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
    ("globs", Active),
K
Keegan McAllister 已提交
40
    ("macro_rules", Accepted),
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
struct MacroVisitor<'a> {
    context: &'a Context<'a>
}

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

K
Keegan McAllister 已提交
172
        if id == token::str_to_ident("asm") {
C
Corey Richardson 已提交
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
            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> {
207 208
    fn visit_name(&mut self, sp: Span, name: ast::Name) {
        if !token::get_name(name).get().is_ascii() {
209 210 211 212 213
            self.gate_feature("non_ascii_idents", sp,
                              "non-ascii idents are not fully supported.");
        }
    }

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

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

264
            ast::ItemFn(..) => {
A
Alex Crichton 已提交
265
                if attr::contains_name(i.attrs[], "plugin_registrar") {
266 267
                    self.gate_feature("plugin_registrar", i.span,
                                      "compiler plugins are experimental and possibly buggy");
268 269 270
                }
            }

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

278 279 280 281 282 283 284 285 286 287 288
            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 已提交
289
                if attr::contains_name(i.attrs[],
290 291 292 293 294 295 296
                                       "unsafe_destructor") {
                    self.gate_feature("unsafe_destructor",
                                      i.span,
                                      "`#[unsafe_destructor]` allows too \
                                       many unsafe patterns and may be \
                                       removed in the future");
                }
297 298 299 300 301 302 303 304 305 306 307 308

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

311 312 313
            _ => {}
        }

314
        visit::walk_item(self, i);
315
    }
316

317 318 319 320 321
    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",
322
                                  ti.ty_param.span,
323 324 325 326 327
                                  "associated types are experimental")
            }
        }
    }

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

        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");
        }

344
        visit::walk_foreign_item(self, i)
345 346
    }

347
    fn visit_ty(&mut self, t: &ast::Ty) {
348 349 350 351
        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);
352 353
        }

354
        visit::walk_ty(self, t);
355
    }
356

357
    fn visit_expr(&mut self, e: &ast::Expr) {
358
        match e.node {
359
            ast::ExprRange(..) => {
360 361
                self.gate_feature("slicing_syntax",
                                  e.span,
362
                                  "range syntax is experimental");
363
            }
364 365
            _ => {}
        }
366
        visit::walk_expr(self, e);
367
    }
368

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

383
    fn visit_attribute(&mut self, attr: &ast::Attribute) {
384
        if attr::contains_name(slice::ref_slice(attr), "lang") {
385 386 387 388 389 390
            self.gate_feature("lang_items",
                              attr.span,
                              "language items are subject to change");
        }
    }

391
    fn visit_pat(&mut self, pattern: &ast::Pat) {
392 393 394 395 396 397 398 399 400 401
        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")
            }
            _ => {}
        }
402
        visit::walk_pat(self, pattern)
403 404
    }

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

C
Corey Richardson 已提交
423 424 425 426 427
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)
{
428
    let mut cx = Context {
429
        features: Vec::new(),
N
Nick Cameron 已提交
430
        span_handler: span_handler,
C
Corey Richardson 已提交
431
        cm: cm,
432 433
    };

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

436
    for attr in krate.attrs.iter() {
S
Steven Fackler 已提交
437
        if !attr.check_name("feature") {
438 439
            continue
        }
440 441 442

        match attr.meta_item_list() {
            None => {
N
Nick Cameron 已提交
443 444
                span_handler.span_err(attr.span, "malformed feature attribute, \
                                                  expected #![feature(...)]");
445 446
            }
            Some(list) => {
447
                for mi in list.iter() {
448
                    let name = match mi.node {
449
                        ast::MetaWord(ref word) => (*word).clone(),
450
                        _ => {
N
Nick Cameron 已提交
451 452 453
                            span_handler.span_err(mi.span,
                                                  "malformed feature, expected just \
                                                   one word");
454 455 456
                            continue
                        }
                    };
457
                    match KNOWN_FEATURES.iter()
458
                                        .find(|& &(n, _)| name == n) {
459 460 461 462 463 464 465 466 467 468
                        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");
                        }
469
                        Some(&(_, Removed)) => {
N
Nick Cameron 已提交
470
                            span_handler.span_err(mi.span, "feature has been removed");
471 472
                        }
                        Some(&(_, Accepted)) => {
N
Nick Cameron 已提交
473 474
                            span_handler.span_warn(mi.span, "feature has been added to Rust, \
                                                             directive not necessary");
475 476
                        }
                        None => {
N
Nick Cameron 已提交
477
                            unknown_features.push(mi.span);
478 479 480 481 482 483 484
                        }
                    }
                }
            }
        }
    }

C
Corey Richardson 已提交
485
    check(&mut cx, krate);
486

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

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))
}