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.
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.
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
18 //! Features are enabled in programs via the crate-level attributes of
19 //! #[feature(...)] with a comma-separated list of features.
25 use syntax::attr::AttrMetaMethods;
26 use syntax::codemap::Span;
28 use syntax::visit::Visitor;
29 use syntax::parse::token;
31 use driver::session::Session;
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)] = &[
38 ("macro_rules", Active),
39 ("struct_variant", Active),
42 ("managed_boxes", Active),
43 ("non_ascii_idents", Active),
44 ("thread_local", Active),
45 ("link_args", Active),
47 // These are used to test this portion of the compiler, they don't actually
49 ("test_accepted_feature", Accepted),
50 ("test_removed_feature", Removed),
54 /// Represents an active feature that is currently being implemented or
55 /// currently being considered for addition/removal.
58 /// Represents a feature which has since been removed (it was once Active)
61 /// This language feature has since been Accepted (it was once Active)
66 features: ~[&'static str],
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",
80 fn has_feature(&self, feature: &str) -> bool {
81 self.features.iter().any(|n| n.as_slice() == feature)
85 impl Visitor<()> for Context {
86 fn visit_ident(&mut self, sp: Span, id: ast::Ident, _: ()) {
87 let s = token::ident_to_str(&id);
90 self.gate_feature("non_ascii_idents", sp,
91 "non-ascii idents are not fully supported.");
95 fn visit_view_item(&mut self, i: &ast::view_item, _: ()) {
97 ast::view_item_use(ref paths) => {
98 for path in paths.iter() {
100 ast::view_path_glob(..) => {
101 self.gate_feature("globs", path.span,
102 "glob import statements are \
103 experimental and possibly buggy");
111 visit::walk_view_item(self, i, ())
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");
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");
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")
149 visit::walk_item(self, i, ());
152 fn visit_mac(&mut self, macro: &ast::mac, _: ()) {
153 let ast::mac_invoc_tt(ref path, _, _) = macro.node;
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");
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");
166 fn visit_ty(&mut self, t: &ast::Ty, _: ()) {
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");
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.");
184 visit::walk_ty(self, t, ());
188 pub fn check_crate(sess: Session, crate: &ast::Crate) {
189 let mut cx = Context {
194 for attr in crate.attrs.iter() {
195 if "feature" != attr.name() { continue }
197 match attr.meta_item_list() {
199 sess.span_err(attr.span, "malformed feature attribute, \
200 expected #[feature(...)]");
203 for &mi in list.iter() {
204 let name = match mi.node {
205 ast::MetaWord(word) => word,
207 sess.span_err(mi.span, "malformed feature, expected \
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");
217 Some(&(_, Accepted)) => {
218 sess.span_warn(mi.span, "feature has added to rust, \
219 directive not necessary");
222 sess.add_lint(lint::unknown_features,
233 visit::walk_crate(&mut cx, crate, ());
235 sess.abort_if_errors();