]> git.lizzy.rs Git - rust.git/blob - src/librustc/front/feature_gate.rs
Register new snapshots
[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 middle::lint;
22
23 use syntax::ast;
24 use syntax::attr;
25 use syntax::attr::AttrMetaMethods;
26 use syntax::codemap::Span;
27 use syntax::visit;
28 use syntax::visit::Visitor;
29 use syntax::parse::token;
30
31 use driver::session::Session;
32
33 /// This is a list of all known features since the beginning of time. This list
34 /// can never shrink, it may only be expanded (in order to prevent old programs
35 /// from failing to compile). The status of each feature may change, however.
36 static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
37     ("globs", Active),
38     ("macro_rules", Active),
39     ("struct_variant", Active),
40     ("once_fns", Active),
41     ("asm", Active),
42     ("managed_boxes", Active),
43     ("non_ascii_idents", Active),
44     ("thread_local", Active),
45     ("link_args", Active),
46
47     // These are used to test this portion of the compiler, they don't actually
48     // mean anything
49     ("test_accepted_feature", Accepted),
50     ("test_removed_feature", Removed),
51 ];
52
53 enum Status {
54     /// Represents an active feature that is currently being implemented or
55     /// currently being considered for addition/removal.
56     Active,
57
58     /// Represents a feature which has since been removed (it was once Active)
59     Removed,
60
61     /// This language feature has since been Accepted (it was once Active)
62     Accepted,
63 }
64
65 struct Context {
66     features: ~[&'static str],
67     sess: Session,
68 }
69
70 impl Context {
71     fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
72         if !self.has_feature(feature) {
73             self.sess.span_err(span, explain);
74             self.sess.span_note(span, format!("add \\#[feature({})] to the \
75                                                   crate attributes to enable",
76                                                  feature));
77         }
78     }
79
80     fn has_feature(&self, feature: &str) -> bool {
81         self.features.iter().any(|n| n.as_slice() == feature)
82     }
83 }
84
85 impl Visitor<()> for Context {
86     fn visit_ident(&mut self, sp: Span, id: ast::Ident, _: ()) {
87         let s = token::ident_to_str(&id);
88
89         if !s.is_ascii() {
90             self.gate_feature("non_ascii_idents", sp,
91                               "non-ascii idents are not fully supported.");
92         }
93     }
94
95     fn visit_view_item(&mut self, i: &ast::view_item, _: ()) {
96         match i.node {
97             ast::view_item_use(ref paths) => {
98                 for path in paths.iter() {
99                     match path.node {
100                         ast::view_path_glob(..) => {
101                             self.gate_feature("globs", path.span,
102                                               "glob import statements are \
103                                                experimental and possibly buggy");
104                         }
105                         _ => {}
106                     }
107                 }
108             }
109             _ => {}
110         }
111         visit::walk_view_item(self, i, ())
112     }
113
114     fn visit_item(&mut self, i: @ast::item, _:()) {
115         for attr in i.attrs.iter() {
116             if "thread_local" == attr.name() {
117                 self.gate_feature("thread_local", i.span,
118                                   "`#[thread_local]` is an experimental feature, and does not \
119                                   currently handle destructors. There is no corresponding \
120                                   `#[task_local]` mapping to the task model");
121             }
122         }
123         match i.node {
124             ast::item_enum(ref def, _) => {
125                 for variant in def.variants.iter() {
126                     match variant.node.kind {
127                         ast::struct_variant_kind(..) => {
128                             self.gate_feature("struct_variant", variant.span,
129                                               "enum struct variants are \
130                                                experimental and possibly buggy");
131                         }
132                         _ => {}
133                     }
134                 }
135             }
136
137             ast::item_foreign_mod(..) => {
138                 if attr::contains_name(i.attrs, "link_args") {
139                     self.gate_feature("link_args", i.span,
140                                       "the `link_args` attribute is not portable \
141                                        across platforms, it is recommended to \
142                                        use `#[link(name = \"foo\")]` instead")
143                 }
144             }
145
146             _ => {}
147         }
148
149         visit::walk_item(self, i, ());
150     }
151
152     fn visit_mac(&mut self, macro: &ast::mac, _: ()) {
153         let ast::mac_invoc_tt(ref path, _, _) = macro.node;
154
155         if path.segments.last().identifier == self.sess.ident_of("macro_rules") {
156             self.gate_feature("macro_rules", path.span, "macro definitions are \
157                 not stable enough for use and are subject to change");
158         }
159
160         else if path.segments.last().identifier == self.sess.ident_of("asm") {
161             self.gate_feature("asm", path.span, "inline assembly is not \
162                 stable enough for use and is subject to change");
163         }
164     }
165
166     fn visit_ty(&mut self, t: &ast::Ty, _: ()) {
167         match t.node {
168             ast::ty_closure(closure) if closure.onceness == ast::Once &&
169                     closure.sigil != ast::OwnedSigil => {
170                 self.gate_feature("once_fns", t.span,
171                                   "once functions are \
172                                    experimental and likely to be removed");
173
174             },
175             ast::ty_box(_) => {
176                 self.gate_feature("managed_boxes", t.span,
177                                   "The managed box syntax is being replaced by the `std::gc::Gc` \
178                                   and `std::rc::Rc` types. Equivalent functionality to managed \
179                                   trait objects will be implemented but is currently missing.");
180             }
181             _ => {}
182         }
183
184         visit::walk_ty(self, t, ());
185     }
186 }
187
188 pub fn check_crate(sess: Session, crate: &ast::Crate) {
189     let mut cx = Context {
190         features: ~[],
191         sess: sess,
192     };
193
194     for attr in crate.attrs.iter() {
195         if "feature" != attr.name() { continue }
196
197         match attr.meta_item_list() {
198             None => {
199                 sess.span_err(attr.span, "malformed feature attribute, \
200                                           expected #[feature(...)]");
201             }
202             Some(list) => {
203                 for &mi in list.iter() {
204                     let name = match mi.node {
205                         ast::MetaWord(word) => word,
206                         _ => {
207                             sess.span_err(mi.span, "malformed feature, expected \
208                                                     just one word");
209                             continue
210                         }
211                     };
212                     match KNOWN_FEATURES.iter().find(|& &(n, _)| n == name) {
213                         Some(&(name, Active)) => { cx.features.push(name); }
214                         Some(&(_, Removed)) => {
215                             sess.span_err(mi.span, "feature has been removed");
216                         }
217                         Some(&(_, Accepted)) => {
218                             sess.span_warn(mi.span, "feature has added to rust, \
219                                                      directive not necessary");
220                         }
221                         None => {
222                             sess.add_lint(lint::unknown_features,
223                                           ast::CRATE_NODE_ID,
224                                           mi.span,
225                                           ~"unknown feature");
226                         }
227                     }
228                 }
229             }
230         }
231     }
232
233     visit::walk_crate(&mut cx, crate, ());
234
235     sess.abort_if_errors();
236 }