]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/config.rs
Rollup merge of #24675 - steveklabnik:two_more_chapters, r=alexcrichton
[rust.git] / src / libsyntax / config.rs
1 // Copyright 2012-2014 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 use attr::AttrMetaMethods;
12 use diagnostic::SpanHandler;
13 use fold::Folder;
14 use {ast, fold, attr};
15 use codemap::{Spanned, respan};
16 use ptr::P;
17
18 use util::small_vector::SmallVector;
19
20 /// A folder that strips out items that do not belong in the current
21 /// configuration.
22 struct Context<F> where F: FnMut(&[ast::Attribute]) -> bool {
23     in_cfg: F,
24 }
25
26 // Support conditional compilation by transforming the AST, stripping out
27 // any items that do not belong in the current configuration
28 pub fn strip_unconfigured_items(diagnostic: &SpanHandler, krate: ast::Crate) -> ast::Crate {
29     let krate = process_cfg_attr(diagnostic, krate);
30     let config = krate.config.clone();
31     strip_items(krate, |attrs| in_cfg(diagnostic, &config, attrs))
32 }
33
34 impl<F> fold::Folder for Context<F> where F: FnMut(&[ast::Attribute]) -> bool {
35     fn fold_mod(&mut self, module: ast::Mod) -> ast::Mod {
36         fold_mod(self, module)
37     }
38     fn fold_block(&mut self, block: P<ast::Block>) -> P<ast::Block> {
39         fold_block(self, block)
40     }
41     fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
42         fold_foreign_mod(self, foreign_mod)
43     }
44     fn fold_item_underscore(&mut self, item: ast::Item_) -> ast::Item_ {
45         fold_item_underscore(self, item)
46     }
47     fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
48         fold_expr(self, expr)
49     }
50     fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
51         fold::noop_fold_mac(mac, self)
52     }
53     fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
54         fold_item(self, item)
55     }
56 }
57
58 pub fn strip_items<F>(krate: ast::Crate, in_cfg: F) -> ast::Crate where
59     F: FnMut(&[ast::Attribute]) -> bool,
60 {
61     let mut ctxt = Context {
62         in_cfg: in_cfg,
63     };
64     ctxt.fold_crate(krate)
65 }
66
67 fn fold_mod<F>(cx: &mut Context<F>,
68                ast::Mod {inner, items}: ast::Mod)
69                -> ast::Mod where
70     F: FnMut(&[ast::Attribute]) -> bool
71 {
72     ast::Mod {
73         inner: inner,
74         items: items.into_iter().flat_map(|a| {
75             cx.fold_item(a).into_iter()
76         }).collect()
77     }
78 }
79
80 fn filter_foreign_item<F>(cx: &mut Context<F>,
81                           item: P<ast::ForeignItem>)
82                           -> Option<P<ast::ForeignItem>> where
83     F: FnMut(&[ast::Attribute]) -> bool
84 {
85     if foreign_item_in_cfg(cx, &*item) {
86         Some(item)
87     } else {
88         None
89     }
90 }
91
92 fn fold_foreign_mod<F>(cx: &mut Context<F>,
93                        ast::ForeignMod {abi, items}: ast::ForeignMod)
94                        -> ast::ForeignMod where
95     F: FnMut(&[ast::Attribute]) -> bool
96 {
97     ast::ForeignMod {
98         abi: abi,
99         items: items.into_iter()
100                     .filter_map(|a| filter_foreign_item(cx, a))
101                     .collect()
102     }
103 }
104
105 fn fold_item<F>(cx: &mut Context<F>, item: P<ast::Item>) -> SmallVector<P<ast::Item>> where
106     F: FnMut(&[ast::Attribute]) -> bool
107 {
108     if item_in_cfg(cx, &*item) {
109         SmallVector::one(item.map(|i| cx.fold_item_simple(i)))
110     } else {
111         SmallVector::zero()
112     }
113 }
114
115 fn fold_item_underscore<F>(cx: &mut Context<F>, item: ast::Item_) -> ast::Item_ where
116     F: FnMut(&[ast::Attribute]) -> bool
117 {
118     let item = match item {
119         ast::ItemImpl(u, o, a, b, c, impl_items) => {
120             let impl_items = impl_items.into_iter()
121                                        .filter(|ii| (cx.in_cfg)(&ii.attrs))
122                                        .collect();
123             ast::ItemImpl(u, o, a, b, c, impl_items)
124         }
125         ast::ItemTrait(u, a, b, methods) => {
126             let methods = methods.into_iter()
127                                  .filter(|ti| (cx.in_cfg)(&ti.attrs))
128                                  .collect();
129             ast::ItemTrait(u, a, b, methods)
130         }
131         ast::ItemStruct(def, generics) => {
132             ast::ItemStruct(fold_struct(cx, def), generics)
133         }
134         ast::ItemEnum(def, generics) => {
135             let variants = def.variants.into_iter().filter_map(|v| {
136                 if !(cx.in_cfg)(&v.node.attrs) {
137                     None
138                 } else {
139                     Some(v.map(|Spanned {node: ast::Variant_ {id, name, attrs, kind,
140                                                               disr_expr, vis}, span}| {
141                         Spanned {
142                             node: ast::Variant_ {
143                                 id: id,
144                                 name: name,
145                                 attrs: attrs,
146                                 kind: match kind {
147                                     ast::TupleVariantKind(..) => kind,
148                                     ast::StructVariantKind(def) => {
149                                         ast::StructVariantKind(fold_struct(cx, def))
150                                     }
151                                 },
152                                 disr_expr: disr_expr,
153                                 vis: vis
154                             },
155                             span: span
156                         }
157                     }))
158                 }
159             });
160             ast::ItemEnum(ast::EnumDef {
161                 variants: variants.collect(),
162             }, generics)
163         }
164         item => item,
165     };
166
167     fold::noop_fold_item_underscore(item, cx)
168 }
169
170 fn fold_struct<F>(cx: &mut Context<F>, def: P<ast::StructDef>) -> P<ast::StructDef> where
171     F: FnMut(&[ast::Attribute]) -> bool
172 {
173     def.map(|ast::StructDef { fields, ctor_id }| {
174         ast::StructDef {
175             fields: fields.into_iter().filter(|m| {
176                 (cx.in_cfg)(&m.node.attrs)
177             }).collect(),
178             ctor_id: ctor_id,
179         }
180     })
181 }
182
183 fn retain_stmt<F>(cx: &mut Context<F>, stmt: &ast::Stmt) -> bool where
184     F: FnMut(&[ast::Attribute]) -> bool
185 {
186     match stmt.node {
187         ast::StmtDecl(ref decl, _) => {
188             match decl.node {
189                 ast::DeclItem(ref item) => {
190                     item_in_cfg(cx, &**item)
191                 }
192                 _ => true
193             }
194         }
195         _ => true
196     }
197 }
198
199 fn fold_block<F>(cx: &mut Context<F>, b: P<ast::Block>) -> P<ast::Block> where
200     F: FnMut(&[ast::Attribute]) -> bool
201 {
202     b.map(|ast::Block {id, stmts, expr, rules, span}| {
203         let resulting_stmts: Vec<P<ast::Stmt>> =
204             stmts.into_iter().filter(|a| retain_stmt(cx, &**a)).collect();
205         let resulting_stmts = resulting_stmts.into_iter()
206             .flat_map(|stmt| cx.fold_stmt(stmt).into_iter())
207             .collect();
208         ast::Block {
209             id: id,
210             stmts: resulting_stmts,
211             expr: expr.map(|x| cx.fold_expr(x)),
212             rules: rules,
213             span: span,
214         }
215     })
216 }
217
218 fn fold_expr<F>(cx: &mut Context<F>, expr: P<ast::Expr>) -> P<ast::Expr> where
219     F: FnMut(&[ast::Attribute]) -> bool
220 {
221     expr.map(|ast::Expr {id, span, node}| {
222         fold::noop_fold_expr(ast::Expr {
223             id: id,
224             node: match node {
225                 ast::ExprMatch(m, arms, source) => {
226                     ast::ExprMatch(m, arms.into_iter()
227                                         .filter(|a| (cx.in_cfg)(&a.attrs))
228                                         .collect(), source)
229                 }
230                 _ => node
231             },
232             span: span
233         }, cx)
234     })
235 }
236
237 fn item_in_cfg<F>(cx: &mut Context<F>, item: &ast::Item) -> bool where
238     F: FnMut(&[ast::Attribute]) -> bool
239 {
240     return (cx.in_cfg)(&item.attrs);
241 }
242
243 fn foreign_item_in_cfg<F>(cx: &mut Context<F>, item: &ast::ForeignItem) -> bool where
244     F: FnMut(&[ast::Attribute]) -> bool
245 {
246     return (cx.in_cfg)(&item.attrs);
247 }
248
249 // Determine if an item should be translated in the current crate
250 // configuration based on the item's attributes
251 fn in_cfg(diagnostic: &SpanHandler, cfg: &[P<ast::MetaItem>], attrs: &[ast::Attribute]) -> bool {
252     attrs.iter().all(|attr| {
253         let mis = match attr.node.value.node {
254             ast::MetaList(_, ref mis) if attr.check_name("cfg") => mis,
255             _ => return true
256         };
257
258         if mis.len() != 1 {
259             diagnostic.span_err(attr.span, "expected 1 cfg-pattern");
260             return true;
261         }
262
263         attr::cfg_matches(diagnostic, cfg, &*mis[0])
264     })
265 }
266
267 struct CfgAttrFolder<'a> {
268     diag: &'a SpanHandler,
269     config: ast::CrateConfig,
270 }
271
272 // Process `#[cfg_attr]`.
273 fn process_cfg_attr(diagnostic: &SpanHandler, krate: ast::Crate) -> ast::Crate {
274     let mut fld = CfgAttrFolder {
275         diag: diagnostic,
276         config: krate.config.clone(),
277     };
278     fld.fold_crate(krate)
279 }
280
281 impl<'a> fold::Folder for CfgAttrFolder<'a> {
282     fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
283         if !attr.check_name("cfg_attr") {
284             return fold::noop_fold_attribute(attr, self);
285         }
286
287         let attr_list = match attr.meta_item_list() {
288             Some(attr_list) => attr_list,
289             None => {
290                 self.diag.span_err(attr.span, "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
291                 return None;
292             }
293         };
294         let (cfg, mi) = match (attr_list.len(), attr_list.get(0), attr_list.get(1)) {
295             (2, Some(cfg), Some(mi)) => (cfg, mi),
296             _ => {
297                 self.diag.span_err(attr.span, "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
298                 return None;
299             }
300         };
301
302         if attr::cfg_matches(self.diag, &self.config[..], &cfg) {
303             Some(respan(mi.span, ast::Attribute_ {
304                 id: attr::mk_attr_id(),
305                 style: attr.node.style,
306                 value: mi.clone(),
307                 is_sugared_doc: false,
308             }))
309         } else {
310             None
311         }
312     }
313
314     // Need the ability to run pre-expansion.
315     fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
316         fold::noop_fold_mac(mac, self)
317     }
318 }