]> git.proxmox.com Git - rustc.git/blob - src/libsyntax/config.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.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 errors::Handler;
13 use feature_gate::GatedCfgAttr;
14 use fold::Folder;
15 use {ast, fold, attr};
16 use visit;
17 use codemap::{Spanned, respan};
18 use ptr::P;
19
20 use util::small_vector::SmallVector;
21
22 /// A folder that strips out items that do not belong in the current
23 /// configuration.
24 struct Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool {
25 in_cfg: F,
26 diagnostic: &'a Handler,
27 }
28
29 // Support conditional compilation by transforming the AST, stripping out
30 // any items that do not belong in the current configuration
31 pub fn strip_unconfigured_items(diagnostic: &Handler, krate: ast::Crate,
32 feature_gated_cfgs: &mut Vec<GatedCfgAttr>)
33 -> ast::Crate
34 {
35 // Need to do this check here because cfg runs before feature_gates
36 check_for_gated_stmt_expr_attributes(&krate, feature_gated_cfgs);
37
38 let krate = process_cfg_attr(diagnostic, krate, feature_gated_cfgs);
39 let config = krate.config.clone();
40 strip_items(diagnostic,
41 krate,
42 |attrs| {
43 let mut diag = CfgDiagReal {
44 diag: diagnostic,
45 feature_gated_cfgs: feature_gated_cfgs,
46 };
47 in_cfg(&config, attrs, &mut diag)
48 })
49 }
50
51 impl<'a, F> fold::Folder for Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool {
52 fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
53 fold_foreign_mod(self, foreign_mod)
54 }
55 fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
56 fold_item_kind(self, item)
57 }
58 fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
59 // If an expr is valid to cfg away it will have been removed by the
60 // outer stmt or expression folder before descending in here.
61 // Anything else is always required, and thus has to error out
62 // in case of a cfg attr.
63 //
64 // NB: This is intentionally not part of the fold_expr() function
65 // in order for fold_opt_expr() to be able to avoid this check
66 if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) {
67 self.diagnostic.span_err(attr.span,
68 "removing an expression is not supported in this position");
69 }
70 fold_expr(self, expr)
71 }
72 fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
73 fold_opt_expr(self, expr)
74 }
75 fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
76 fold_stmt(self, stmt)
77 }
78 fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
79 fold::noop_fold_mac(mac, self)
80 }
81 fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
82 fold_item(self, item)
83 }
84 }
85
86 pub fn strip_items<'a, F>(diagnostic: &'a Handler,
87 krate: ast::Crate, in_cfg: F) -> ast::Crate where
88 F: FnMut(&[ast::Attribute]) -> bool,
89 {
90 let mut ctxt = Context {
91 in_cfg: in_cfg,
92 diagnostic: diagnostic,
93 };
94 ctxt.fold_crate(krate)
95 }
96
97 fn filter_foreign_item<F>(cx: &mut Context<F>,
98 item: ast::ForeignItem)
99 -> Option<ast::ForeignItem> where
100 F: FnMut(&[ast::Attribute]) -> bool
101 {
102 if foreign_item_in_cfg(cx, &item) {
103 Some(item)
104 } else {
105 None
106 }
107 }
108
109 fn fold_foreign_mod<F>(cx: &mut Context<F>,
110 ast::ForeignMod {abi, items}: ast::ForeignMod)
111 -> ast::ForeignMod where
112 F: FnMut(&[ast::Attribute]) -> bool
113 {
114 ast::ForeignMod {
115 abi: abi,
116 items: items.into_iter()
117 .filter_map(|a| filter_foreign_item(cx, a))
118 .collect()
119 }
120 }
121
122 fn fold_item<F>(cx: &mut Context<F>, item: P<ast::Item>) -> SmallVector<P<ast::Item>> where
123 F: FnMut(&[ast::Attribute]) -> bool
124 {
125 if item_in_cfg(cx, &item) {
126 SmallVector::one(item.map(|i| cx.fold_item_simple(i)))
127 } else {
128 SmallVector::zero()
129 }
130 }
131
132 fn fold_item_kind<F>(cx: &mut Context<F>, item: ast::ItemKind) -> ast::ItemKind where
133 F: FnMut(&[ast::Attribute]) -> bool
134 {
135 let item = match item {
136 ast::ItemKind::Impl(u, o, a, b, c, impl_items) => {
137 let impl_items = impl_items.into_iter()
138 .filter(|ii| (cx.in_cfg)(&ii.attrs))
139 .collect();
140 ast::ItemKind::Impl(u, o, a, b, c, impl_items)
141 }
142 ast::ItemKind::Trait(u, a, b, methods) => {
143 let methods = methods.into_iter()
144 .filter(|ti| (cx.in_cfg)(&ti.attrs))
145 .collect();
146 ast::ItemKind::Trait(u, a, b, methods)
147 }
148 ast::ItemKind::Struct(def, generics) => {
149 ast::ItemKind::Struct(fold_struct(cx, def), generics)
150 }
151 ast::ItemKind::Enum(def, generics) => {
152 let variants = def.variants.into_iter().filter_map(|v| {
153 if !(cx.in_cfg)(&v.node.attrs) {
154 None
155 } else {
156 Some(Spanned {
157 node: ast::Variant_ {
158 name: v.node.name,
159 attrs: v.node.attrs,
160 data: fold_struct(cx, v.node.data),
161 disr_expr: v.node.disr_expr,
162 },
163 span: v.span
164 })
165 }
166 });
167 ast::ItemKind::Enum(ast::EnumDef {
168 variants: variants.collect(),
169 }, generics)
170 }
171 item => item,
172 };
173
174 fold::noop_fold_item_kind(item, cx)
175 }
176
177 fn fold_struct<F>(cx: &mut Context<F>, vdata: ast::VariantData) -> ast::VariantData where
178 F: FnMut(&[ast::Attribute]) -> bool
179 {
180 match vdata {
181 ast::VariantData::Struct(fields, id) => {
182 ast::VariantData::Struct(fields.into_iter().filter(|m| {
183 (cx.in_cfg)(&m.attrs)
184 }).collect(), id)
185 }
186 ast::VariantData::Tuple(fields, id) => {
187 ast::VariantData::Tuple(fields.into_iter().filter(|m| {
188 (cx.in_cfg)(&m.attrs)
189 }).collect(), id)
190 }
191 ast::VariantData::Unit(id) => ast::VariantData::Unit(id)
192 }
193 }
194
195 fn fold_opt_expr<F>(cx: &mut Context<F>, expr: P<ast::Expr>) -> Option<P<ast::Expr>>
196 where F: FnMut(&[ast::Attribute]) -> bool
197 {
198 if expr_in_cfg(cx, &expr) {
199 Some(fold_expr(cx, expr))
200 } else {
201 None
202 }
203 }
204
205 fn fold_expr<F>(cx: &mut Context<F>, expr: P<ast::Expr>) -> P<ast::Expr> where
206 F: FnMut(&[ast::Attribute]) -> bool
207 {
208 expr.map(|ast::Expr {id, span, node, attrs}| {
209 fold::noop_fold_expr(ast::Expr {
210 id: id,
211 node: match node {
212 ast::ExprKind::Match(m, arms) => {
213 ast::ExprKind::Match(m, arms.into_iter()
214 .filter(|a| (cx.in_cfg)(&a.attrs))
215 .collect())
216 }
217 _ => node
218 },
219 span: span,
220 attrs: attrs,
221 }, cx)
222 })
223 }
224
225 fn fold_stmt<F>(cx: &mut Context<F>, stmt: ast::Stmt) -> SmallVector<ast::Stmt>
226 where F: FnMut(&[ast::Attribute]) -> bool
227 {
228 if stmt_in_cfg(cx, &stmt) {
229 fold::noop_fold_stmt(stmt, cx)
230 } else {
231 SmallVector::zero()
232 }
233 }
234
235 fn stmt_in_cfg<F>(cx: &mut Context<F>, stmt: &ast::Stmt) -> bool where
236 F: FnMut(&[ast::Attribute]) -> bool
237 {
238 (cx.in_cfg)(stmt.node.attrs())
239 }
240
241 fn expr_in_cfg<F>(cx: &mut Context<F>, expr: &ast::Expr) -> bool where
242 F: FnMut(&[ast::Attribute]) -> bool
243 {
244 (cx.in_cfg)(expr.attrs())
245 }
246
247 fn item_in_cfg<F>(cx: &mut Context<F>, item: &ast::Item) -> bool where
248 F: FnMut(&[ast::Attribute]) -> bool
249 {
250 return (cx.in_cfg)(&item.attrs);
251 }
252
253 fn foreign_item_in_cfg<F>(cx: &mut Context<F>, item: &ast::ForeignItem) -> bool where
254 F: FnMut(&[ast::Attribute]) -> bool
255 {
256 return (cx.in_cfg)(&item.attrs);
257 }
258
259 fn is_cfg(attr: &ast::Attribute) -> bool {
260 attr.check_name("cfg")
261 }
262
263 // Determine if an item should be translated in the current crate
264 // configuration based on the item's attributes
265 fn in_cfg<T: CfgDiag>(cfg: &[P<ast::MetaItem>],
266 attrs: &[ast::Attribute],
267 diag: &mut T) -> bool {
268 attrs.iter().all(|attr| {
269 let mis = match attr.node.value.node {
270 ast::MetaItemKind::List(_, ref mis) if is_cfg(&attr) => mis,
271 _ => return true
272 };
273
274 if mis.len() != 1 {
275 diag.emit_error(|diagnostic| {
276 diagnostic.span_err(attr.span, "expected 1 cfg-pattern");
277 });
278 return true;
279 }
280
281 attr::cfg_matches(cfg, &mis[0], diag)
282 })
283 }
284
285 struct CfgAttrFolder<'a, T> {
286 diag: T,
287 config: &'a ast::CrateConfig,
288 }
289
290 // Process `#[cfg_attr]`.
291 fn process_cfg_attr(diagnostic: &Handler, krate: ast::Crate,
292 feature_gated_cfgs: &mut Vec<GatedCfgAttr>) -> ast::Crate {
293 let mut fld = CfgAttrFolder {
294 diag: CfgDiagReal {
295 diag: diagnostic,
296 feature_gated_cfgs: feature_gated_cfgs,
297 },
298 config: &krate.config.clone(),
299 };
300 fld.fold_crate(krate)
301 }
302
303 impl<'a, T: CfgDiag> fold::Folder for CfgAttrFolder<'a, T> {
304 fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
305 if !attr.check_name("cfg_attr") {
306 return fold::noop_fold_attribute(attr, self);
307 }
308
309 let attr_list = match attr.meta_item_list() {
310 Some(attr_list) => attr_list,
311 None => {
312 self.diag.emit_error(|diag| {
313 diag.span_err(attr.span,
314 "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
315 });
316 return None;
317 }
318 };
319 let (cfg, mi) = match (attr_list.len(), attr_list.get(0), attr_list.get(1)) {
320 (2, Some(cfg), Some(mi)) => (cfg, mi),
321 _ => {
322 self.diag.emit_error(|diag| {
323 diag.span_err(attr.span,
324 "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
325 });
326 return None;
327 }
328 };
329
330 if attr::cfg_matches(&self.config[..], &cfg, &mut self.diag) {
331 Some(respan(mi.span, ast::Attribute_ {
332 id: attr::mk_attr_id(),
333 style: attr.node.style,
334 value: mi.clone(),
335 is_sugared_doc: false,
336 }))
337 } else {
338 None
339 }
340 }
341
342 // Need the ability to run pre-expansion.
343 fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
344 fold::noop_fold_mac(mac, self)
345 }
346 }
347
348 fn check_for_gated_stmt_expr_attributes(krate: &ast::Crate,
349 discovered: &mut Vec<GatedCfgAttr>) {
350 let mut v = StmtExprAttrFeatureVisitor {
351 config: &krate.config,
352 discovered: discovered,
353 };
354 visit::walk_crate(&mut v, krate);
355 }
356
357 /// To cover this feature, we need to discover all attributes
358 /// so we need to run before cfg.
359 struct StmtExprAttrFeatureVisitor<'a, 'b> {
360 config: &'a ast::CrateConfig,
361 discovered: &'b mut Vec<GatedCfgAttr>,
362 }
363
364 // Runs the cfg_attr and cfg folders locally in "silent" mode
365 // to discover attribute use on stmts or expressions ahead of time
366 impl<'v, 'a, 'b> visit::Visitor<'v> for StmtExprAttrFeatureVisitor<'a, 'b> {
367 fn visit_stmt(&mut self, s: &'v ast::Stmt) {
368 // check if there even are any attributes on this node
369 let stmt_attrs = s.node.attrs();
370 if stmt_attrs.len() > 0 {
371 // attributes on items are fine
372 if let ast::StmtKind::Decl(ref decl, _) = s.node {
373 if let ast::DeclKind::Item(_) = decl.node {
374 visit::walk_stmt(self, s);
375 return;
376 }
377 }
378
379 // flag the offending attributes
380 for attr in stmt_attrs {
381 self.discovered.push(GatedCfgAttr::GatedAttr(attr.span));
382 }
383
384 // if the node does not end up being cfg-d away, walk down
385 if node_survives_cfg(stmt_attrs, self.config) {
386 visit::walk_stmt(self, s);
387 }
388 } else {
389 visit::walk_stmt(self, s);
390 }
391 }
392
393 fn visit_expr(&mut self, ex: &'v ast::Expr) {
394 // check if there even are any attributes on this node
395 let expr_attrs = ex.attrs();
396 if expr_attrs.len() > 0 {
397
398 // flag the offending attributes
399 for attr in expr_attrs {
400 self.discovered.push(GatedCfgAttr::GatedAttr(attr.span));
401 }
402
403 // if the node does not end up being cfg-d away, walk down
404 if node_survives_cfg(expr_attrs, self.config) {
405 visit::walk_expr(self, ex);
406 }
407 } else {
408 visit::walk_expr(self, ex);
409 }
410 }
411
412 fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
413 if node_survives_cfg(&i.attrs, self.config) {
414 visit::walk_foreign_item(self, i);
415 }
416 }
417
418 fn visit_item(&mut self, i: &'v ast::Item) {
419 if node_survives_cfg(&i.attrs, self.config) {
420 visit::walk_item(self, i);
421 }
422 }
423
424 fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
425 if node_survives_cfg(&ii.attrs, self.config) {
426 visit::walk_impl_item(self, ii);
427 }
428 }
429
430 fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) {
431 if node_survives_cfg(&ti.attrs, self.config) {
432 visit::walk_trait_item(self, ti);
433 }
434 }
435
436 fn visit_struct_field(&mut self, s: &'v ast::StructField) {
437 if node_survives_cfg(&s.attrs, self.config) {
438 visit::walk_struct_field(self, s);
439 }
440 }
441
442 fn visit_variant(&mut self, v: &'v ast::Variant,
443 g: &'v ast::Generics, item_id: ast::NodeId) {
444 if node_survives_cfg(&v.node.attrs, self.config) {
445 visit::walk_variant(self, v, g, item_id);
446 }
447 }
448
449 fn visit_arm(&mut self, a: &'v ast::Arm) {
450 if node_survives_cfg(&a.attrs, self.config) {
451 visit::walk_arm(self, a);
452 }
453 }
454
455 // This visitor runs pre expansion, so we need to prevent
456 // the default panic here
457 fn visit_mac(&mut self, mac: &'v ast::Mac) {
458 visit::walk_mac(self, mac)
459 }
460 }
461
462 pub trait CfgDiag {
463 fn emit_error<F>(&mut self, f: F) where F: FnMut(&Handler);
464 fn flag_gated<F>(&mut self, f: F) where F: FnMut(&mut Vec<GatedCfgAttr>);
465 }
466
467 pub struct CfgDiagReal<'a, 'b> {
468 pub diag: &'a Handler,
469 pub feature_gated_cfgs: &'b mut Vec<GatedCfgAttr>,
470 }
471
472 impl<'a, 'b> CfgDiag for CfgDiagReal<'a, 'b> {
473 fn emit_error<F>(&mut self, mut f: F) where F: FnMut(&Handler) {
474 f(self.diag)
475 }
476 fn flag_gated<F>(&mut self, mut f: F) where F: FnMut(&mut Vec<GatedCfgAttr>) {
477 f(self.feature_gated_cfgs)
478 }
479 }
480
481 struct CfgDiagSilent {
482 error: bool,
483 }
484
485 impl CfgDiag for CfgDiagSilent {
486 fn emit_error<F>(&mut self, _: F) where F: FnMut(&Handler) {
487 self.error = true;
488 }
489 fn flag_gated<F>(&mut self, _: F) where F: FnMut(&mut Vec<GatedCfgAttr>) {}
490 }
491
492 fn node_survives_cfg(attrs: &[ast::Attribute],
493 config: &ast::CrateConfig) -> bool {
494 let mut survives_cfg = true;
495
496 for attr in attrs {
497 let mut fld = CfgAttrFolder {
498 diag: CfgDiagSilent { error: false },
499 config: config,
500 };
501 let attr = fld.fold_attribute(attr.clone());
502
503 // In case of error we can just return true,
504 // since the actual cfg folders will end compilation anyway.
505
506 if fld.diag.error { return true; }
507
508 survives_cfg &= attr.map(|attr| {
509 let mut diag = CfgDiagSilent { error: false };
510 let r = in_cfg(config, &[attr], &mut diag);
511 if diag.error { return true; }
512 r
513 }).unwrap_or(true)
514 }
515
516 survives_cfg
517 }