feature_gate.rs 16.5 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
static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
H
Huon Wilson 已提交
39
    ("globs", Accepted),
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),
H
Huon Wilson 已提交
57
    ("default_type_params", Accepted),
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),
H
Huon Wilson 已提交
70
    ("associated_types", Accepted),
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 {
117
    pub unboxed_closures: bool,
N
Nick Cameron 已提交
118 119
    pub rustc_diagnostic_macros: bool,
    pub import_shadowing: bool,
120
    pub visible_private_types: bool,
121
    pub quote: bool,
122
    pub opt_out_copy: bool,
123
    pub old_orphan_check: bool,
124 125 126 127 128
}

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

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

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

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

C
Corey Richardson 已提交
161 162 163 164 165
struct MacroVisitor<'a> {
    context: &'a Context<'a>
}

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

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

212
    fn visit_view_item(&mut self, i: &ast::ViewItem) {
213
        match i.node {
H
Huon Wilson 已提交
214
            ast::ViewItemUse(..) => {}
215
            ast::ViewItemExternCrate(..) => {
216
                for attr in i.attrs.iter() {
217 218 219 220
                    if attr.check_name("plugin") {
                        self.gate_feature("plugin", attr.span,
                                          "compiler plugins are experimental \
                                           and possibly buggy");
221 222 223
                    }
                }
            }
224
        }
225
        visit::walk_view_item(self, i)
226 227
    }

228
    fn visit_item(&mut self, i: &ast::Item) {
D
Daniel Micay 已提交
229
        for attr in i.attrs.iter() {
230
            if attr.name() == "thread_local" {
D
Daniel Micay 已提交
231 232 233 234
                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");
235
            } else if attr.name() == "linkage" {
236 237 238
                self.gate_feature("linkage", i.span,
                                  "the `linkage` attribute is experimental \
                                   and not portable across platforms")
D
Daniel Micay 已提交
239 240
            }
        }
241
        match i.node {
242
            ast::ItemForeignMod(ref foreign_module) => {
243
                if attr::contains_name(i.attrs.index(&FullRange), "link_args") {
244 245 246 247 248
                    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")
                }
249 250 251 252 253
                if foreign_module.abi == RustIntrinsic {
                    self.gate_feature("intrinsics",
                                      i.span,
                                      "intrinsics are subject to change")
                }
254 255
            }

256
            ast::ItemFn(..) => {
257
                if attr::contains_name(i.attrs.index(&FullRange), "plugin_registrar") {
258 259
                    self.gate_feature("plugin_registrar", i.span,
                                      "compiler plugins are experimental and possibly buggy");
260 261 262
                }
            }

263
            ast::ItemStruct(..) => {
264
                if attr::contains_name(i.attrs.index(&FullRange), "simd") {
D
David Manescu 已提交
265 266
                    self.gate_feature("simd", i.span,
                                      "SIMD types are experimental and possibly buggy");
267
                }
D
David Manescu 已提交
268 269
            }

A
Alex Crichton 已提交
270
            ast::ItemImpl(_, polarity, _, _, _, _) => {
271 272 273 274 275 276 277 278 279 280
                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 已提交
281
                if attr::contains_name(i.attrs[],
282 283 284 285 286 287 288
                                       "unsafe_destructor") {
                    self.gate_feature("unsafe_destructor",
                                      i.span,
                                      "`#[unsafe_destructor]` allows too \
                                       many unsafe patterns and may be \
                                       removed in the future");
                }
289

290 291 292 293 294 295 296
                if attr::contains_name(i.attrs[],
                                       "old_orphan_check") {
                    self.gate_feature(
                        "old_orphan_check",
                        i.span,
                        "the new orphan check rules will eventually be strictly enforced");
                }
297 298
            }

299 300 301
            _ => {}
        }

302
        visit::walk_item(self, i);
303
    }
304

305
    fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
306
        if attr::contains_name(i.attrs.index(&FullRange), "linkage") {
307 308 309
            self.gate_feature("linkage", i.span,
                              "the `linkage` attribute is experimental \
                               and not portable across platforms")
310
        }
311 312 313 314 315 316 317 318 319 320

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

321
        visit::walk_foreign_item(self, i)
322 323
    }

324 325
    fn visit_ty(&mut self, t: &ast::Ty) {
        visit::walk_ty(self, t);
326
    }
327

328
    fn visit_expr(&mut self, e: &ast::Expr) {
329
        match e.node {
330
            ast::ExprRange(..) => {
331 332
                self.gate_feature("slicing_syntax",
                                  e.span,
333
                                  "range syntax is experimental");
334
            }
335 336
            _ => {}
        }
337
        visit::walk_expr(self, e);
338
    }
339

340
    fn visit_attribute(&mut self, attr: &ast::Attribute) {
341
        if attr::contains_name(slice::ref_slice(attr), "lang") {
342 343 344 345 346 347
            self.gate_feature("lang_items",
                              attr.span,
                              "language items are subject to change");
        }
    }

348
    fn visit_pat(&mut self, pattern: &ast::Pat) {
349 350 351 352 353 354 355 356 357 358
        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")
            }
            _ => {}
        }
359
        visit::walk_pat(self, pattern)
360 361
    }

362
    fn visit_fn(&mut self,
363 364 365
                fn_kind: visit::FnKind<'v>,
                fn_decl: &'v ast::FnDecl,
                block: &'v ast::Block,
366
                span: Span,
367
                _node_id: NodeId) {
368 369
        match fn_kind {
            visit::FkItemFn(_, _, _, abi) if abi == RustIntrinsic => {
370 371 372 373 374 375
                self.gate_feature("intrinsics",
                                  span,
                                  "intrinsics are subject to change")
            }
            _ => {}
        }
376
        visit::walk_fn(self, fn_kind, fn_decl, block, span);
377
    }
378 379
}

C
Corey Richardson 已提交
380 381 382 383 384
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)
{
385
    let mut cx = Context {
386
        features: Vec::new(),
N
Nick Cameron 已提交
387
        span_handler: span_handler,
C
Corey Richardson 已提交
388
        cm: cm,
389 390
    };

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

393
    for attr in krate.attrs.iter() {
S
Steven Fackler 已提交
394
        if !attr.check_name("feature") {
395 396
            continue
        }
397 398 399

        match attr.meta_item_list() {
            None => {
N
Nick Cameron 已提交
400 401
                span_handler.span_err(attr.span, "malformed feature attribute, \
                                                  expected #![feature(...)]");
402 403
            }
            Some(list) => {
404
                for mi in list.iter() {
405
                    let name = match mi.node {
406
                        ast::MetaWord(ref word) => (*word).clone(),
407
                        _ => {
N
Nick Cameron 已提交
408 409 410
                            span_handler.span_err(mi.span,
                                                  "malformed feature, expected just \
                                                   one word");
411 412 413
                            continue
                        }
                    };
414
                    match KNOWN_FEATURES.iter()
415
                                        .find(|& &(n, _)| name == n) {
416 417 418 419 420 421 422 423 424 425
                        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");
                        }
426
                        Some(&(_, Removed)) => {
N
Nick Cameron 已提交
427
                            span_handler.span_err(mi.span, "feature has been removed");
428 429
                        }
                        Some(&(_, Accepted)) => {
N
Nick Cameron 已提交
430 431
                            span_handler.span_warn(mi.span, "feature has been added to Rust, \
                                                             directive not necessary");
432 433
                        }
                        None => {
N
Nick Cameron 已提交
434
                            unknown_features.push(mi.span);
435 436 437 438 439 440 441
                        }
                    }
                }
            }
        }
    }

C
Corey Richardson 已提交
442
    check(&mut cx, krate);
443

N
Nick Cameron 已提交
444
    (Features {
445
        unboxed_closures: cx.has_feature("unboxed_closures"),
N
Nick Cameron 已提交
446 447
        rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
        import_shadowing: cx.has_feature("import_shadowing"),
448
        visible_private_types: cx.has_feature("visible_private_types"),
449
        quote: cx.has_feature("quote"),
450
        opt_out_copy: cx.has_feature("opt_out_copy"),
451
        old_orphan_check: cx.has_feature("old_orphan_check"),
N
Nick Cameron 已提交
452 453
    },
    unknown_features)
454
}
C
Corey Richardson 已提交
455 456 457 458 459 460 461 462 463 464 465 466 467

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