]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/feature_gate.rs
auto merge of #17654 : gereeter/rust/no-unnecessary-cell, r=alexcrichton
[rust.git] / src / libsyntax / 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 abi::RustIntrinsic;
22 use ast::NodeId;
23 use ast;
24 use attr;
25 use attr::AttrMetaMethods;
26 use codemap::Span;
27 use diagnostic::SpanHandler;
28 use visit;
29 use visit::Visitor;
30 use parse::token;
31
32 use std::slice;
33
34 /// This is a list of all known features since the beginning of time. This list
35 /// can never shrink, it may only be expanded (in order to prevent old programs
36 /// from failing to compile). The status of each feature may change, however.
37 static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
38     ("globs", Active),
39     ("macro_rules", Active),
40     ("struct_variant", Active),
41     ("once_fns", Active),
42     ("asm", Active),
43     ("managed_boxes", Active),
44     ("non_ascii_idents", Active),
45     ("thread_local", Active),
46     ("link_args", Active),
47     ("phase", Active),
48     ("plugin_registrar", Active),
49     ("log_syntax", Active),
50     ("trace_macros", Active),
51     ("concat_idents", Active),
52     ("unsafe_destructor", Active),
53     ("intrinsics", Active),
54     ("lang_items", Active),
55
56     ("simd", Active),
57     ("default_type_params", Active),
58     ("quote", Active),
59     ("linkage", Active),
60     ("struct_inherit", Active),
61     ("overloaded_calls", Active),
62     ("unboxed_closure_sugar", Active),
63
64     ("quad_precision_float", Removed),
65
66     ("rustc_diagnostic_macros", Active),
67     ("unboxed_closures", Active),
68     ("import_shadowing", Active),
69     ("advanced_slice_patterns", Active),
70     ("tuple_indexing", Active),
71     ("associated_types", Active),
72     ("visible_private_types", Active),
73
74     ("if_let", Active),
75
76     // if you change this list without updating src/doc/rust.md, cmr will be sad
77
78     // A temporary feature gate used to enable parser extensions needed
79     // to bootstrap fix for #5723.
80     ("issue_5723_bootstrap", Accepted),
81
82     // These are used to test this portion of the compiler, they don't actually
83     // mean anything
84     ("test_accepted_feature", Accepted),
85     ("test_removed_feature", Removed),
86 ];
87
88 enum Status {
89     /// Represents an active feature that is currently being implemented or
90     /// currently being considered for addition/removal.
91     Active,
92
93     /// Represents a feature which has since been removed (it was once Active)
94     Removed,
95
96     /// This language feature has since been Accepted (it was once Active)
97     Accepted,
98 }
99
100 /// A set of features to be used by later passes.
101 pub struct Features {
102     pub default_type_params: bool,
103     pub overloaded_calls: bool,
104     pub rustc_diagnostic_macros: bool,
105     pub import_shadowing: bool,
106     pub visible_private_types: bool,
107     pub quote: bool,
108 }
109
110 impl Features {
111     pub fn new() -> Features {
112         Features {
113             default_type_params: false,
114             overloaded_calls: false,
115             rustc_diagnostic_macros: false,
116             import_shadowing: false,
117             visible_private_types: false,
118             quote: false,
119         }
120     }
121 }
122
123 struct Context<'a> {
124     features: Vec<&'static str>,
125     span_handler: &'a SpanHandler,
126 }
127
128 impl<'a> Context<'a> {
129     fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
130         if !self.has_feature(feature) {
131             self.span_handler.span_err(span, explain);
132             self.span_handler.span_note(span, format!("add #![feature({})] to the \
133                                                        crate attributes to enable",
134                                                       feature).as_slice());
135         }
136     }
137
138     fn gate_box(&self, span: Span) {
139         self.gate_feature("managed_boxes", span,
140                           "The managed box syntax is being replaced by the \
141                            `std::gc::Gc` and `std::rc::Rc` types. Equivalent \
142                            functionality to managed trait objects will be \
143                            implemented but is currently missing.");
144     }
145
146     fn has_feature(&self, feature: &str) -> bool {
147         self.features.iter().any(|n| n.as_slice() == feature)
148     }
149 }
150
151 impl<'a, 'v> Visitor<'v> for Context<'a> {
152     fn visit_ident(&mut self, sp: Span, id: ast::Ident) {
153         if !token::get_ident(id).get().is_ascii() {
154             self.gate_feature("non_ascii_idents", sp,
155                               "non-ascii idents are not fully supported.");
156         }
157     }
158
159     fn visit_view_item(&mut self, i: &ast::ViewItem) {
160         match i.node {
161             ast::ViewItemUse(ref path) => {
162                 match path.node {
163                     ast::ViewPathGlob(..) => {
164                         self.gate_feature("globs", path.span,
165                                           "glob import statements are \
166                                            experimental and possibly buggy");
167                     }
168                     _ => {}
169                 }
170             }
171             ast::ViewItemExternCrate(..) => {
172                 for attr in i.attrs.iter() {
173                     if attr.name().get() == "phase"{
174                         self.gate_feature("phase", attr.span,
175                                           "compile time crate loading is \
176                                            experimental and possibly buggy");
177                     }
178                 }
179             }
180         }
181         visit::walk_view_item(self, i)
182     }
183
184     fn visit_item(&mut self, i: &ast::Item) {
185         for attr in i.attrs.iter() {
186             if attr.name().equiv(&("thread_local")) {
187                 self.gate_feature("thread_local", i.span,
188                                   "`#[thread_local]` is an experimental feature, and does not \
189                                   currently handle destructors. There is no corresponding \
190                                   `#[task_local]` mapping to the task model");
191             }
192         }
193         match i.node {
194             ast::ItemEnum(ref def, _) => {
195                 for variant in def.variants.iter() {
196                     match variant.node.kind {
197                         ast::StructVariantKind(..) => {
198                             self.gate_feature("struct_variant", variant.span,
199                                               "enum struct variants are \
200                                                experimental and possibly buggy");
201                         }
202                         _ => {}
203                     }
204                 }
205             }
206
207             ast::ItemForeignMod(ref foreign_module) => {
208                 if attr::contains_name(i.attrs.as_slice(), "link_args") {
209                     self.gate_feature("link_args", i.span,
210                                       "the `link_args` attribute is not portable \
211                                        across platforms, it is recommended to \
212                                        use `#[link(name = \"foo\")]` instead")
213                 }
214                 if foreign_module.abi == RustIntrinsic {
215                     self.gate_feature("intrinsics",
216                                       i.span,
217                                       "intrinsics are subject to change")
218                 }
219             }
220
221             ast::ItemFn(..) => {
222                 if attr::contains_name(i.attrs.as_slice(), "plugin_registrar") {
223                     self.gate_feature("plugin_registrar", i.span,
224                                       "compiler plugins are experimental and possibly buggy");
225                 }
226             }
227
228             ast::ItemStruct(ref struct_definition, _) => {
229                 if attr::contains_name(i.attrs.as_slice(), "simd") {
230                     self.gate_feature("simd", i.span,
231                                       "SIMD types are experimental and possibly buggy");
232                 }
233                 match struct_definition.super_struct {
234                     Some(ref path) => self.gate_feature("struct_inherit", path.span,
235                                                         "struct inheritance is experimental \
236                                                          and possibly buggy"),
237                     None => {}
238                 }
239                 if struct_definition.is_virtual {
240                     self.gate_feature("struct_inherit", i.span,
241                                       "struct inheritance (`virtual` keyword) is \
242                                        experimental and possibly buggy");
243                 }
244             }
245
246             ast::ItemImpl(_, _, _, ref items) => {
247                 if attr::contains_name(i.attrs.as_slice(),
248                                        "unsafe_destructor") {
249                     self.gate_feature("unsafe_destructor",
250                                       i.span,
251                                       "`#[unsafe_destructor]` allows too \
252                                        many unsafe patterns and may be \
253                                        removed in the future");
254                 }
255
256                 for item in items.iter() {
257                     match *item {
258                         ast::MethodImplItem(_) => {}
259                         ast::TypeImplItem(ref typedef) => {
260                             self.gate_feature("associated_types",
261                                               typedef.span,
262                                               "associated types are \
263                                                experimental")
264                         }
265                     }
266                 }
267             }
268
269             _ => {}
270         }
271
272         visit::walk_item(self, i);
273     }
274
275     fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) {
276         match *trait_item {
277             ast::RequiredMethod(_) | ast::ProvidedMethod(_) => {}
278             ast::TypeTraitItem(ref ti) => {
279                 self.gate_feature("associated_types",
280                                   ti.span,
281                                   "associated types are experimental")
282             }
283         }
284     }
285
286     fn visit_mac(&mut self, macro: &ast::Mac) {
287         let ast::MacInvocTT(ref path, _, _) = macro.node;
288         let id = path.segments.last().unwrap().identifier;
289
290         if id == token::str_to_ident("macro_rules") {
291             self.gate_feature("macro_rules", path.span, "macro definitions are \
292                 not stable enough for use and are subject to change");
293         }
294
295         else if id == token::str_to_ident("asm") {
296             self.gate_feature("asm", path.span, "inline assembly is not \
297                 stable enough for use and is subject to change");
298         }
299
300         else if id == token::str_to_ident("log_syntax") {
301             self.gate_feature("log_syntax", path.span, "`log_syntax!` is not \
302                 stable enough for use and is subject to change");
303         }
304
305         else if id == token::str_to_ident("trace_macros") {
306             self.gate_feature("trace_macros", path.span, "`trace_macros` is not \
307                 stable enough for use and is subject to change");
308         }
309
310         else if id == token::str_to_ident("concat_idents") {
311             self.gate_feature("concat_idents", path.span, "`concat_idents` is not \
312                 stable enough for use and is subject to change");
313         }
314     }
315
316     fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
317         if attr::contains_name(i.attrs.as_slice(), "linkage") {
318             self.gate_feature("linkage", i.span,
319                               "the `linkage` attribute is experimental \
320                                and not portable across platforms")
321         }
322         visit::walk_foreign_item(self, i)
323     }
324
325     fn visit_ty(&mut self, t: &ast::Ty) {
326         match t.node {
327             ast::TyClosure(ref closure) if closure.onceness == ast::Once => {
328                 self.gate_feature("once_fns", t.span,
329                                   "once functions are \
330                                    experimental and likely to be removed");
331
332             },
333             ast::TyBox(_) => { self.gate_box(t.span); }
334             ast::TyUnboxedFn(..) => {
335                 self.gate_feature("unboxed_closure_sugar",
336                                   t.span,
337                                   "unboxed closure trait sugar is experimental");
338             }
339             _ => {}
340         }
341
342         visit::walk_ty(self, t);
343     }
344
345     fn visit_expr(&mut self, e: &ast::Expr) {
346         match e.node {
347             ast::ExprUnary(ast::UnBox, _) => {
348                 self.gate_box(e.span);
349             }
350             ast::ExprUnboxedFn(..) => {
351                 self.gate_feature("unboxed_closures",
352                                   e.span,
353                                   "unboxed closures are a work-in-progress \
354                                    feature with known bugs");
355             }
356             ast::ExprTupField(..) => {
357                 self.gate_feature("tuple_indexing",
358                                   e.span,
359                                   "tuple indexing is experimental");
360             }
361             ast::ExprIfLet(..) => {
362                 self.gate_feature("if_let", e.span,
363                                   "`if let` syntax is experimental");
364             }
365             _ => {}
366         }
367         visit::walk_expr(self, e);
368     }
369
370     fn visit_generics(&mut self, generics: &ast::Generics) {
371         for type_parameter in generics.ty_params.iter() {
372             match type_parameter.default {
373                 Some(ref ty) => {
374                     self.gate_feature("default_type_params", ty.span,
375                                       "default type parameters are \
376                                        experimental and possibly buggy");
377                 }
378                 None => {}
379             }
380         }
381         visit::walk_generics(self, generics);
382     }
383
384     fn visit_attribute(&mut self, attr: &ast::Attribute) {
385         if attr::contains_name(slice::ref_slice(attr), "lang") {
386             self.gate_feature("lang_items",
387                               attr.span,
388                               "language items are subject to change");
389         }
390     }
391
392     fn visit_pat(&mut self, pattern: &ast::Pat) {
393         match pattern.node {
394             ast::PatVec(_, Some(_), ref last) if !last.is_empty() => {
395                 self.gate_feature("advanced_slice_patterns",
396                                   pattern.span,
397                                   "multiple-element slice matches anywhere \
398                                    but at the end of a slice (e.g. \
399                                    `[0, ..xs, 0]` are experimental")
400             }
401             _ => {}
402         }
403         visit::walk_pat(self, pattern)
404     }
405
406     fn visit_fn(&mut self,
407                 fn_kind: visit::FnKind<'v>,
408                 fn_decl: &'v ast::FnDecl,
409                 block: &'v ast::Block,
410                 span: Span,
411                 _: NodeId) {
412         match fn_kind {
413             visit::FkItemFn(_, _, _, abi) if abi == RustIntrinsic => {
414                 self.gate_feature("intrinsics",
415                                   span,
416                                   "intrinsics are subject to change")
417             }
418             _ => {}
419         }
420         visit::walk_fn(self, fn_kind, fn_decl, block, span);
421     }
422 }
423
424 pub fn check_crate(span_handler: &SpanHandler, krate: &ast::Crate) -> (Features, Vec<Span>) {
425     let mut cx = Context {
426         features: Vec::new(),
427         span_handler: span_handler,
428     };
429
430     let mut unknown_features = Vec::new();
431
432     for attr in krate.attrs.iter() {
433         if !attr.check_name("feature") {
434             continue
435         }
436
437         match attr.meta_item_list() {
438             None => {
439                 span_handler.span_err(attr.span, "malformed feature attribute, \
440                                                   expected #![feature(...)]");
441             }
442             Some(list) => {
443                 for mi in list.iter() {
444                     let name = match mi.node {
445                         ast::MetaWord(ref word) => (*word).clone(),
446                         _ => {
447                             span_handler.span_err(mi.span,
448                                                   "malformed feature, expected just \
449                                                    one word");
450                             continue
451                         }
452                     };
453                     match KNOWN_FEATURES.iter()
454                                         .find(|& &(n, _)| name.equiv(&n)) {
455                         Some(&(name, Active)) => { cx.features.push(name); }
456                         Some(&(_, Removed)) => {
457                             span_handler.span_err(mi.span, "feature has been removed");
458                         }
459                         Some(&(_, Accepted)) => {
460                             span_handler.span_warn(mi.span, "feature has been added to Rust, \
461                                                              directive not necessary");
462                         }
463                         None => {
464                             unknown_features.push(mi.span);
465                         }
466                     }
467                 }
468             }
469         }
470     }
471
472     visit::walk_crate(&mut cx, krate);
473
474     (Features {
475         default_type_params: cx.has_feature("default_type_params"),
476         overloaded_calls: cx.has_feature("overloaded_calls"),
477         rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
478         import_shadowing: cx.has_feature("import_shadowing"),
479         visible_private_types: cx.has_feature("visible_private_types"),
480         quote: cx.has_feature("quote"),
481     },
482     unknown_features)
483 }
484