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