]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/config.rs
Unify structures and enum variants in AST
[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, def,
144                                                               disr_expr}, span}| {
145                         Spanned {
146                             node: ast::Variant_ {
147                                 id: id,
148                                 name: name,
149                                 attrs: attrs,
150                                 def: fold_struct(cx, def),
151                                 disr_expr: disr_expr,
152                             },
153                             span: span
154                         }
155                     }))
156                 }
157             });
158             ast::ItemEnum(ast::EnumDef {
159                 variants: variants.collect(),
160             }, generics)
161         }
162         item => item,
163     };
164
165     fold::noop_fold_item_underscore(item, cx)
166 }
167
168 fn fold_struct<F>(cx: &mut Context<F>, def: P<ast::StructDef>) -> P<ast::StructDef> where
169     F: FnMut(&[ast::Attribute]) -> bool
170 {
171     def.map(|ast::StructDef { fields, ctor_id }| {
172         ast::StructDef {
173             fields: fields.into_iter().filter(|m| {
174                 (cx.in_cfg)(&m.node.attrs)
175             }).collect(),
176             ctor_id: ctor_id,
177         }
178     })
179 }
180
181 fn retain_stmt<F>(cx: &mut Context<F>, stmt: &ast::Stmt) -> bool where
182     F: FnMut(&[ast::Attribute]) -> bool
183 {
184     match stmt.node {
185         ast::StmtDecl(ref decl, _) => {
186             match decl.node {
187                 ast::DeclItem(ref item) => {
188                     item_in_cfg(cx, &**item)
189                 }
190                 _ => true
191             }
192         }
193         _ => true
194     }
195 }
196
197 fn fold_block<F>(cx: &mut Context<F>, b: P<ast::Block>) -> P<ast::Block> where
198     F: FnMut(&[ast::Attribute]) -> bool
199 {
200     b.map(|ast::Block {id, stmts, expr, rules, span}| {
201         let resulting_stmts: Vec<P<ast::Stmt>> =
202             stmts.into_iter().filter(|a| retain_stmt(cx, &**a)).collect();
203         let resulting_stmts = resulting_stmts.into_iter()
204             .flat_map(|stmt| cx.fold_stmt(stmt).into_iter())
205             .collect();
206         ast::Block {
207             id: id,
208             stmts: resulting_stmts,
209             expr: expr.map(|x| cx.fold_expr(x)),
210             rules: rules,
211             span: span,
212         }
213     })
214 }
215
216 fn fold_expr<F>(cx: &mut Context<F>, expr: P<ast::Expr>) -> P<ast::Expr> where
217     F: FnMut(&[ast::Attribute]) -> bool
218 {
219     expr.map(|ast::Expr {id, span, node}| {
220         fold::noop_fold_expr(ast::Expr {
221             id: id,
222             node: match node {
223                 ast::ExprMatch(m, arms) => {
224                     ast::ExprMatch(m, arms.into_iter()
225                                         .filter(|a| (cx.in_cfg)(&a.attrs))
226                                         .collect())
227                 }
228                 _ => node
229             },
230             span: span
231         }, cx)
232     })
233 }
234
235 fn item_in_cfg<F>(cx: &mut Context<F>, item: &ast::Item) -> bool where
236     F: FnMut(&[ast::Attribute]) -> bool
237 {
238     return (cx.in_cfg)(&item.attrs);
239 }
240
241 fn foreign_item_in_cfg<F>(cx: &mut Context<F>, item: &ast::ForeignItem) -> bool where
242     F: FnMut(&[ast::Attribute]) -> bool
243 {
244     return (cx.in_cfg)(&item.attrs);
245 }
246
247 // Determine if an item should be translated in the current crate
248 // configuration based on the item's attributes
249 fn in_cfg(diagnostic: &SpanHandler, cfg: &[P<ast::MetaItem>], attrs: &[ast::Attribute],
250           feature_gated_cfgs: &mut Vec<GatedCfg>) -> bool {
251     attrs.iter().all(|attr| {
252         let mis = match attr.node.value.node {
253             ast::MetaList(_, ref mis) if attr.check_name("cfg") => mis,
254             _ => return true
255         };
256
257         if mis.len() != 1 {
258             diagnostic.span_err(attr.span, "expected 1 cfg-pattern");
259             return true;
260         }
261
262         attr::cfg_matches(diagnostic, cfg, &*mis[0],
263                           feature_gated_cfgs)
264     })
265 }
266
267 struct CfgAttrFolder<'a, 'b> {
268     diag: &'a SpanHandler,
269     config: ast::CrateConfig,
270     feature_gated_cfgs: &'b mut Vec<GatedCfg>
271 }
272
273 // Process `#[cfg_attr]`.
274 fn process_cfg_attr(diagnostic: &SpanHandler, krate: ast::Crate,
275                     feature_gated_cfgs: &mut Vec<GatedCfg>) -> ast::Crate {
276     let mut fld = CfgAttrFolder {
277         diag: diagnostic,
278         config: krate.config.clone(),
279         feature_gated_cfgs: feature_gated_cfgs,
280     };
281     fld.fold_crate(krate)
282 }
283
284 impl<'a,'b> fold::Folder for CfgAttrFolder<'a,'b> {
285     fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
286         if !attr.check_name("cfg_attr") {
287             return fold::noop_fold_attribute(attr, self);
288         }
289
290         let attr_list = match attr.meta_item_list() {
291             Some(attr_list) => attr_list,
292             None => {
293                 self.diag.span_err(attr.span, "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
294                 return None;
295             }
296         };
297         let (cfg, mi) = match (attr_list.len(), attr_list.get(0), attr_list.get(1)) {
298             (2, Some(cfg), Some(mi)) => (cfg, mi),
299             _ => {
300                 self.diag.span_err(attr.span, "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
301                 return None;
302             }
303         };
304
305         if attr::cfg_matches(self.diag, &self.config[..], &cfg,
306                              self.feature_gated_cfgs) {
307             Some(respan(mi.span, ast::Attribute_ {
308                 id: attr::mk_attr_id(),
309                 style: attr.node.style,
310                 value: mi.clone(),
311                 is_sugared_doc: false,
312             }))
313         } else {
314             None
315         }
316     }
317
318     // Need the ability to run pre-expansion.
319     fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
320         fold::noop_fold_mac(mac, self)
321     }
322 }