]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
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}; | |
85aaf69f | 15 | use codemap::{Spanned, respan}; |
1a4d82fc JJ |
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 { | |
85aaf69f | 29 | let krate = process_cfg_attr(diagnostic, krate); |
1a4d82fc | 30 | let config = krate.config.clone(); |
85aaf69f | 31 | strip_items(krate, |attrs| in_cfg(diagnostic, &config, attrs)) |
1a4d82fc JJ |
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 | ||
1a4d82fc | 67 | fn fold_mod<F>(cx: &mut Context<F>, |
85aaf69f SL |
68 | ast::Mod {inner, items}: ast::Mod) |
69 | -> ast::Mod where | |
1a4d82fc JJ |
70 | F: FnMut(&[ast::Attribute]) -> bool |
71 | { | |
72 | ast::Mod { | |
73 | inner: inner, | |
1a4d82fc JJ |
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>, | |
85aaf69f | 93 | ast::ForeignMod {abi, items}: ast::ForeignMod) |
1a4d82fc JJ |
94 | -> ast::ForeignMod where |
95 | F: FnMut(&[ast::Attribute]) -> bool | |
96 | { | |
97 | ast::ForeignMod { | |
98 | abi: abi, | |
1a4d82fc JJ |
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() | |
c34b1796 | 121 | .filter(|ii| (cx.in_cfg)(&ii.attrs)) |
1a4d82fc JJ |
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() | |
c34b1796 | 127 | .filter(|ti| (cx.in_cfg)(&ti.attrs)) |
1a4d82fc JJ |
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| { | |
85aaf69f | 136 | if !(cx.in_cfg)(&v.node.attrs) { |
1a4d82fc JJ |
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| { | |
85aaf69f | 176 | (cx.in_cfg)(&m.node.attrs) |
1a4d82fc JJ |
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 | { | |
85aaf69f | 202 | b.map(|ast::Block {id, stmts, expr, rules, span}| { |
1a4d82fc JJ |
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(); | |
1a4d82fc JJ |
208 | ast::Block { |
209 | id: id, | |
1a4d82fc JJ |
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() | |
85aaf69f | 227 | .filter(|a| (cx.in_cfg)(&a.attrs)) |
1a4d82fc JJ |
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 | { | |
85aaf69f | 240 | return (cx.in_cfg)(&item.attrs); |
1a4d82fc JJ |
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 | { | |
85aaf69f | 246 | return (cx.in_cfg)(&item.attrs); |
1a4d82fc JJ |
247 | } |
248 | ||
1a4d82fc JJ |
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 | } | |
85aaf69f SL |
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 (cfg, mi) = match attr.meta_item_list() { | |
288 | Some([ref cfg, ref mi]) => (cfg, mi), | |
289 | _ => { | |
290 | self.diag.span_err(attr.span, "expected `#[cfg_attr(<cfg pattern>, <attr>)]`"); | |
291 | return None; | |
292 | } | |
293 | }; | |
294 | ||
295 | if attr::cfg_matches(self.diag, &self.config[..], &cfg) { | |
296 | Some(respan(mi.span, ast::Attribute_ { | |
297 | id: attr::mk_attr_id(), | |
298 | style: attr.node.style, | |
299 | value: mi.clone(), | |
300 | is_sugared_doc: false, | |
301 | })) | |
302 | } else { | |
303 | None | |
304 | } | |
305 | } | |
306 | ||
307 | // Need the ability to run pre-expansion. | |
308 | fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { | |
309 | fold::noop_fold_mac(mac, self) | |
310 | } | |
311 | } |