]> git.lizzy.rs Git - rust.git/blob - src/librustc/front/feature_gate.rs
auto merge of #15570 : omasanori/rust/radix, r=alexcrichton
[rust.git] / src / librustc / front / feature_gate.rs
1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Feature gating
12 //!
13 //! This modules implements the gating necessary for preventing certain compiler
14 //! features from being used by default. This module will crawl a pre-expanded
15 //! AST to ensure that there are no features which are used that are not
16 //! enabled.
17 //!
18 //! Features are enabled in programs via the crate-level attributes of
19 //! `#![feature(...)]` with a comma-separated list of features.
20
21 use lint;
22
23 use syntax::abi::RustIntrinsic;
24 use syntax::ast::NodeId;
25 use syntax::ast;
26 use syntax::attr;
27 use syntax::attr::AttrMetaMethods;
28 use syntax::codemap::Span;
29 use syntax::visit;
30 use syntax::visit::Visitor;
31 use syntax::parse::token;
32
33 use driver::session::Session;
34
35 use std::cell::Cell;
36
37 /// This is a list of all known features since the beginning of time. This list
38 /// can never shrink, it may only be expanded (in order to prevent old programs
39 /// from failing to compile). The status of each feature may change, however.
40 static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
41     ("globs", Active),
42     ("macro_rules", Active),
43     ("struct_variant", Active),
44     ("once_fns", Active),
45     ("asm", Active),
46     ("managed_boxes", Active),
47     ("non_ascii_idents", Active),
48     ("thread_local", Active),
49     ("link_args", Active),
50     ("phase", Active),
51     ("plugin_registrar", Active),
52     ("log_syntax", Active),
53     ("trace_macros", Active),
54     ("concat_idents", Active),
55     ("unsafe_destructor", Active),
56     ("intrinsics", Active),
57     ("lang_items", Active),
58
59     ("simd", Active),
60     ("default_type_params", Active),
61     ("quote", Active),
62     ("linkage", Active),
63     ("struct_inherit", Active),
64     ("overloaded_calls", Active),
65     ("unboxed_closure_sugar", Active),
66
67     ("quad_precision_float", Removed),
68
69     ("rustc_diagnostic_macros", Active),
70
71     // A temporary feature gate used to enable parser extensions needed
72     // to bootstrap fix for #5723.
73     ("issue_5723_bootstrap", Active),
74
75     // These are used to test this portion of the compiler, they don't actually
76     // mean anything
77     ("test_accepted_feature", Accepted),
78     ("test_removed_feature", Removed),
79 ];
80
81 enum Status {
82     /// Represents an active feature that is currently being implemented or
83     /// currently being considered for addition/removal.
84     Active,
85
86     /// Represents a feature which has since been removed (it was once Active)
87     Removed,
88
89     /// This language feature has since been Accepted (it was once Active)
90     Accepted,
91 }
92
93 /// A set of features to be used by later passes.
94 pub struct Features {
95     pub default_type_params: Cell<bool>,
96     pub issue_5723_bootstrap: Cell<bool>,
97     pub overloaded_calls: Cell<bool>,
98     pub rustc_diagnostic_macros: Cell<bool>
99 }
100
101 impl Features {
102     pub fn new() -> Features {
103         Features {
104             default_type_params: Cell::new(false),
105             issue_5723_bootstrap: Cell::new(false),
106             overloaded_calls: Cell::new(false),
107             rustc_diagnostic_macros: Cell::new(false)
108         }
109     }
110 }
111
112 struct Context<'a> {
113     features: Vec<&'static str>,
114     sess: &'a Session,
115 }
116
117 impl<'a> Context<'a> {
118     fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
119         if !self.has_feature(feature) {
120             self.sess.span_err(span, explain);
121             self.sess.span_note(span, format!("add #![feature({})] to the \
122                                                crate attributes to enable",
123                                               feature).as_slice());
124         }
125     }
126
127     fn gate_box(&self, span: Span) {
128         self.gate_feature("managed_boxes", span,
129                           "The managed box syntax is being replaced by the \
130                            `std::gc::Gc` and `std::rc::Rc` types. Equivalent \
131                            functionality to managed trait objects will be \
132                            implemented but is currently missing.");
133     }
134
135     fn has_feature(&self, feature: &str) -> bool {
136         self.features.iter().any(|n| n.as_slice() == feature)
137     }
138 }
139
140 impl<'a> Visitor<()> for Context<'a> {
141     fn visit_ident(&mut self, sp: Span, id: ast::Ident, _: ()) {
142         if !token::get_ident(id).get().is_ascii() {
143             self.gate_feature("non_ascii_idents", sp,
144                               "non-ascii idents are not fully supported.");
145         }
146     }
147
148     fn visit_view_item(&mut self, i: &ast::ViewItem, _: ()) {
149         match i.node {
150             ast::ViewItemUse(ref path) => {
151                 match path.node {
152                     ast::ViewPathGlob(..) => {
153                         self.gate_feature("globs", path.span,
154                                           "glob import statements are \
155                                            experimental and possibly buggy");
156                     }
157                     _ => {}
158                 }
159             }
160             ast::ViewItemExternCrate(..) => {
161                 for attr in i.attrs.iter() {
162                     if attr.name().get() == "phase"{
163                         self.gate_feature("phase", attr.span,
164                                           "compile time crate loading is \
165                                            experimental and possibly buggy");
166                     }
167                 }
168             }
169         }
170         visit::walk_view_item(self, i, ())
171     }
172
173     fn visit_item(&mut self, i: &ast::Item, _:()) {
174         for attr in i.attrs.iter() {
175             if attr.name().equiv(&("thread_local")) {
176                 self.gate_feature("thread_local", i.span,
177                                   "`#[thread_local]` is an experimental feature, and does not \
178                                   currently handle destructors. There is no corresponding \
179                                   `#[task_local]` mapping to the task model");
180             }
181         }
182         match i.node {
183             ast::ItemEnum(ref def, _) => {
184                 for variant in def.variants.iter() {
185                     match variant.node.kind {
186                         ast::StructVariantKind(..) => {
187                             self.gate_feature("struct_variant", variant.span,
188                                               "enum struct variants are \
189                                                experimental and possibly buggy");
190                         }
191                         _ => {}
192                     }
193                 }
194             }
195
196             ast::ItemForeignMod(ref foreign_module) => {
197                 if attr::contains_name(i.attrs.as_slice(), "link_args") {
198                     self.gate_feature("link_args", i.span,
199                                       "the `link_args` attribute is not portable \
200                                        across platforms, it is recommended to \
201                                        use `#[link(name = \"foo\")]` instead")
202                 }
203                 if foreign_module.abi == RustIntrinsic {
204                     self.gate_feature("intrinsics",
205                                       i.span,
206                                       "intrinsics are subject to change")
207                 }
208             }
209
210             ast::ItemFn(..) => {
211                 if attr::contains_name(i.attrs.as_slice(), "plugin_registrar") {
212                     self.gate_feature("plugin_registrar", i.span,
213                                       "compiler plugins are experimental and possibly buggy");
214                 }
215             }
216
217             ast::ItemStruct(struct_definition, _) => {
218                 if attr::contains_name(i.attrs.as_slice(), "simd") {
219                     self.gate_feature("simd", i.span,
220                                       "SIMD types are experimental and possibly buggy");
221                 }
222                 match struct_definition.super_struct {
223                     Some(ref path) => self.gate_feature("struct_inherit", path.span,
224                                                         "struct inheritance is experimental \
225                                                          and possibly buggy"),
226                     None => {}
227                 }
228                 if struct_definition.is_virtual {
229                     self.gate_feature("struct_inherit", i.span,
230                                       "struct inheritance (`virtual` keyword) is \
231                                        experimental and possibly buggy");
232                 }
233             }
234
235             ast::ItemImpl(..) => {
236                 if attr::contains_name(i.attrs.as_slice(),
237                                        "unsafe_destructor") {
238                     self.gate_feature("unsafe_destructor",
239                                       i.span,
240                                       "`#[unsafe_destructor]` allows too \
241                                        many unsafe patterns and may be \
242                                        removed in the future");
243                 }
244             }
245
246             _ => {}
247         }
248
249         visit::walk_item(self, i, ());
250     }
251
252     fn visit_mac(&mut self, macro: &ast::Mac, _: ()) {
253         let ast::MacInvocTT(ref path, _, _) = macro.node;
254         let id = path.segments.last().unwrap().identifier;
255         let quotes = ["quote_tokens", "quote_expr", "quote_ty",
256                       "quote_item", "quote_pat", "quote_stmt"];
257         let msg = " is not stable enough for use and are subject to change";
258
259
260         if id == token::str_to_ident("macro_rules") {
261             self.gate_feature("macro_rules", path.span, "macro definitions are \
262                 not stable enough for use and are subject to change");
263         }
264
265         else if id == token::str_to_ident("asm") {
266             self.gate_feature("asm", path.span, "inline assembly is not \
267                 stable enough for use and is subject to change");
268         }
269
270         else if id == token::str_to_ident("log_syntax") {
271             self.gate_feature("log_syntax", path.span, "`log_syntax!` is not \
272                 stable enough for use and is subject to change");
273         }
274
275         else if id == token::str_to_ident("trace_macros") {
276             self.gate_feature("trace_macros", path.span, "`trace_macros` is not \
277                 stable enough for use and is subject to change");
278         }
279
280         else if id == token::str_to_ident("concat_idents") {
281             self.gate_feature("concat_idents", path.span, "`concat_idents` is not \
282                 stable enough for use and is subject to change");
283         }
284
285         else {
286             for &quote in quotes.iter() {
287                 if id == token::str_to_ident(quote) {
288                   self.gate_feature("quote",
289                                     path.span,
290                                     format!("{}{}", quote, msg).as_slice());
291                 }
292             }
293         }
294     }
295
296     fn visit_foreign_item(&mut self, i: &ast::ForeignItem, _: ()) {
297         if attr::contains_name(i.attrs.as_slice(), "linkage") {
298             self.gate_feature("linkage", i.span,
299                               "the `linkage` attribute is experimental \
300                                and not portable across platforms")
301         }
302         visit::walk_foreign_item(self, i, ())
303     }
304
305     fn visit_ty(&mut self, t: &ast::Ty, _: ()) {
306         match t.node {
307             ast::TyClosure(closure, _) if closure.onceness == ast::Once => {
308                 self.gate_feature("once_fns", t.span,
309                                   "once functions are \
310                                    experimental and likely to be removed");
311
312             },
313             ast::TyBox(_) => { self.gate_box(t.span); }
314             ast::TyUnboxedFn(_) => {
315                 self.gate_feature("unboxed_closure_sugar",
316                                   t.span,
317                                   "unboxed closure trait sugar is experimental");
318             }
319             _ => {}
320         }
321
322         visit::walk_ty(self, t, ());
323     }
324
325     fn visit_expr(&mut self, e: &ast::Expr, _: ()) {
326         match e.node {
327             ast::ExprUnary(ast::UnBox, _) => {
328                 self.gate_box(e.span);
329             }
330             _ => {}
331         }
332         visit::walk_expr(self, e, ());
333     }
334
335     fn visit_generics(&mut self, generics: &ast::Generics, _: ()) {
336         for type_parameter in generics.ty_params.iter() {
337             match type_parameter.default {
338                 Some(ty) => {
339                     self.gate_feature("default_type_params", ty.span,
340                                       "default type parameters are \
341                                        experimental and possibly buggy");
342                 }
343                 None => {}
344             }
345         }
346         visit::walk_generics(self, generics, ());
347     }
348
349     fn visit_attribute(&mut self, attr: &ast::Attribute, _: ()) {
350         if attr::contains_name([*attr], "lang") {
351             self.gate_feature("lang_items",
352                               attr.span,
353                               "language items are subject to change");
354         }
355     }
356
357     fn visit_fn(&mut self,
358                 fn_kind: &visit::FnKind,
359                 fn_decl: &ast::FnDecl,
360                 block: &ast::Block,
361                 span: Span,
362                 _: NodeId,
363                 (): ()) {
364         match *fn_kind {
365             visit::FkItemFn(_, _, _, ref abi) if *abi == RustIntrinsic => {
366                 self.gate_feature("intrinsics",
367                                   span,
368                                   "intrinsics are subject to change")
369             }
370             _ => {}
371         }
372         visit::walk_fn(self, fn_kind, fn_decl, block, span, ());
373     }
374 }
375
376 pub fn check_crate(sess: &Session, krate: &ast::Crate) {
377     let mut cx = Context {
378         features: Vec::new(),
379         sess: sess,
380     };
381
382     for attr in krate.attrs.iter() {
383         if !attr.check_name("feature") {
384             continue
385         }
386
387         match attr.meta_item_list() {
388             None => {
389                 sess.span_err(attr.span, "malformed feature attribute, \
390                                           expected #![feature(...)]");
391             }
392             Some(list) => {
393                 for &mi in list.iter() {
394                     let name = match mi.node {
395                         ast::MetaWord(ref word) => (*word).clone(),
396                         _ => {
397                             sess.span_err(mi.span,
398                                           "malformed feature, expected just \
399                                            one word");
400                             continue
401                         }
402                     };
403                     match KNOWN_FEATURES.iter()
404                                         .find(|& &(n, _)| name.equiv(&n)) {
405                         Some(&(name, Active)) => { cx.features.push(name); }
406                         Some(&(_, Removed)) => {
407                             sess.span_err(mi.span, "feature has been removed");
408                         }
409                         Some(&(_, Accepted)) => {
410                             sess.span_warn(mi.span, "feature has added to rust, \
411                                                      directive not necessary");
412                         }
413                         None => {
414                             sess.add_lint(lint::builtin::UNKNOWN_FEATURES,
415                                           ast::CRATE_NODE_ID,
416                                           mi.span,
417                                           "unknown feature".to_string());
418                         }
419                     }
420                 }
421             }
422         }
423     }
424
425     visit::walk_crate(&mut cx, krate, ());
426
427     sess.abort_if_errors();
428
429     sess.features.default_type_params.set(cx.has_feature("default_type_params"));
430     sess.features.issue_5723_bootstrap.set(cx.has_feature("issue_5723_bootstrap"));
431     sess.features.overloaded_calls.set(cx.has_feature("overloaded_calls"));
432     sess.features.rustc_diagnostic_macros.set(cx.has_feature("rustc_diagnostic_macros"));
433 }